Skip to content

Commit 038b13d

Browse files
authored
Support virtio-net (#45)
This patch provides preliminary support for virtio-net, enabling inter-guest communication with the correct settings. To enable virtio support, we have made vwifi capable of recognizing virtio-net devices by registering vwifi with the virtio driver and providing the necessary probe/remove functions. Regarding virtqueues (referred to as vq), we currently have one RX vq and one TX vq (multi-queue support is not yet available): - TX vq: Whenever a frame needs to be sent, we add an output buffer to the TX vq and call virtqueue_kick() to transmit the data to the virtio-net device. - RX vq: Upon ndo_open(), we populate an input buffer into the RX vq and replenish the input buffer with each RX interrupt. It is important to note that when putting/getting buffers or kicking buffers, we must hold the virtio spinlock to ensure that no other operations occur on the vq simultaneously. Additionally, while acquiring the spinlock, we should avoid calling dev_kfree_skb() (as it is not safe in interrupt context) and restrict ourselves to using kmalloc() with GFP_ATOMIC. We use spin_lock_irqsave() because the TX/RX completion interrupt might be reentrant and could potentially lead to deadlocks. As of now, vwifi does not support most of the virtio-net features. To provide future support, our focus should initially be on implementing multi-queue support and incorporating NAPI on the RX path. In the context of vwifi management frames, originally, vwifi performed management operations, such as cfg80211_ops->scan(), and handled the TX/RX path using a 'shortcut' method. This entailed acquiring the network interface structures and struct owl_vif by iterating through the vif list and locating the one with the same MAC address. This approach was feasible only when all interfaces resided on the same host. To address the challenge of dealing with vwifi spread across multiple machines, we have introduced the concept of "vwifi management frames." Many of these management frame types draw inspiration from IEEE 802.11 management frames, with minor adjustments made due to the fact that we are dealing with Ethernet frames. It is important to note that we transmit these management frames with the Ethertype/length field set as "length" (i.e., 802.3 frames). This allows us to distinguish them from data frames (Ethernet II). Additionally, it is crucial to ensure that multi-octet fields follow a specific byte order so that data is consistently interpreted across different machines. We employ a little-endian byte order, making it possible for most machines to directly read the data. Multi-octet fields should be of the __leX type, and the sender is responsible for calling cpu_to_leX() on the data to be transmitted. This enables the receiver to utilize leX_to_cpu() to access and store the data in its machine's byte order. For a more comprehensive description of all management frame types (including how an STA handles frames from other BSS), please refer to the comments in vwifi.c, located above the definition of enum VWIFI_VIRTIO_PACKET_TYPE. Signed-off-by: EN-WEI WU <[email protected]>
1 parent ad2cb66 commit 038b13d

File tree

6 files changed

+1523
-18
lines changed

6 files changed

+1523
-18
lines changed

.clang-format

+2
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ ForEachMacros:
2727
- vec_foreach
2828
- hash_for_each_possible_safe
2929
- hash_for_each_safe
30+
- hash_for_each
31+
- hash_for_each_possible

README.md

+190-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ It is based on the [cfg80211 subsystem](https://www.kernel.org/doc/html/latest/d
66
which works together with FullMAC drivers.
77
Currently, vwifi supports both Station Mode and Host AP Mode, and is well-equipped with WPA/WPA2 security facilities. This enables users to set up a wireless environment using vwifi, hostapd (in HostAP mode interface), and wpa_supplicant (in station mode interface).
88

9+
Furthermore, when running on hypervisors like Qemu, `vwifi` becomes a `virtio-net` driver and has the capability to do inter-guest communication. To test `vwifi` with the virtio feature, [click here](#Testing-environment-(virtio)) to jump to the virtio section below.
10+
911
## Prerequisite
1012

1113
The following packages must be installed before building `vwifi`.
@@ -28,7 +30,7 @@ $ sudo apt install python3 python3-pip hostapd
2830
$ pip3 install numpy matplotlib
2931
```
3032

31-
## Testing environment
33+
## Testing environment (non-virtio)
3234
<p align="center"><img src="assets/vwifi.png" alt="logo image" width=60%></p>
3335

3436
The testing environment consists of **one AP and two STAs**.
@@ -50,7 +52,7 @@ the kernel will use the loopback device for packet transmission/reception. This
5052

5153
In conclusion, all the interfaces created by `vwifi` in the testing environment will be added to an isolated network namespace.
5254

53-
## Build and Run
55+
## Build and Run (non-virtio)
5456

5557
To build the kernel module, execute the following command:
5658
```shell
@@ -337,6 +339,190 @@ PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
337339
rtt min/avg/max/mdev = 0.054/0.141/0.342/0.117 ms
338340
```
339341

342+
## Testing environment (virtio)
343+
Below is our testing environment with virtio feature:
344+
345+
<p align="center"><img src="assets/vwifi_virtio_arch.drawio.png" alt="vwifi virtio image" width=60%></p>
346+
347+
vwifi makes use of the `virtio-net` device, Linux tap device, and Linux bridge device to fulfill inter-guest communication.
348+
In our testing environment, on the host side, we create three `tap` devices and one `bridge` device, then attach each `tap` device to the `bridge` device. And we create three VMs by `Qemu` with the `virtio-net` devices and specify that every `virtio-net` device connects to a `tap` device in the host.
349+
In VM1, the network interface created by `vwifi` will be in HostAP mode, with the user program `hostapd` running on top of it.
350+
In VM2 and VM3, we have an STA mode interface and `wpa_supplicant` running on top of it.
351+
## Build and Run (virtio)
352+
Below describes how to build a minimal workable VM environment.
353+
### Build Linux kernel
354+
Download Linux kernel source from [kernel.org](https://kernel.org/).
355+
356+
Enter the top directory of the Linux kernel source and use the following command to generate the configuration file:
357+
```shell
358+
make menuconfig
359+
```
360+
The default kernel cofiguration will work for our testing environment, so just click `save` and we get `.config` on the top directory.
361+
362+
Before building the kernel, please ensure that all the needed packges or libraries have been installed in your machine.
363+
364+
Then Build the kernel:
365+
```
366+
make -j<num threads>
367+
```
368+
369+
Once we have finished building the kernel, the `bzImage` will be in the `arch/<architecture>/boot/` directory. For x86 machine, that is `arch/x86/boot/bzImage`.
370+
371+
### Build `vwifi`
372+
Assume directory vwifi is cloned from [vwifi](https://github.com/sysprog21/vwifi), and enter the top directory.
373+
374+
`vwifi` let the `$(KDIR)` in `Makefile` point to the default kernel source on the host, but the kernel version of the host may differ from the one for the VMs, so we need to replace it to point to the kernel source we previously built for the VMs:
375+
```shell
376+
KDIR = <linux kernel top directory for VMs>
377+
```
378+
379+
Save the change and build `vwifi`:
380+
```shell
381+
make
382+
```
383+
384+
On success, we expect that `vwifi.ko` will show in the top directory of `vwifi`.
385+
386+
The cfg80211 API and net device API in the kernel have changed along with the new kernel version, and forgive us for not testing for all of them.
387+
### Build Rootfs by Buildroot
388+
We use `buildroot` to build our rootfs, you can either choose your desired tool for building rootfs.
389+
390+
Get the `builtroot` from [https://buildroot.org](https://buildroot.org).
391+
392+
Enter the top directory of the `buildroot` source and use the following command to generate the first-phase configuration file:
393+
```shell
394+
make qemu_x86_64_defconfig
395+
```
396+
397+
The `qemu_x86_64_defconfig` configuration will not fit the testing environment, so we need to further configure it:
398+
```shell
399+
make menuconfig
400+
```
401+
402+
We need a filesystem overlay for putting our `vwifi` kernel module and some user program's configuration file into the VM, so choose a place for the filesystem overlay (we recommand creating an empty directory for it).
403+
404+
Also, we need network applications like `iw`, `hostapd` and `wpa_supplicant`.
405+
```
406+
System configuration ---> Run a getty (login prompt) after boot ---> TTY port ---> ttyS0
407+
System configuration ---> Root filesystem overlay directories ---> <the place you wnat>
408+
Kernel ---> Linux Kernel ---> no
409+
Target packages ---> Networking applications ---> hostapd
410+
Target packages ---> Networking applications ---> iw
411+
Target packages ---> Networking applications ---> wpa_supplicant
412+
Filesystem images ---> ext2/3/4 root filesystem
413+
```
414+
415+
After finishing the configuration, we can put the needed kernel modules and configuration files into the filesystem overlay directory:
416+
```shell
417+
cp <linux-top-dir>/net/wireless/cfg80211.ko \
418+
<vwifi-top-dir>/vwifi.ko \
419+
<vwifi-top-dir>/scripts/hostapd.conf \
420+
<vwifi-top-dir>/scripts/wpa_supplicant.conf \
421+
<overlay-dir>
422+
```
423+
424+
Then start building rootfs:
425+
```shell
426+
make
427+
```
428+
This may take a long time, please be patient.
429+
430+
The output image will be `output/images/rootfs.ext2`. We need three images for three VMs, so we simply copy this file into three:
431+
```shell
432+
cp output/images/rootfs.ext2 output/images/rootfs2.ext2
433+
cp output/images/rootfs.ext2 output/images/rootfs3.ext2
434+
```
435+
### Setup Host Network Device
436+
We need three `tap` devices and each of them must be attached to a `bridge` device. Please note, creating `tap` and `bridge` devices needs privilege permission.
437+
438+
Creating three `tap` devices:
439+
```shell
440+
sudo ip tuntap add mode tap tap0
441+
sudo ip tuntap add mode tap tap1
442+
sudo ip tuntap add mode tap tap2
443+
```
444+
445+
Creating `bridge` device:
446+
```shell
447+
sudo ip link add name br0 type bridge
448+
```
449+
450+
Attach three `tap` devices on `bridge` device:
451+
```shell
452+
sudo ip link set tap0 master br0
453+
sudo ip link set tap1 master br0
454+
sudo ip link set tap2 master br0
455+
```
456+
457+
### Start VM with Qemu
458+
Once we have our kernel image and rootfs, we can start running `Qemu`:
459+
460+
```shell
461+
sudo qemu-system-x86_64 -kernel bzImage \
462+
-drive file=<buildroot rootfs image> -nographic \
463+
-append "console=ttyS0" \
464+
-append root=/dev/sda \
465+
-netdev tap,id=<any name>,ifname=<host tap device> \
466+
-device virtio-net-pci,netdev=<the name in id=>,mac=<MAC address>,mrg_rxbuf=off \
467+
-serial mon:stdio
468+
```
469+
470+
You need to run the command above three times, please ensure the `buildroot` rootfs image, `tap` device and MAC address in every VM must be different. Also, ensure that the `mrg_rxbuf=off` has been specified.
471+
472+
### Needed Steps in Every VM
473+
#### Raondom Number Generator
474+
`hostapd` and `wpa_supplicant` need the random number generator `/dev/random` for generating the random number used in a 4-way handshake. However, for some reason (which may be related to IRQ), accessing `/dev/random` may be not available or even not possible. And we found that `/dev/urandom` is always available, so we use a soft link to let the `/dev/random` link to `/dev/urandom`:
475+
```shell
476+
mv /dev/random /dev/random.orig
477+
ln -s /dev/urandom /dev/random
478+
```
479+
#### Loading Kernel Modules
480+
`vwifi` depends on `cfg80211.ko`, so firstly we load the `cfg80211.ko`:
481+
```shell
482+
insmod cfg80211.ko
483+
```
484+
485+
Then we can load our `vwifi.ko`. Note that for now, we only allow single network interface in `vwifi` when it's running on virtio:
486+
```shell
487+
insmod vwifi.ko station=1
488+
```
489+
490+
#### Setting Network Interface
491+
Start the network interface:
492+
```shell
493+
ip link set owl0 up
494+
```
495+
And assign an IP address. Note that, we should assign different IP addresses (but the same subnet) for every network interface in the three VMs:
496+
```shell
497+
ip addr add <IP address/netmask> dev owl0
498+
```
499+
### Start `hostapd` and `wpa_supplicant`
500+
In our testing environment, the HostAP mode interface is in VM1, so running `hostapd` on VM1:
501+
```shell
502+
hostapd -i owl0 -B hostapd.conf
503+
```
504+
And running `wpa_supplicant` on the other two VMs:
505+
```shell
506+
wpa_supplicant -i owl0 -B -c wpa_supplicant.conf
507+
```
508+
509+
For now, the two STA mode interfaces in VM1 and VM2 should have been connected to the AP mode interface in VM0.
510+
511+
For validating the connection for the STA mode network interface, use the following command:
512+
```shell
513+
iw dev owl0 link
514+
```
515+
### Ping Test
516+
In VM1, we can ping the network interfaces in VM2 and VM3:
517+
```
518+
ping <vm2 interface's ip address>
519+
```
520+
521+
```
522+
ping <vm3 interface's ip address>
523+
```
524+
Likewise, VM2 and VM3 can ping the other VMs as well.
525+
340526
### Optional: Monitoring Wireless Device
341527

342528
If desired, you can use wireless device monitoring applications such as [wavemon](https://github.com/uoaerg/wavemon) to observe signal and noise levels,
@@ -359,3 +545,5 @@ by a MIT-style license that can be found in the LICENSE file.
359545
* [Emulating WLAN in Linux - part II: mac80211_hwsim](https://linuxembedded.fr/2021/01/emulating-wlan-in-linux-part-ii-mac80211hwsim)
360546
* [virt_wifi](https://github.com/torvalds/linux/blob/master/drivers/net/wireless/virtual/virt_wifi.c): a complete virtual wireless driver that can be used as a wrapper around Ethernet.
361547
* [vwifi](https://github.com/Raizo62/vwifi): simulate Wi-Fi (802.11) between Linux Virtual Machines.
548+
* [virtio-overview](https://www.redhat.com/en/blog/virtio-devices-and-drivers-overview-headjack-and-phone): an virtio overview.
549+
* [virtio: How data travels](https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels): explain the virtqueue and virtio ring.

assets/vwifi_virtio_arch.drawio.png

182 KB
Loading

scripts/hostapd.conf

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
interface=owl0
22
driver=nl80211
3-
ssid=TestAP
3+
debug=1
4+
ctrl_interface=/var/run/hostapd
5+
ctrl_interface_group=wheel
46
channel=6
7+
ssid=test
8+
wpa=2
9+
wpa_passphrase=12345678
10+
wpa_key_mgmt=WPA-PSK
11+
wpa_pairwise=CCMP

scripts/wpa_supplicant.conf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
network={
2+
ssid="test"
3+
psk="12345678"
4+
}

0 commit comments

Comments
 (0)