Creating Virtual WLAN Interfaces


This article has been contributed by Felix Niederwanger, Software Engineer at SUSE.




When it comes to testing and virtual devices for testing purposes, the Linux kernel is always a surprisingly rich source. I’m at the moment working on setting up an openQA test for wpa_supplicant, probably the most widespread program for protected WiFi access. wpa_supplicant is also used in Android and is the part, that provides most of the higher level WiFi functionality. In order to verify that wpa_supplicant keeps working, I created an automated test that checks for every update if it still works. I used virtual WLAN interfaces and network namespace separation, so that the test is self-containing (i.e. it runs on a single virtual machine) and portable.

So, in this guide we are going to do the following:

  • Set up two virtual WLAN interfaces (WLAN0 and WLAN1)
  • Isolate WLAN0 and WLAN1 into two network namespaces
  • Create a virtual access point using hostapd and dnsmasq
  • Connect the WiFi client to the network
  • Run basic network ping tests

You will need the following:

  • A physical or virtual machine running openSUSE Leap/Tumbleweed (but it should work on other distributions as well)
  • Root access to that machine



First install the requirements for this guide:

leap-15_1:~ # zypper in hostapd dnsmasq wpa_supplicant iw

Set Up the WLAN Interfaces

The Linux kernel provides already everything we need via the mac80211_hwsim module. It’s a software simulator for IEEE 802.11 radio networks (commonly known as WLAN networks) written by Jouni Malinen. This kernel module is a piece of art – it creates an arbitrary amount of virtual WiFi interfaces that can communicate with each other if they are tuned to the same channel. Like this it’s possible to simulate various arbitrarily complex scenarios, ranging from a simple two device network (like we will be using) to complex setups with multiple clients connecting to various hotspots that run on different channels.

Set up the WiFi interfaces as follows:

leap-15_1:~ # modprobe mac80211_hwsim radios=2

The argument radios=2 defines how many virtual interfaces will be created and defaults to two devices. After successfully loading the kernel module,WLAN0 and WLAN1 are showing up, as shown in figure 1 below (execute ip link). The third interface that pops up is hwsim0. As stated before, WLAN interfaces can be tuned to channels and only can send and receive radio frames on that particular channel. hwsim0 is a virtual interface for debugging purposes, where you could listen to all radio frames on all channels. We won’t need it for this guide.


mac80211_hwsim module is loaded and shows two WLAN interfaces

Isolating the Interfaces into Namespaces

After setting up the interfaces, all of them are visible and accessible from the system. We need to separate the two WiFi interfaces, so that we are able to test the connectivity between them. Let’s assume WLAN0 has the IP address and WLAN1 has the IP address If you now perform a simple ping on one of the two addresses, the system will perform the ping directly on that interface. In this configuration we cannot test if the packets would go over the virtual WiFi.

Luckily, network namespace separation allows us, to separate interfaces from each other. More specifically, it allows us to run a separate shell, where one of the WLAN interfaces will be. In that configuration, the system has no other possibility than to route packets over the WLAN interface to reach the other one. See the following figures for an illustration of the separation:


Without network namespace separation, both interfaces are visible


Interfaces in different network namespace don’t see each other. In this configuration, packets need to go over the virtual wifi to reach each other

We are going to create a network namespace called wifi_master for the WLAN0 interface and run a separate shell in that namespace. With that we end up having two shells: One for the WiFi hotspot (wifi_master) and one for the WiFi client. Both shells only see their corresponding WLAN interface and don’t know the other one exists.

First create the network namespace:

leap-15_1:~ # ip netns add wifi_master
leap-15_1:~ # ip netns list

One of the most convenient ways to assign network namespaces would be via ip link set. Unfortunately this doesn’t work with the virtual interfaces, thus we need to use the iw utility for that.

leap-15_1:~ # ip link set dev wlan1 netns wifi_master
RTNETLINK answers: Invalid argument

Assigning WLAN interfaces with the iw utility works on a process ID basis. So we first are going to launch our WiFi master shell in the new network namespace:

# Run this in a separate shell.
leap-15_1:~ # ip netns exec wifi_master bash
leap-15_1:~ # echo $BASHPID

This will be our WiFi hotspot called wifi_master and has the pid 3282. Now, from our first shell, where WLAN0 is still visible, we are going to assign WLAN0 to the wifi_master namespace (replace 3282 with the pid of your wifi_master shell):

leap-15_1:~ # iw phy phy0 set netns 3282


WiFi master shell


WiFi client shell

The result should be those two shells: WiFi master with the WLAN0 interface and WiFi client with the WLAN1 interface.


Configure the Access Point

In the WiFi master shell we configure now the access point. The interface gets a static IP, dnsmasq will run as DHCP server and hostapd for the WiFi setup. First we set the static IP and setup dnsmasq as DHCP server (and run it as background job):

leap-15_1:~ # ip addr add dev wlan0

Next we setup dnsmasq as DHCP server and run it as background job:

leap-15_1:~ # dnsmasq -i wlan0 --dhcp-range=, &

For hostapd we use the following, very trivial configuration file hostapd.conf:

# hostapd.conf
ssid=Virtual Wifi
wpa_pairwise=TKIP CCMP

Then we run hostapd with the following command:

leap-15_1:~ # hostapd hostapd.conf

You should see something similar to the screen below:


Wifi access point is started

Congratulations!  The access point is up and running. The next step is to configure the WiFi client.


Connecting the WiFi Client

We use wpa_supplicant to connect to the WiFi access point. In the Wifi client shell, we first create the following very simple wpa_supplicant.conf file:

# wpa_supplicant.conf
  ssid="Virtual Wifi"

and connect to the access point via the following command:

# Run those commands in the wifi-client shell!
# Note: -B is for run in background
leap-15_1:~ # wpa_supplicant -B -i wlan1 -c wpa_supplicant.conf

You should now see the authentication in the WiFi master terminal:


WPA handshake completed on the WiFi access point


DHCP Client Using Wicked

Create the following very basic configuration file for wicked:

# /etc/sysconfig/network/ifcfg-wlan1

Then restart wicked to get the new IP address:

leap-15_1:~ # systemctl restart wicked
leap-15_1:~ # ip addr show dev wlan1
4: wlan1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN 
group default qlen 1000
    link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::ff:fe00:100/64 scope link 
       valid_lft forever preferred_lft forever

Finally ping the access point:

leap-15_1:~ # ping -c 4


Ping works successfully in the WiFi client shell

Congratulations! You have successfully set up your virtual WLAN network!


(Visited 1 times, 1 visits today)

Leave a Reply

Your email address will not be published. Required fields are marked *

No comments yet

Meike Chabowski
Meike Chabowski

Meike Chabowski works as Documentation Strategist at SUSE. Before joining the SUSE Documentation team, she was Product Marketing Manager for Enterprise Linux Servers at SUSE, with a focus on Linux for Mainframes, Linux in Retail, and High Performance Computing. Prior to joining SUSE more than 20 years ago, Meike held marketing positions with several IT companies like defacto and Siemens, and was working as Assistant Professor for Mass Media. Meike holds a Master of Arts in Science of Mass Media and Theatre, as well as a Master of Arts in Education from University of Erlangen-Nuremberg/ Germany, and in Italian Literature and Language from University of Parma/Italy.