A virtual wlan network in Linux

The Linux Kernel is mighty when it comes to virtual devices for testing purposes. In this guide we are going to setup a virtual wlan hotspot using virtual wlan devices on a single Linux machine. I wrote this guide while developing an automated test for wpa_supplicant in order to document and understand the principles behind it.

In this guide, we are going to

  • Setup two virtual wlan interfaces (for the master and one for the client)
  • Isolate the master wlan interface in a separate Linux network namespace so it becomes "invisible"
  • Create a virtual access point using hostapd
  • Connect the client to the network
  • Ping the master node from the client

I'm using a virtual machine running openSUSE Leap 15.1. I do not see any reason why it should not work on any other Linux distribution as well.

Setup of the wlan interfaces

There is a Linux Kernel module that provides virtual wlan-interfaces for testing purposes: mac80211_hwsim.
When loading this kernel module, by default you get two wlan interfaces (wlan0 and wlan1) plus a monitor device hwsim0. Like real wlan interfaces, wlan0 and wlan1 can be tuned to channels. Only wlan interfaces that are on the same channel, receive radio frames from each other.

We start this guide by loading the mac80211_hwsim module:

You can see, that we got three interfaces: wlan0, wlan and hswim0. hswim0 is another purely virtual interface, that lets you listen to all frames on all channels. We do not need this one in this guide.

You can use the radios=N option, if you need more interfaces

For this guide, two interfaces are enough.

Namespace-isolating the interfaces

In order to accurately test the wifi-master (that's the access point we are connecting to) and the wifi-client (the client connecting to that access point) we need to put the interfaces into different network namespaces. Namespaces are an abstraction in the Linux kernel, to isolate processes and resources. More specifically, we use network namespace to create two shells, where each shell only has one wlan interface. Like this, one shell acts as wifi_master and acts as access point (and only sees wlan0), and the second shell acts as client and only sees wlan1. This is necessary, so that we force the network traffic to go from one interface to the other, and not directly to the target interface:

Without network namespace separation, both interfaces are visible to the shell
With network namespace separation, each shell only sees their interface - thus they are forced to go over their wlan interface to reach the other ip address

So, first we create the wifi_master namespace

Now, we are going to run a separate shell in the wifi_master namespace and assign the wlan0 device to the pid of the new shell. For that, open a new terminal window and execute the following

The last line prints out the process id (or pid) of the shell. We need that, because the wlan interface can only be assigned to a pid via the iw utility. In another shell (this will be the client shell) we assign the wlan0 interface to the wifi_master shell

After that, check the interfaces that are available for each of the shells: for the wifi_master shell and for the client shell by running ip link

The wifi_master has the wlan0 interfaces, the client shell has the other interfaces (especially wlan1) but not wlan0 anymore.

Left side: wifi_master, right side wifi_client.

At this stage we have two shells, one has wlan0 the other one wlan1 (and the other interface we don't care about) and we can setup a wlan hotspot with hostapd.

Creating access point with hostapd

First make sure hostapd is installed (ships with your default package manager for most distributions)

Then create the following, very basic hostapd.conf file. The absolute path does not matter, as long as the shell and the file are in the same path

In the wifi_master shell now run hostapd with the custom hostapd.conf file

In the client shell, create the following wpa_supplicant.conf file. Also here, the absolute path is irrelevant, as long as the shell is in the same directory.

Execute wpa_supplicant with the config file:

This activates the interface, scans for wireless networks and connects to the known network "Virtual Wifi".
Now monitor the wifi_master shell. Something like this should pop up in the next seconds

This tells us that wlan0 and wlan1 are now connected and authenticated to each other. Congratulations! You just connected to your own virtual wlan network 🙂

ping!

Now we want to test, if we can reach the wifi master via the wifi client.

The easiest way is to use IPv6 link-local addresses, but we are also going to do a more conservative IPv4 ping test.

Zero-configuration ping using IPv6

Every IPv6 capable device gets a unique link-local address without any configuration. This is pretty cool and handy - we are going to use this property for our most simple first ping test. First in the wifi_master shell, we need to find out what our link-local address is.
For that we send hostapd in the background (CTRL+Z, followed by bg) and run ip -6 a to list our IPv6 addresses

The IPv6 address of the wifi-master is fe80::ff:fe00:0.

In our client shell, we are going to ping that address

Note: The address we are pinging is fe80::ff:fe00:0%wlan1 - Link local addresses need the interface as well where they can be found. This is necessary, as every device has it's own network of link-local addresses. For any other IPv6 addresses, the interface suffix is not necessary.

Pretty neat - we are able to ping the wifi-master without any IP configuration. Our virtual wifi network works!

Ping over IPv4 with static configuration

IPv4 is a bit more complicated, as we need to first configure the interfaces. We are going to assign static ip-addresses to wlan0 and wlan1by using ip addr add commands:

Now in the client shell we can ping the wifi-master

Congratulations! You have successfully created a virtual wlan network of two interface, that are separated in network namespaces and are able to communicate with each other.


Known issues

Adding interface to namespace via ip link set doesn't work

I know but I don't know why.

Using the iw utility works, but then you are limited to assigning the interface only to one single process. In the end I decided to follow this path and work from a separate shell

It's not the most elegant solution, but it works.

1 thought on “A virtual wlan network in Linux”

  1. Nice! I had no idea you could setup virtual wifi interfaces. Great job! Shame ip link isn't working as expected but glad you found a workaround 🙂

    Matt

Leave a Reply

Your email address will not be published.