How to setup a wifi software access-point, using wpasupplicant and hostapd.

We create two virtual interfaces on top of one physical device. One will act as a client, the other as an access-point.

Most of the credit for this tutorial goes to the Archlinux Software Access Point Wiki.

Introduction

We use an atheros-powered wifi dongle, but you can use any wifi chip, as long as it can handle virtual interfaces. There’s a list of hardware with their capabilities on linux-wireless.

Installation

At first, we have to check if the firmware for our wifi chip is installed. You may not know that, but most (maybe every) wifi chip needs a firmware to operate, and this firmware is loaded dynamically when the chip is powered. Hence the firmware files must be installed. Plenty of firmwares are not free, and they’re not included by default in Debian.

Type dmesg -w in your console, then plug the wifi dongle.

$ dmesg -w
usb 1-1.4: new high-speed USB device number 4 using dwc_otg
usb 1-1.4: New USB device found, idVendor=0846, idProduct=9030
usb 1-1.4: New USB device strings: Mfr=16, Product=32, SerialNumber=48
usb 1-1.4: Product: WNA1100
usb 1-1.4: Manufacturer: NETGEAR WNA
usb 1-1.4: SerialNumber: 12345
cfg80211: Calling CRDA to update world regulatory domain
usb 1-1.4: ath9k_htc: Firmware htc_9271.fw requested
usbcore: registered new interface driver ath9k_htc
usb 1-1.4: firmware: failed to load htc_9271.fw (-2)
usb 1-1.4: ath9k_htc: USB layer deinitialized

See the line failed to load htc_9271.fw ? The firmware requested is not installed. Let’s check what firmwares are installed. On a fresh Debian install, there should be only the free firmwares.

$ dpkg -l | grep firmware
ii  firmware-linux-free 3.3 ...

Ok, so we can probably find our firmware in the non-free Debian repository. We must enable it, then find the package containing our firmware files. The name of the package is common sense, in our case it’s firmware-atheros. Let’s go.

$ vi /etc/apt/sources.list
# Add non-free to the main debian repository, at the end of the line
$ apt-get update
$ apt-get install firmware-atheros

Now, plug the wifi dongle again. This time, the output of dmesg is different:

$ dmesg -w
usb 1-1.4: ath9k_htc: Firmware htc_9271.fw requested
usb 1-1.4: firmware: direct-loading firmware htc_9271.fw
usb 1-1.4: ath9k_htc: Transferred FW: htc_9271.fw, size: 51272
ath9k_htc 1-1.4:1.0: ath9k_htc: HTC initialized with 33 credits
ath9k_htc 1-1.4:1.0: ath9k_htc: FW Version: 1.3
ath: EEPROM regdomain: 0x60
ath: EEPROM indicates we should expect a direct regpair map
ath: Country alpha2 being used: 00
ath: Regpair used: 0x60
ieee80211 phy0: Atheros AR9271 Rev:1

Virtual interfaces

Install the tools needed to configure wireless devices.

$ apt-get install iw

Have a look at the wireless devices present on the machine.

$ iw dev
phy#0
	Interface wlan0
		ifindex 3
		wdev 0x1
		addr 12:34:56:78:ab:cd
		type managed

Then show capabilities for the device.

$ iw phy phy0 info
...
	Supported interface modes:
	 * IBSS
	 * managed
	 * AP
	 * AP/VLAN
	 * monitor
	 * mesh point
	 * P2P-client
	 * P2P-GO
...
	valid interface combinations:
	 * #{ managed, P2P-client } <= 2, #{ AP, mesh point, P2P-GO } <= 2,
	   total <= 2, #channels <= 1

These lines shows that we can have only two virtual interfaces (total <= 2), and that the combination managed (aka station) and AP (aka access-point) is valid. That’s what we want. #channels <= 1 means that both interfaces will have to operate on the same channel. That’s a common limitation for most (maybe every) hardware.

Alright, let’s create our virtual interfaces:

$ iw dev wlan0 interface add wlan0_sta type station
$ iw dev wlan0 interface add wlan0_ap  type __ap

If you look at them with ifconfig -a, you notice that the interfaces have the same MAC address. This needs to be changed, every interface must have a unique MAC. You can put almost whathever you want (check MAC address on Wikipedia, the most significant octet is NOT everything you want). I personnally just tune the original address a little bit.

$ ip link set dev wlan0_sta address 12:34:56:78:ab:c0
$ ip link set dev wlan0_ap  address 12:34:56:78:ab:c1

Configure the station (wpasupplicant)

Credit: http://linuxcommando.blogspot.fr/2013/10/how-to-connect-to-wpawpa2-wifi-network.html

If you want your station to connect to a WPA-secured access-point, you will need WPA Supplicant.

$ apt-get install wpasupplicant

Alright, let’s have a look at the access-points around.

$ ifconfig wlan0_sta up
$ iw dev wlan0_sta scan
...
BSS de:ad:be:ef:ca:fe(on wlan0_sta)
	...
	SSID: MYHAPPYAP
	RSN:  * Version: 1
		  * Group cipher: TKIP
		  * Pairwise ciphers: CCMP TKIP
		  * Authentication suites: PSK
		  * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c)
...

Here, I assume you want to connect to the access-point named MYHAPPYAP. The security protocol in use is RSN, which is another name for WPA2. Alright, to create the configuration file needed to connect to this access-point, there’s nothing easier:

$ wpa_passphrase MYHAPPYAP >> /etc/wpa_supplicant/wlan0_sta.conf
... type in the passphrase and hit enter ...

Now you can look at the configuration file generated if you’re curious, with something like cat /etc/wpa_supplicant/wlan0_sta.conf.

OK, we’re almost there. Ensure that wlan0_sta is not connected (just to show you one more command I learnt a few seconds ago :)

$ iw wlan0_sta link
Not connected.

Alright, let’s launch wpasupplicant.

$ wpa_supplicant -i wlan0_sta -c /etc/wpa_supplicant/wlan0_sta.conf
Successfully initialized wpa_supplicant
wlan0_sta: SME: Trying to authenticate with de:ad:be:ef:ca:fe (SSID='MYHAPPYAP' freq=2462 MHz)
wlan0_sta: Trying to associate with de:ad:be:ef:ca:fe (SSID='MYHAPPYAP' freq=2462 MHz)
wlan0_sta: Associated with de:ad:be:ef:ca:fe
wlan0_sta: WPA: Key negotiation completed with de:ad:be:ef:ca:fe [PTK=CCMP GTK=TKIP]
wlan0_sta: CTRL-EVENT-CONNECTED - Connection to de:ad:be:ef:ca:fe completed [id=0 id_str=]

In another shell, get to know more:

$ iw wlan0_sta link
Connected to de:ad:be:ef:ca:fe (on wlan0_sta)
...

The only thing left to do is to obtain an IP adress:

$ dhclient wlan0_sta

Configure the access-point (hostapd, dhcpd)

Credit: https://wireless.wiki.kernel.org/en/users/Documentation/hostapd

To make an access-point, you definitely need to install Hostapd:

$ apt-get install hostapd

A basic configuration file:

$ echo '#change wlan0_ap to your wireless device
interface=wlan0_ap
driver=nl80211
ssid=test
channel=1
' > ~/hostapd.conf

Let’s launch it:

$ hostapd ~/hostapd.conf
Configuration file: .../hostapd.conf
Using interface wlan0 with hwaddr xx:xx:xx:xx:xx:xx and ssid "test"
wlan0: interface state UNINITIALIZED->ENABLED
wlan0: AP-ENABLED 

If you see something like this, everything is alright. If you scan for wireless access-points on another machine, you can see that the test access-point exists. Now, the next step is to install and configure a DHCP server. Since we’re gonna play with it a little bit, it’s better to disable the service, and start/stop it manually.

$ apt-get install isc-dhcp-server
$ systemctl disable isc-dhcp-server

Create the configuration file for your DHCP server (this is just a quick example, change the IP adress according to your needs):

$ echo '
subnet 192.168.2.0 netmask 255.255.255.0 {
	option routers 192.168.2.1;
	range 192.168.2.10 192.168.2.20;
}
' > /etc/dhcp/wlan0_ap.conf

Set the interface up and launch the DHCP server:

$ ifconfig wlan0_ap 192.168.2.1 up
$ dhcpd -cf /etc/dhcp/wlan0_ap.conf wlan0_ap

Alright, from this point you should be able to connect to your AP from another device.

The channel issue

There is one thing that you should not underastimate: both virtual devices must operate on the same channel. The implication of that is simple. If your station is connected to a remote AP with auto-channel enabled, you will be in trouble pretty quickly. Because as soon as the remote AP will change channel, your station won’t be able to re-connect on a different channel, since you already have your own local AP up and runnning.

To handle that, you need to bring both interfaces down, then bring them up. And of course, you must bring up the station first, because it’s the one who decides which channel to use.

If you write a script that’s supposed to maintain the wifi up all the time, you will have a little bit of work to handle that properly.

The other solution, if you can, is to disable auto-channel on the remote AP, so that you KNOW for sure that your wifi operates on a given channel and never changes.

References

This was a long post, wasn’t it ? I wouldn’t have made it without help: