diff --git a/README.md b/README.md index e03a0e7..0d22192 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,14 @@ $ pip3 install numpy matplotlib ``` ## Testing environment (non-virtio) +To test the network environment effectively, we utilize **Linux network namespaces**. These namespaces isolate network environments from the host system, providing distinct instances of network stacks with independent routes, firewall rules, and network devices. + +Without network namespaces, virtual interfaces created within the same namespace use the loopback device for packet transmission between them, as the kernel recognizes them as residing on the same host. + +In our testing setup, all interfaces created by `vwifi` are placed within an isolated network namespace. This approach ensures that each virtual interface operates independently, facilitating comprehensive testing of networking functionalities without interference from the host's network configuration. + +Below, we will conduct two separate tests: Infrastructure BSS and Independent BSS. +### Infrastructure BSS

logo image

The testing environment consists of **one AP and two STAs**. @@ -48,15 +56,12 @@ The AP then performs the following actions based on the packet type: 2. Broadcast: The AP forwards the packet to all other STAs in the network, except for the source STA, and then passes it to the protocol stack. 3. Multicast: The AP treats multicast packets the same way as broadcast packets. -To test the network environment, we can utilize the **Linux network namespace**. -Linux network namespace allows us to isolate a network environment from the host system, providing its own routes, firewall rules, and network devices. -Essentially, it creates a separate instance of the network stack. +### Independent BSS +

logo image

-Without network namespace, when virtual interfaces are created that share the same network namespace and start transmitting/receiving packets between them, -the kernel will use the loopback device for packet transmission/reception. This behavior occurs because the kernel identifies that the sender and receiver are on the same host. - -In conclusion, all the interfaces created by `vwifi` in the testing environment will be added to an isolated network namespace. +The testing environment consists of **two IBSS devices**. +The testing environment operates in IEEE 802.11 independent BSS. IBSS devices can communicate with any device in the same IBSS network **without the need to establish a connection beforehand**. However, devices in different IBSS networks cannot communicate with each other. ## Build and Run (non-virtio) To build the kernel module, execute the following command: @@ -72,7 +77,7 @@ $ sudo modprobe cfg80211 Insert the `vwifi` driver. This will create three interfaces (the "station" parameter can be modified according to preference): ```shell -$ sudo insmod vwifi.ko station=3 +$ sudo insmod vwifi.ko station=5 ``` Please note that interfaces can only be created in station mode during the initialization phase. @@ -85,7 +90,7 @@ To check the network interfaces, run the following command: $ ip link ``` -There should be entries starting with `vw0`, `vw1`, and `vw2`, which correspond to the interfaces created by `vwifi`. +There should be entries starting with `vw0`, `vw1`, `vw2`, `vw3`, and `vw4`, which correspond to the interfaces created by `vwifi`. To view the available wireless interfaces, execute the following command: ```shell @@ -94,24 +99,41 @@ $ sudo iw dev You should see something similar to the following output: ``` -phy#2 +phy#5 + Interface vw4 + ifindex 7 + wdev 0x500000001 + addr 00:76:77:34:00:00 + type managed + txpower 0.00 dBm +phy#4 + Interface vw3 + ifindex 6 + wdev 0x400000001 + addr 00:76:77:33:00:00 + type managed + txpower 0.00 dBm +phy#3 Interface vw2 ifindex 5 - wdev 0x200000001 - addr 00:6f:77:6c:32:00 + wdev 0x300000001 + addr 00:76:77:32:00:00 type managed -phy#1 + txpower 0.00 dBm +phy#2 Interface vw1 ifindex 4 - wdev 0x100000001 - addr 00:6f:77:6c:31:00 + wdev 0x200000001 + addr 00:76:77:31:00:00 type managed -phy#0 + txpower 0.00 dBm +phy#1 Interface vw0 ifindex 3 - wdev 0x1 - addr 00:6f:77:6c:30:00 + wdev 0x100000001 + addr 00:76:77:30:00:00 type managed + txpower 0.00 dBm ``` As observed, each interface has its own phy (`struct wiphy`), allowing them to be placed into separate network namespaces. @@ -125,6 +147,10 @@ $ sudo iw list Reference output: ``` +Wiphy vw_phy4 +(... omit) +Wiphy vw_phy3 +(... omit) Wiphy vw_phy2 (... omit) Wiphy vw_phy1 @@ -366,6 +392,175 @@ PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 4 packets transmitted, 4 received, 0% packet loss, time 3058ms rtt min/avg/max/mdev = 0.054/0.141/0.342/0.117 ms ``` +### IBSS mode + +#### Creating Network Namespaces +Create three network namespaces using the following commands: +```shell +$ sudo ip netns add ns3 +$ sudo ip netns add ns4 +``` +Find the `wiphy` name for the two interfaces. +The index number for the `wiphy` name postfix might be different each time. +Please use the following command for the ease of memorizing different index number everytime. +```shell +$ vw3_phy=$(sudo iw dev vw3 info | grep wiphy | awk '{print $2}') +$ vw3_phy=$(sudo iw list | grep "wiphy index: $vw3_phy" -B 1 | grep Wiphy | awk '{print $2}') +$ vw4_phy=$(sudo iw dev vw4 info | grep wiphy | awk '{print $2}') +$ vw4_phy=$(sudo iw list | grep "wiphy index: $vw4_phy" -B 1 | grep Wiphy | awk '{print $2}') +``` +Check whether the name of each `wiphy` is the same as the name listing under the command `sudo iw list` +```shell +$ echo $vw3_phy +vw_phy3 +$ echo $vw4_phy +vw_phy4 +``` +Assign the two interfaces to separate network namespaces. +Please note that the `wiphy` is placed within the network namespace, and the interface associated with that wiphy will be contained within it. +```shell +$ sudo iw phy vw_phy3 set netns name ns3 +$ sudo iw phy vw_phy4 set netns name ns4 +``` +#### Assigning IP Addresses to Each Interface + +Now, assign an IP address to both interfaces using the following commands: +```shell +$ sudo ip netns exec ns3 ip addr add 10.0.0.4/24 dev vw3 +$ sudo ip netns exec ns4 ip addr add 10.0.0.5/24 dev vw4 +``` +There are two methods to configure an IBSS network: manual configuration or using WPA. +#### Option1 : Manual configuration +##### Switch to IBSS mode +Switch device to IBSS mode using the following command : + +***iw dev [interface] set type ibss*** + +The following commands switch `vw3` and `vw4` to IBSS mode. +```shell +$ sudo ip netns exec ns3 iw dev vw3 set type ibss +$ sudo ip netns exec ns4 iw dev vw4 set type ibss +``` +Check the information of `vw3`. +```shell +$ sudo ip netns exec ns3 iw dev vw3 info +``` +You should see output similar to the following: +``` +Interface vw3 + ifindex 6 + wdev 0x400000001 + addr 00:76:77:33:00:00 + type IBSS + wiphy 4 + txpower 0.00 dBm +``` +##### Join IBSS network +```shell +$ sudo ip netns exec ns3 ip link set vw3 up +$ sudo ip netns exec ns4 ip link set vw4 up +``` +Users can join a specific IBSS cell and configure additional settings using the command : + +***iw dev [interface] ibss join [SSID] [freq in MHz] [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [fixed-freq] [fixed-bssid] [beacon-interval ] [basic-rates ] [mcast-rate ] [key d:0:abcde]*** + +If the IBSS cell does not already exist, it will be created. + +The following command makes `vw3` and `vw4` join the same IBSS cell with the SSID `ibss1` and specifies the frequency as 2412 MHz: +```shell +$ sudo ip netns exec ns3 iw dev vw3 ibss join ibss1 2412 NOHT fixed-freq 00:76:77:33:00:00 beacon-interval 200 +$ sudo ip netns exec ns4 iw dev vw4 ibss join ibss1 2412 NOHT fixed-freq 00:76:77:33:00:00 beacon-interval 200 +``` +Check the information of `vw3`. +```shell +$ sudo ip netns exec ns3 iw dev vw3 info +``` +You should see output similar to the following: +``` +Interface vw3 + ifindex 6 + wdev 0x400000001 + addr 00:76:77:33:00:00 + ssid ibss1 + type IBSS + wiphy 4 + txpower 0.00 dBm +``` +#### Option2 : Using WPA +```shell +$ sudo ip netns exec ns3 ip link set vw3 up +$ sudo ip netns exec ns4 ip link set vw4 up +``` +Prepare the following script `wpa_supplicant_ibss.conf` (you can modify the script based on your needs): +```shell +network={ + ssid="ibss1" + mode=1 + frequency=2412 + key_mgmt=WPA-PSK + proto=RSN + pairwise=CCMP + group=CCMP + psk="12345678" +} +``` +Using the command **wpa_supplicant**, configure `vw3` and `vw4` to join `ibss1`. +```shell +$ sudo ip netns exec ns3 wpa_supplicant -i vw3 -B -c scripts/wpa_supplicant_ibss.conf +$ sudo ip netns exec ns4 wpa_supplicant -i vw4 -B -c scripts/wpa_supplicant_ibss.conf +``` +Check the information of `vw3`. +```shell +$ sudo ip netns exec ns3 iw dev vw3 info +``` +You should see output similar to the following: +``` +Interface vw3 + ifindex 6 + wdev 0x400000001 + addr 00:76:77:33:00:00 + ssid ibss1 + type IBSS + wiphy 4 + txpower 0.00 dBm +``` +#### Transmission/Receivement test +To perform a ping test between two IBSS devices (`vw3` and `vw4`) in the same ibss cell (`ibss1`), use the following command: +```shell +$ sudo ip netns exec ns3 ping -c 1 10.0.0.5 +``` +You should see output similar to the following: +``` +PING 10.0.0.5 (10.0.0.5) 56(84) bytes of data. +64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=0.093 ms + +--- 10.0.0.5 ping statistics --- +1 packets transmitted, 1 received, 0% packet loss, time 0ms +rtt min/avg/max/mdev = 0.093/0.093/0.093/0.000 ms +``` +#### Leave IBSS network +To leave the current IBSS cell, use ***iw dev [interface] ibss leave***. + +The following command makes `vw3` and `vw4` leave `ibss1`: +```shell +$ sudo ip netns exec ns3 iw dev vw3 ibss leave +$ sudo ip netns exec ns4 iw dev vw4 ibss leave +``` +Check the information of `vw3`. +```shell +$ sudo ip netns exec ns3 iw dev vw3 info +``` +You should see output similar to the following: +``` +Interface vw3 + ifindex 6 + wdev 0x400000001 + addr 00:76:77:33:00:00 + type IBSS + wiphy 4 + txpower 0.00 dBm +``` + ### vwifi-tool A userspace tool which supports more user-specific utilization for vwifi. Aiming to provide more flexibility and customization for users of vwifi. diff --git a/assets/ibss.drawio b/assets/ibss.drawio new file mode 100644 index 0000000..f82d8aa --- /dev/null +++ b/assets/ibss.drawio @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/ibss.png b/assets/ibss.png new file mode 100644 index 0000000..3e17e0b Binary files /dev/null and b/assets/ibss.png differ diff --git a/scripts/verify.sh b/scripts/verify.sh index 6113f07..baaa815 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -10,7 +10,7 @@ if [ $? -ne 0 ]; then final_ret=1 fi -insert_kmod vwifi.ko station=3 +insert_kmod vwifi.ko station=6 if [ $? -ne 0 ]; then final_ret=2 fi @@ -28,22 +28,34 @@ if [ $final_ret -eq 0 ]; then sudo iw dev vw0 set txpower auto sudo iw dev vw1 set txpower fixed 1200 sudo iw dev vw2 set txpower fixed 1300 + sudo iw dev vw3 set txpower auto + sudo iw dev vw4 set txpower auto + sudo iw dev vw5 set txpower auto # get phy number of each interface sudo iw dev > device.log vw0_phy=$(get_wiphy_name vw0) vw1_phy=$(get_wiphy_name vw1) vw2_phy=$(get_wiphy_name vw2) + vw3_phy=$(get_wiphy_name vw3) + vw4_phy=$(get_wiphy_name vw4) + vw5_phy=$(get_wiphy_name vw5) # create network namespaces for each phy (interface) sudo ip netns add ns0 sudo ip netns add ns1 sudo ip netns add ns2 + sudo ip netns add ns3 + sudo ip netns add ns4 + sudo ip netns add ns5 # add each phy (interface) to separate network namesapces sudo iw phy $vw0_phy set netns name ns0 sudo iw phy $vw1_phy set netns name ns1 sudo iw phy $vw2_phy set netns name ns2 + sudo iw phy $vw3_phy set netns name ns3 + sudo iw phy $vw4_phy set netns name ns4 + sudo iw phy $vw5_phy set netns name ns5 # running hostapd on vw0, so vw0 becomes AP sudo ip netns exec ns0 ip link set vw0 up @@ -56,10 +68,22 @@ if [ $final_ret -eq 0 ]; then sudo ip netns exec ns2 ip link set vw2 up sudo ip netns exec ns2 ip link set lo up + sudo ip netns exec ns3 ip link set vw3 up + sudo ip netns exec ns3 ip link set lo up + + sudo ip netns exec ns4 ip link set vw4 up + sudo ip netns exec ns4 ip link set lo up + + sudo ip netns exec ns5 ip link set vw5 up + sudo ip netns exec ns5 ip link set lo up + # assing IP address to each interface sudo ip netns exec ns0 ip addr add 10.0.0.1/24 dev vw0 sudo ip netns exec ns1 ip addr add 10.0.0.2/24 dev vw1 sudo ip netns exec ns2 ip addr add 10.0.0.3/24 dev vw2 + sudo ip netns exec ns3 ip addr add 10.0.0.4/24 dev vw3 + sudo ip netns exec ns4 ip addr add 10.0.0.5/24 dev vw4 + sudo ip netns exec ns5 ip addr add 10.0.0.6/24 dev vw5 # ping test: STA vw1 <--> STA vw2, should fail, because they # haven't connected to AP @@ -136,6 +160,64 @@ if [ $final_ret -eq 0 ]; then final_ret=7 fi + # vw3 becomes an IBSS and then joins the "ibss1" network. + echo + echo "==============" + echo "vw3 join ibss1" + echo "==============" + sudo ip netns exec ns3 wpa_supplicant -i vw3 -B -c scripts/wpa_supplicant_ibss.conf + + # vw4 becomes an IBSS and then joins the "ibss1" network. + echo + echo "==============" + echo "vw4 join ibss1" + echo "==============" + sudo ip netns exec ns4 wpa_supplicant -i vw4 -B -c scripts/wpa_supplicant_ibss.conf + + # vw5 becomes an IBSS and then joins the "ibss2" network (BSSID: 00:76:77:35:00:00). + echo + echo "==================================" + echo "vw5 join ibss2 (00:76:77:35:00:00)" + echo "==================================" + sudo ip netns exec ns5 iw dev vw5 set type ibss + sudo ip netns exec ns5 iw dev vw5 ibss join ibss2 2412 NOHT fixed-freq 00:76:77:35:00:00 beacon-interval 300 + + # ping test: IBSS vw3 <--> STA vw2, should fail + echo + echo "================================================================================" + echo "Ping Test: IBSS vw3 (10.0.0.4) (in ibss1) <--> STA vw2 (10.0.0.3)" + echo + echo "(should fail)" + echo "(be patient, it will take some time to route...)" + echo "================================================================================" + sudo ip netns exec ns3 ping -c 1 10.0.0.3 + + # ping test: IBSS vw3 <--> IBSS vw5, should fail + echo + echo "================================================================================" + echo "Ping Test: IBSS vw3 (10.0.0.4) (in ibss1) <--> IBSS vw5 (10.0.0.6) (in ibss2)" + echo + echo "(should fail)" + echo "(be patient, it will take some time to route...)" + echo "================================================================================" + sudo ip netns exec ns3 ping -c 1 10.0.0.6 + + # ping test: IBSS vw3 <--> IBSS vw4, should success + echo + echo "================================================================================" + echo "Ping Test: IBSS vw3 (10.0.0.4) (in ibss1) <--> IBSS vw4 (10.0.0.5) (in ibss1)" + echo + echo "(should success)" + echo "(be patient, it will take some time to route...)" + echo "================================================================================" + sudo ip netns exec ns3 ping -c 1 10.0.0.5 + + # sudo ip netns exec ns3 ping -c 1 10.0.0.5 + ping_rc=$? + if [ $ping_rc -ne 0 ]; then + final_ret=8 + fi + # verify TSF (in usec) sudo ip netns exec ns1 iw dev vw1 scan > scan_result.log tsf=$(cat scan_result.log | grep "TSF" | tail -n 1 | awk '{print $2}') @@ -145,7 +227,7 @@ if [ $final_ret -eq 0 ]; then # difference between tsf and uptime should less than 0.5 sec. if [ "${diff#-}" -gt 500000 ]; then - final_ret=8 + final_ret=9 fi # plot the distribution of RSSI of vw0 @@ -162,7 +244,7 @@ if [ $final_ret -eq 0 ]; then python3 $ROOT/scripts/plot_rssi.py plot_rc=$? if [ $plot_rc -ne 0 ]; then - final_ret=9 + final_ret=10 fi # TestAP performs station dump @@ -172,7 +254,7 @@ if [ $final_ret -eq 0 ]; then sudo ip netns exec "ns${num}" iw dev | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' > station_ssid.log DIFF=$(diff dump_ssid.log station_ssid.log) if [ "$DIFF" != "" ]; then - final_ret=10 + final_ret=11 break fi done @@ -184,6 +266,9 @@ if [ $final_ret -eq 0 ]; then sudo ip netns del ns0 sudo ip netns del ns1 sudo ip netns del ns2 + sudo ip netns del ns3 + sudo ip netns del ns4 + sudo ip netns del ns5 rm scan_result.log scan_bssid.log connected.log device.log rssi.txt station_dump_result.log dump_ssid.log station_ssid.log echo "==== Test PASSED ====" exit 0 diff --git a/scripts/wpa_supplicant_ibss.conf b/scripts/wpa_supplicant_ibss.conf new file mode 100644 index 0000000..e2fe299 --- /dev/null +++ b/scripts/wpa_supplicant_ibss.conf @@ -0,0 +1,10 @@ +network={ + ssid="ibss1" + mode=1 + frequency=2412 + key_mgmt=WPA-PSK + proto=RSN + pairwise=CCMP + group=CCMP + psk="12345678" +} \ No newline at end of file diff --git a/vwifi.c b/vwifi.c index 2e09827..318cdee 100644 --- a/vwifi.c +++ b/vwifi.c @@ -63,10 +63,11 @@ struct vwifi_context { * the whole lifetime. */ struct mutex lock; - enum vwifi_state state; /**< indicate the program state */ - struct list_head vif_list; /**< maintaining all interfaces */ - struct list_head ap_list; /**< maintaining multiple AP */ - char *denylist; /**< maintaining the denylist */ + enum vwifi_state state; /**< indicate the program state */ + struct list_head vif_list; /**< maintaining all interfaces */ + struct list_head ap_list; /**< maintaining multiple AP */ + struct list_head ibss_list; /**< maintaining all ibss devices */ + char *denylist; /**< maintaining the denylist */ }; static DEFINE_SPINLOCK(vif_list_lock); @@ -81,7 +82,7 @@ static atomic_t vwifi_wiphy_counter = ATOMIC_INIT(0); /* Virtual interface pointed to by netdev_priv(). Fields in the structure are * interface-dependent. Every interface has its own vwifi_vif, regardless of the - * interface mode (STA, AP, Ad-hoc...). + * interface mode (STA, AP, IBSS...). */ struct vwifi_vif { struct wireless_dev wdev; @@ -103,7 +104,7 @@ struct vwifi_vif { struct mutex lock; - /* Split logic for STA and AP mode */ + /* Split logic for the interface mode */ union { /* Structure for STA mode */ struct { @@ -138,6 +139,47 @@ struct vwifi_vif { struct ieee80211_channel *channel; enum nl80211_chan_width bw; }; + /* Structure for IBSS(ad hoc) mode */ + struct { + /* List node for storing ibss devices (vwifi->ibss_list is the + * head), this field is for interface in IBSS mode. + */ + struct list_head ibss_list; + /* defines the channel to use if no other IBSS to join can be found + */ + struct cfg80211_chan_def ibss_chandef; + u16 ibss_beacon_int; + /* bitmap of basic rates */ + u32 ibss_basic_rates; + /* The channel should be fixed -- do not search for IBSSs to join on + * other channels. */ + bool ibss_channel_fixed; + /* This is a protected network, keys will be configured after + * joining */ + bool ibss_privacy; + /* whether user space controls IEEE 802.1X port, i.e., + * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is + * required to assume that the port is unauthorized until authorized + * by user space. Otherwise, port is marked authorized by default. + */ + bool ibss_control_port; + /* TRUE if userspace expects to exchange control + * port frames over NL80211 instead of the network interface. + */ + bool ibss_control_port_over_nl80211; + /* whether user space controls DFS operation */ + bool ibss_userspace_handles_dfs; + /* per-band multicast rate index + 1 (0: disabled) */ + int ibss_mcast_rate[NUM_NL80211_BANDS]; + /* HT Capabilities over-rides. */ + struct ieee80211_ht_cap ibss_ht_capa; + /* The bits of ht_capa which are to be used. */ + struct ieee80211_ht_cap ibss_ht_capa_mask; + /* static WEP keys */ + struct key_params *ibss_wep_keys; + /* key index (0..3) of the default TX static WEP key */ + int ibss_wep_tx_key; + }; }; struct timer_list scan_complete; @@ -506,6 +548,54 @@ static void inform_bss(struct vwifi_vif *vif) } } +/* Helper function that prepares a structure with self-defined BSS information + * and "informs" the kernel about the "new" Independent BSS. + */ +static void ibss_inform_bss(struct vwifi_vif *vif) +{ + struct vwifi_vif *ibss; + + list_for_each_entry (ibss, &vwifi->ibss_list, ibss_list) { + struct cfg80211_bss *bss = NULL; + struct cfg80211_inform_bss data = { + /* the only channel */ + .chan = &ibss->wdev.wiphy->bands[NL80211_BAND_2GHZ]->channels[0], +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) + .scan_width = NL80211_BSS_CHAN_WIDTH_20, +#endif + .signal = DBM_TO_MBM(rand_int_smooth(-100, -30, jiffies)), + }; + int capability = WLAN_CAPABILITY_IBSS; + + if (ibss->ibss_privacy) + capability |= WLAN_CAPABILITY_PRIVACY; + + pr_info("vwifi: %s performs scan, found %s (SSID: %s, BSSID: %pM)\n", + vif->ndev->name, ibss->ndev->name, ibss->ssid, ibss->bssid); + pr_info("cap = %d, beacon_ie_len = %d\n", capability, + ibss->beacon_ie_len); + + /* Using the CLOCK_BOOTTIME clock, which remains unaffected by changes + * in the system time-of-day clock and includes any time that the + * system is suspended. + * This clock is suitable for synchronizing the machines in the BSS + * using tsf. + */ + u64 tsf = div_u64(ktime_get_boottime_ns(), 1000); + + /* It is possible to use cfg80211_inform_bss() instead. */ + bss = cfg80211_inform_bss_data( + vif->wdev.wiphy, &data, CFG80211_BSS_FTYPE_UNKNOWN, ibss->bssid, + tsf, capability, ibss->ibss_beacon_int, ibss->beacon_ie, + ibss->beacon_ie_len, GFP_KERNEL); + + /* cfg80211_inform_bss_data() returns cfg80211_bss structure reference + * counter of which should be decremented if it is unused. + */ + cfg80211_put_bss(vif->wdev.wiphy, bss); + } +} + static void vwifi_beacon_inform_bss(struct vwifi_vif *ap, struct vwifi_vif *sta, struct cfg80211_inform_bss *bss_meta, @@ -734,6 +824,10 @@ static int __vwifi_ndo_start_xmit(struct vwifi_vif *vif, pr_info("vwifi: AP %s (%pM) send packet to STA %s (%pM)\n", vif->ndev->name, eth_hdr->h_source, dest_vif->ndev->name, eth_hdr->h_dest); + } else if (vif->wdev.iftype == NL80211_IFTYPE_ADHOC) { + pr_info("vwifi: IBSS %s (%pM) send packet to IBSS %s (%pM)\n", + vif->ndev->name, eth_hdr->h_source, dest_vif->ndev->name, + eth_hdr->h_dest); } pkt = kmalloc(sizeof(struct vwifi_packet), GFP_KERNEL); @@ -771,6 +865,10 @@ static int __vwifi_ndo_start_xmit(struct vwifi_vif *vif, pr_info("vwifi: AP %s (%pM) receive packet from STA %s (%pM)\n", dest_vif->ndev->name, eth_hdr->h_dest, vif->ndev->name, eth_hdr->h_source); + } else if (dest_vif->wdev.iftype == NL80211_IFTYPE_ADHOC) { + pr_info("vwifi: IBSS %s (%pM) receive packet from IBSS %s (%pM)\n", + dest_vif->ndev->name, eth_hdr->h_dest, vif->ndev->name, + eth_hdr->h_source); } /* Directly send to rx_queue, simulate the rx interrupt */ @@ -859,6 +957,49 @@ static netdev_tx_t vwifi_ndo_start_xmit(struct sk_buff *skb, } } } + /* TX by interface of IBSS(ad-hoc) mode */ + else if (vif->wdev.iftype == NL80211_IFTYPE_ADHOC) { + /* Check if the packet is broadcasting */ + if (is_broadcast_ether_addr(eth_hdr->h_dest)) { + list_for_each_entry (dest_vif, &vwifi->ibss_list, ibss_list) { + /* Don't send broadcast packet back to the source interface. + */ + if (ether_addr_equal(eth_hdr->h_source, + dest_vif->ndev->dev_addr)) + continue; + /* Don't send packet from dest_vif's denylist */ + if (denylist_check(dest_vif->ndev->name, vif->ndev->name)) + continue; + /* Don't send packet to device with different SSID. */ + if (strcmp(vif->ssid, dest_vif->ssid)) + continue; + /* Don't send packet to device with different BSSID. */ + if (!ether_addr_equal(vif->bssid, dest_vif->bssid)) + continue; + if (__vwifi_ndo_start_xmit(vif, dest_vif, skb)) + count++; + } + } + /* The packet is unicasting */ + else { + list_for_each_entry (dest_vif, &vwifi->ibss_list, ibss_list) { + if (ether_addr_equal(eth_hdr->h_dest, + dest_vif->ndev->dev_addr)) { + /* Don't send packet from dest_vif's denylist */ + if (denylist_check(dest_vif->ndev->name, vif->ndev->name)) + continue; + /* Don't send packet to device with different SSID. */ + if (strcmp(vif->ssid, dest_vif->ssid)) + continue; + /* Don't send packet to device with different BSSID. */ + if (!ether_addr_equal(vif->bssid, dest_vif->bssid)) + continue; + if (__vwifi_ndo_start_xmit(vif, dest_vif, skb)) + count++; + } + } + } + } if (!count) vif->stats.tx_dropped++; @@ -895,6 +1036,7 @@ static void vwifi_scan_timeout_work(struct work_struct *w) /* inform with dummy BSS */ inform_bss(vif); + ibss_inform_bss(vif); if (mutex_lock_interruptible(&vif->lock)) return; @@ -1440,6 +1582,10 @@ static int vwifi_change_iface(struct wiphy *wiphy, case NL80211_IFTYPE_AP: ndev->ieee80211_ptr->iftype = type; break; + case NL80211_IFTYPE_ADHOC: + pr_info("vwifi: %s start acting in IBSS mode.\n", ndev->name); + ndev->ieee80211_ptr->iftype = type; + break; default: pr_info("vwifi: invalid interface type %u\n", type); return -EINVAL; @@ -1915,6 +2061,91 @@ static int vwifi_get_tx_power(struct wiphy *wiphy, return 0; } +/* Make the device join a specific IBSS. */ +static int vwifi_join_ibss(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_ibss_params *params) +{ + struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); + /* Validate vif pointer */ + if (!vif) + return -EINVAL; + if (mutex_lock_interruptible(&vif->lock)) + return -ERESTARTSYS; + /* Retrieve IBSS configuration parameters */ + memcpy(vif->ssid, params->ssid, params->ssid_len); + vif->ibss_chandef = params->chandef; + vif->ssid_len = params->ssid_len; + /* When the BSSID is automatically generated by the system, it will not be + * passed as a parameter to the join function. */ + if (params->bssid) + memcpy(vif->bssid, params->bssid, ETH_ALEN); + else { + /* Search for IBSS networks with WPA settings in the IBSS list. If a + * matching network exists, join it. Otherwise, create one. */ + memcpy(vif->bssid, ndev->dev_addr, ETH_ALEN); + struct vwifi_vif *ibss_vif = NULL; + list_for_each_entry (ibss_vif, &vwifi->ibss_list, ibss_list) { + if (ibss_vif->ssid_len == vif->ssid_len && + !memcmp(ibss_vif->ssid, vif->ssid, vif->ssid_len) && + ibss_vif->ibss_chandef.center_freq1 == + vif->ibss_chandef.center_freq1) { + memcpy(vif->bssid, ibss_vif->bssid, ETH_ALEN); + break; + } + } + } + vif->beacon_ie_len = params->ie_len; + memcpy(vif->beacon_ie, params->ie, params->ie_len); + vif->ibss_beacon_int = params->beacon_interval; + vif->ibss_basic_rates = params->basic_rates; + vif->ibss_channel_fixed = params->channel_fixed; + vif->ibss_privacy = params->privacy; + vif->ibss_control_port = params->control_port; + vif->ibss_control_port_over_nl80211 = params->control_port_over_nl80211; + vif->ibss_userspace_handles_dfs = params->userspace_handles_dfs; + memcpy(vif->ibss_mcast_rate, params->mcast_rate, + sizeof(params->mcast_rate)); + vif->ibss_ht_capa = params->ht_capa; + vif->ibss_ht_capa_mask = params->ht_capa_mask; + vif->ibss_wep_keys = params->wep_keys; + vif->ibss_wep_tx_key = params->wep_tx_key; + + mutex_unlock(&vif->lock); + + /* Insert ibss into global ibss_list */ + if (mutex_lock_interruptible(&vwifi->lock)) + return -ERESTARTSYS; + + list_add_tail(&vif->ibss_list, &vwifi->ibss_list); + + mutex_unlock(&vwifi->lock); + + pr_info("vwifi : %s join %s.\n", vif->ndev->name, vif->ssid); + + return 0; +} + +/* Make the device leave the current IBSS. */ +static int vwifi_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) +{ + struct vwifi_vif *vif = ndev_get_vwifi_vif(ndev); + /* Validate vif pointer */ + if (!vif) + return -EINVAL; + /* Remove ibss from global ibss_list */ + if (mutex_lock_interruptible(&vwifi->lock)) + return -ERESTARTSYS; + + list_del(&vif->ibss_list); + + mutex_unlock(&vwifi->lock); + + pr_info("vwifi : %s leave %s.\n", vif->ndev->name, vif->ssid); + + return 0; +} + /* Structure of functions for FullMAC 80211 drivers. Functions implemented * along with fields/flags in the wiphy structure represent driver features. * This module can only perform "scan" and "connect". Some functions cannot @@ -1937,6 +2168,8 @@ static struct cfg80211_ops vwifi_cfg_ops = { .change_station = vwifi_change_station, .set_tx_power = vwifi_set_tx_power, .get_tx_power = vwifi_get_tx_power, + .join_ibss = vwifi_join_ibss, + .leave_ibss = vwifi_leave_ibss, }; /* Macro for defining 2GHZ channel array */ @@ -2075,8 +2308,8 @@ static struct wiphy *vwifi_cfg80211_add(void) * add other required types like "BIT(NL80211_IFTYPE_STATION) | * BIT(NL80211_IFTYPE_AP)" etc. */ - wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC); for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { /* FIXME: add other band capabilities if needed, such as 40 width */ @@ -3096,6 +3329,7 @@ static int __init vwifi_init(void) mutex_init(&vwifi->lock); INIT_LIST_HEAD(&vwifi->vif_list); INIT_LIST_HEAD(&vwifi->ap_list); + INIT_LIST_HEAD(&vwifi->ibss_list); vwifi->denylist = kmalloc(sizeof(char) * MAX_DENYLIST_SIZE, GFP_KERNEL); for (int i = 0; i < station; i++) {