Wireless Router DIY

Original author: Renaud Cerrato
  • Transfer
  • Tutorial

  1. Selection of components
  2. Network Interface Launch
  3. Install an 802.11ac access point (5 GHz)
  4. Configuring a virtual SSID using hostapd

For the last ten years, I have bought cheap network equipment and put DD-WRT on it in order to return “functions” at the cost of more than $ 500 removed from the Linux kernel, on which the stock firmware is based.

Despite unstable assemblies, uncorrected errors and disputes , DD-WRT is still preferable to stock firmware. But now worthy components are cheaper than ever, and the DIY community has moved to Linux without exception (I'm looking at you, Mr. Raspberry), so why not put together your own wireless router once and for all?

Selection of components

First you need to decide on the platform: x86 or ARM ? I will not discuss in detail the key differences , but in short: the former has better performance, and the latter is cheaper and more energy efficient. Raspberry Pi boards (and analogues) are extremely cheap and probably more powerful than most wireless commercial routers, but x86 platforms are widespread and have an advantage due to standardized form factors and expansion ports.

Of course, the most important detail is the chipset. Today, the de facto standards are 802.11n (2.4 GHz) and 802.11ac (5 GHz), but finding drivers for Linux is also a problem., especially with support for AP mode (access point). In short, if you do not want problems, choose Atheros chipsets . The ath9k and ath10k drivers are well supported, you can easily find them with USB and / or mini-PCIe interfaces.

At least one network interface controller (NIC) is the minimum necessary, and choose RAM and storage to your liking.

List of materials

Having sacrificed price and power consumption, I chose the x86 platform for a modular, relatively powerful configuration available for upgrade.

If you do not need ARM, then the fan is not required.

The case is spacious, with two prepared holes for the AC / DC plug. The installation of the motherboard, RAM and Pico-PSU went smoothly:

Iron porn

The installation of mini-PCIe WiFi was the most difficult because the board only supports half-size cards: here the mPCIe extender came to the rescue. I took a 20 cm FFC cable (included) to connect both sides of the adapter and secured the mini-PCIe to the chassis using double-sided tape.

Mini-PCIe Expander

Fortunately, the chassis comes with three pre-cut antenna holes. Here is the final result:


It is clear that we put Linux. Depending on the hardware, this could be an optimized distribution like Raspbian (for Raspberry Pi) or any other Linux distribution that you like. Since I have been using Ubuntu for many years, I chose Ubuntu Server 18.04 LTS , with which I am accustomed to work and who have long-term support.

The rest of this article assumes that you are using a Debian based distribution.

If the installation went well and you entered the console, we define the interface names:

$ ip -br a | awk '{print $1}'

There are two embedded NICs on the motherboard: this enp1s0and enp2s0. The wireless card is displayed as wlp5s0and supports AP mode, as expected:

$ iw list
Supported interface modes:
                 * managed
                 * AP
                 * AP/VLAN
                 * monitor
                 * mesh point

Now we can outline what we need: we put the first NIC as a WAN port, and the second is connected to the wireless interface:


If you have Ubuntu 18.04, then immediately get rid of netplan, to return to the support of / etc / network / interfaces:

$ sudo apt-get install ifupdown bridge-utils
$ sudo systemctl stop networkd-dispatcher
$ sudo systemctl disable networkd-dispatcher
$ sudo systemctl mask networkd-dispatcher
$ sudo apt-get purge nplan netplan.io

As a DHCP / DNS server, choose dnsmasq :

$ sudo apt-get install dnsmasq

Since we will start and configure the process dnsmasqvia a hook post-up, do not forget to disable the daemon at boot:

$ sudo sed -i "s/^ENABLED=1$/ENABLED=0/g" /etc/default/dnsmasq

We will write a preliminary configuration of network interfaces in accordance with the diagram, including the minimum setting dnsmasq:

$ cat /etc/network/interfaces
# Loopback
auto lo
iface lo inet loopback
# WAN interface
auto enp1s0
iface enp1s0 inet dhcp
# Bridge (LAN)
auto br0 
iface br0 inet static
    bridge_ports enp2s0
    post-up /usr/sbin/dnsmasq \
              --pid-file=/var/run/dnsmasq.$IFACE.pid \
              --dhcp-leasefile=/var/lib/misc/dnsmasq.$IFACE.leases \
              --conf-file=/dev/null \
              --interface=$IFACE --except-interface=lo \ 
              --bind-interfaces \
    pre-down cat /var/run/dnsmasq.$IFACE.pid | xargs kill

Documentation /etc/network/interfaceshere

As you can see in the section post-up, dnsmasq starts as soon as the bridge rises. Its configuration is performed only by command line arguments ( --conf-file=/dev/null), and the process will stop when the interface is disabled.

The bridge_portsinterface is not specifically specified in the field wlp5s0because it hostapdwill automatically add it to the bridge (brctl may refuse to do this before hostapd is started to change the interface mode).

See the documentation for dnsmasq.

You can now restart the network ( sudo service networking restart) or simply reboot to verify that the network configuration settings are correct.

Please note: although we can currently receive DHCP from enp2s0, but we will not haveno wireless connection (more on this later), no internet access (see below).


At this stage, you need to route packets between the LAN ( enp2s0) and WAN ( enp1s0) interfaces and enable network address translation .

Enable packet forwarding is easy:

$ sudo sysctl -w net.ipv4.ip_forward=1
$ echo"net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

The last command ensures that the configuration is maintained until the next reboot.

Network address translation is another matter; you usually have to figure out (or rather fight) with iptables. Fortunately, the stone age is long over, and the guys from FireHol put a lot of effort, adding the necessary level of abstraction:

$ sudo apt-get install firehol

FireHOL is a state-of-the-art protected firewall language, its configuration is easily understood and accessible. You no longer need to write statements iptables: the configuration file itself is translated into statements iptablesand is applied as it should. No daemon in the background.

The inclusion of network address translation for local network interfaces with the addition of minimum firewall rules is done simply:

$ cat /etc/firehol/firehol.conf 
version 6
# Accept all client traffic on WAN
interface enp1s0 wan
    client all accept
# Accept all traffic on LAN
interface br0 lan
    server all accept
    client all accept
# Route packets between LAN and WAN
router lan2wan inface br0 outface enp1s0
    route all accept

FireHOL is written by people for people, the documentation is here .

You can check the settings by manually running firehol( sudo firehol start) and connecting the laptop to the LAN port: now you can get online if the WAN port is connected.

Before rebooting, do not forget to edit /etc/default/fireholto allow the launch of FireHol at startup:

$ sudo sed -i -E "s/^START_FIREHOL=.+$/START_FIREHOL=YES/g" /etc/default/firehol

I will not go into the details of the entire syntax firehol, the configuration file explains itself, I recommend to consult the documentation in the case of more complex settings. If you are really interested in what you fireholdid with iptables, just type sudo firehol statusin the command line.

Wireless access point

Obviously, we will manage the access point using hostapd :

$ sudo apt-get install hostapd

Below you will find the minimum and almost no explanation for the 802.11 n / 2.4 Ghz / WPA2-AES configuration file:

$ cat /etc/hostapd/hostapd-simple.conf 
#### Interface configuration ####
##### IEEE 802.11 related configuration #####
##### IEEE 802.11n related configuration #####
##### WPA/IEEE 802.11i configuration #####

For documentation, hostpad.confsee c/usr/share/doc/hostapd/examples/hostapd.conf .

The described configuration can be tested manually:

$ sudo hostapd /etc/hostapd/hostapd-simple.conf

If everything goes well, a wireless connection will appear . If you are satisfied with the result, do not forget to change the configuration in order to launch hostapdas soon as the interface rises (as shown below).

Here is your final/etc/network/interfaces:

$ cat /etc/network/interfaces
# Loopback
auto lo
iface lo inet loopback
# WAN interface
auto enp1s0
iface enp1s0 inet dhcp
# Bridge (LAN)
auto br0 
iface br0 inet static
    bridge_ports enp2s0
    post-up /usr/sbin/hostapd \
              -P /var/run/hostapd.$IFACE.pid \
              -B /etc/hostapd/hostapd-simple.conf
    post-up /usr/sbin/dnsmasq \
              --pid-file=/var/run/dnsmasq.$IFACE.pid \
              --dhcp-leasefile=/var/lib/misc/dnsmasq.$IFACE.leases \
              --conf-file=/dev/null \
              --interface=$IFACE --except-interface=lo \ 
              --bind-interfaces \
    pre-down cat /var/run/dnsmasq.$IFACE.pid | xargs kill
    pre-down cat /var/run/hostapd.$IFACE.pid | xargs kill

Install an 802.11ac access point (5 GHz)

Passive scan

According to the Airetos AEX-QCA9880-NX documentation , the chipset supports 802.11ac, so that we can leave the crowded 2.4 GHz channels to the heavenly 5 GHz.

Let's see which frequencies are supported:

$ iw list
            * 2412 MHz [1] (20.0 dBm)
            * 2417 MHz [2] (20.0 dBm)
            * 2422 MHz [3] (20.0 dBm)
            * 2427 MHz [4] (20.0 dBm)
            * 2432 MHz [5] (20.0 dBm)
            * 2437 MHz [6] (20.0 dBm)
            * 2442 MHz [7] (20.0 dBm)
            * 2447 MHz [8] (20.0 dBm)
            * 2452 MHz [9] (20.0 dBm)
            * 2457 MHz [10] (20.0 dBm)
            * 2462 MHz [11] (20.0 dBm)
            * 2467 MHz [12] (disabled)
            * 2472 MHz [13] (disabled)
            * 2484 MHz [14] (disabled)
            * 5180 MHz [36] (17.0 dBm) (no IR)
            * 5200 MHz [40] (17.0 dBm) (no IR)
            * 5220 MHz [44] (17.0 dBm) (no IR)
            * 5240 MHz [48] (17.0 dBm) (no IR)
            * 5260 MHz [52] (23.0 dBm) (no IR, radar detection)
            * 5280 MHz [56] (23.0 dBm) (no IR, radar detection)
            * 5300 MHz [60] (23.0 dBm) (no IR, radar detection)
            * 5320 MHz [64] (23.0 dBm) (no IR, radar detection)
            * 5500 MHz [100] (23.0 dBm) (no IR, radar detection)
            * 5520 MHz [104] (23.0 dBm) (no IR, radar detection)
            * 5540 MHz [108] (23.0 dBm) (no IR, radar detection)
            * 5560 MHz [112] (23.0 dBm) (no IR, radar detection)
            * 5580 MHz [116] (23.0 dBm) (no IR, radar detection)
            * 5600 MHz [120] (23.0 dBm) (no IR, radar detection)
            * 5620 MHz [124] (23.0 dBm) (no IR, radar detection)
            * 5640 MHz [128] (23.0 dBm) (no IR, radar detection)
            * 5660 MHz [132] (23.0 dBm) (no IR, radar detection)
            * 5680 MHz [136] (23.0 dBm) (no IR, radar detection)
            * 5700 MHz [140] (23.0 dBm) (no IR, radar detection)
            * 5720 MHz [144] (23.0 dBm) (no IR, radar detection)
            * 5745 MHz [149] (30.0 dBm) (no IR)
            * 5765 MHz [153] (30.0 dBm) (no IR)
            * 5785 MHz [157] (30.0 dBm) (no IR)
            * 5805 MHz [161] (30.0 dBm) (no IR)
            * 5825 MHz [165] (30.0 dBm) (no IR)

In the above list, we see that the chipset supports channels 1-14 (2.4 GHz) and channels 36-165 (5 GHz), but did you notice the flag no IR?

The flag no IRmeans no-initiating-radiation (i.e. passive scanning ). This means that this mode is prohibited in the case when the device first initiates radiation (including beacons ). In other words, you cannot launch an access point on these channels !

Regulatory requirements

The above situation is explained by the Linux regulatory requirements that govern the use of the radio frequency spectrum depending on the country .

But wait!

I live in the United States, and the link says that I have the right to initiate radiation on channels 36-48, so what's the matter? Let's see which regulation domain is currently used:

$ iw reg get
country 00: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 20), (N/A)
        (2457 - 2482 @ 40), (N/A, 20), (N/A), NO-IR
        (2474 - 2494 @ 20), (N/A, 20), (N/A), NO-OFDM, NO-IR
        (5170 - 5250 @ 80), (N/A, 20), (N/A), NO-IR
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, NO-IR
        (5490 - 5730 @ 160), (N/A, 20), (0 ms), DFS, NO-IR
        (5735 - 5835 @ 80), (N/A, 20), (N/A), NO-IR
        (57240 - 63720 @ 2160), (N/A, 0), (N/A)

The output shows that the world domain is now active (or not established), that is, the minimum values ​​allowed in each country .

Unfortunately, manually installing the domain sudo iw reg setwill fail, because the domain is sewn into the EEPROM:

$ dmesg | grep EEPROM
[   12.123068] ath: EEPROM regdomain: 0x6c


Fortunately, regulatory requirements are handled at the driver level, so they can be easily changed: we find the patch in the Open-WRT source code .

First of all, do not forget to connect the source code repository from /etc/apt/sources.list:

$ cat /etc/apt/sources.list
deb-src http://us.archive.ubuntu.com/ubuntu/ bionic main restricted 

Then prepare the environment by installing the necessary dependencies:

$ sudo apt-get install build-essential fakeroot
$ sudo apt-get build-dep linux

Download your kernel sources:

$ apt-get source linux

Since the original Open-WRT patch cannot be applied “as is” to the Ubuntu core tree due to subtle differences in the build system, I had to fix it:

$ VERSION=$(uname -r)
$ cd linux-${VERSION%%-*}
$ wget -O - https://gist.github.com/renaudcerrato/02de8b2e8dc013bc71326defd2ef062c/raw/a2db325e520e6442c8c12f7599d64ac1b7596a3e/402-ath_regd_optional.patch | patch -p1 -b

Everything is ready for assembly:

$ fakeroot debian/rules clean
$ fakeroot debian/rules binary-generic

If there are no problems, then now you can install the fixed kernel over the previous one:

$ cd ..
$ sudo dpkg -i linux*.deb

Reboot, and voila:

$ sudo iw reg set US
$ iw list
          * 5180 MHz [36] (17.0 dBm)
          * 5200 MHz [40] (17.0 dBm)
          * 5220 MHz [44] (17.0 dBm)
          * 5240 MHz [48] (17.0 dBm)
          * 5260 MHz [52] (23.0 dBm) (radar detection)
          * 5280 MHz [56] (23.0 dBm) (radar detection)
          * 5300 MHz [60] (23.0 dBm) (radar detection)
          * 5320 MHz [64] (23.0 dBm) (radar detection)
          * 5500 MHz [100] (23.0 dBm) (radar detection)
          * 5520 MHz [104] (23.0 dBm) (radar detection)
          * 5540 MHz [108] (23.0 dBm) (radar detection)
          * 5560 MHz [112] (23.0 dBm) (radar detection)
          * 5580 MHz [116] (23.0 dBm) (radar detection)
          * 5600 MHz [120] (23.0 dBm) (radar detection)
          * 5620 MHz [124] (23.0 dBm) (radar detection)
          * 5640 MHz [128] (23.0 dBm) (radar detection)
          * 5660 MHz [132] (23.0 dBm) (radar detection)
          * 5680 MHz [136] (23.0 dBm) (radar detection)
          * 5700 MHz [140] (23.0 dBm) (radar detection)
          * 5720 MHz [144] (23.0 dBm) (radar detection)
          * 5745 MHz [149] (30.0 dBm)
          * 5765 MHz [153] (30.0 dBm)
          * 5785 MHz [157] (30.0 dBm)
          * 5805 MHz [161] (30.0 dBm)
          * 5825 MHz [165] (30.0 dBm)

In order to avoid automatic updating, you may need to fix the version of the Linux kernel .


The new configuration file hostapdwill be quite simple: hw_mode=aincludes 5 GHz bands, and ieee80211ac=1includes 802.11ac (VHT). Option ieee80211d=1indicating country_code=USdefines the regulatory domain, under which we operate.

To maximize the use of bandwidth, ht_capaband vht_capabshould reflect the capabilities of the equipment:

$ iw list
    Band 1:
      Capabilities: 0x19e3
              RX LDPC
              Static SM Power Save
              RX HT20 SGI
              RX HT40 SGI
              TX STBC
              RX STBC 1-stream
              Max AMSDU length: 7935 bytes
              DSSS/CCK HT40
    Band 2:
      VHT Capabilities (0x338001b2):
              Max MPDU length: 11454
              Supported Channel Width: neither 160 nor 80+80
              RX LDPC
              short GI (80 MHz)
              TX STBC
              RX antenna pattern consistency
              TX antenna pattern consistency

Given this, here is the final onehostapd.conf :

$ cat /etc/hostapd/hostapd.conf
#### Interface configuration ####
##### IEEE 802.11 related configuration #####
##### IEEE 802.11n related configuration #####
##### IEEE 802.11ac related configuration #####
##### WPA/IEEE 802.11i configuration #####

For documentation, hostpad.confsee c/usr/share/doc/hostapd/examples/hostapd.conf .

At this stage, the wireless router is fully operational, and if you need a more complex setup, you can now dive into the configuration files.

Configuring a virtual SSID using hostapd

Regardless of whether you want to configure a guest access point or a dedicated wireless network for your VPN, at some point you will have to configure a virtual SSID.


Based on the current configuration , here is an updated diagram of what we want to get. Assuming that it wlp5s0is a physical wireless interface, the virtual SSID will work on the virtual interface wlan0using its own subnet


First, verify that your wireless device supports multiple SSIDs:

$ iw list
    valid interface combinations:
    * #{ AP, mesh point } <= 8,
    total <= 8, #channels <= 1, STA/AP BI must match

As you can see, the chipset supports up to eight access points on one channel. This means that you can configure up to seven virtual SSIDs, and they will all work on the same channel.

Network interface

According to the documentation in hostapd.conf, there is a strict connection between the MAC address of the physical interface and the BSSID of the virtual interfaces:

hostapd will generate a BSSID mask based on the BSSIDs that are configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is not the case, the MAC address of the radio must be changed before starting hostapd. If a BSSID is configured for every secondary BSS, this limitation is not applied at hostapd and other masks may be used if the driver supports them (e.g., swap the locally administered bit)

BSSIDs are assigned in order to each BSS, unless an explicit BSSID is specified using the 'bssid' parameter.

If an explicit BSSID is specified, it must be chosen such that it:
— results in a valid MASK that covers it and the dev_addr
— is not the same as the MAC address of the radio
— is not the same as any other explicitly specified BSSID

To meet these requirements and allow the hostapdvirtual interface (s) to automatically assign the BSSID, update the MAC address of the physical wireless interface to zero the four least significant bits. That's enough for 15 virtual BSSIDs - much more than necessary.

First we define the current MAC address:

$ ip addr show wlp5s0 | grep link | awk '{print $2}'

If you clear the last four bits and set the U / L bit, you’ll end up with a MAC address 46:c3:06:00:03:e0.

Now we will update the configuration in order to set the correct MAC address right before the interface is loaded, and also announce the virtual wireless interface in accordance with our diagram:

$ cat /etc/network/interfaces
# Physical Wireless
auto wlp5s0
iface wlp5s0 inet manual
    pre-up ip link set dev wlp5s0 address 46:c3:06:00:03:e0
# Virtual Wireless
allow-hotplug wlan0
iface wlan0 inet static
    post-up /usr/sbin/dnsmasq \
		--pid-file=/var/run/dnsmasq-wlan0.pid \
		--conf-file=/dev/null \
		--interface=wlan0 --except-interface=lo \
		--bind-interfaces \
    post-down cat /var/run/dnsmasq-wlan0.pid | xargs kill

Fine. I use dnsmasqas a DHCP server - feel free to replace with something you like. Please note that virtual interface is required for correct operation allow-hotplug.

Access point configuration

Now the simplest thing is to add a virtual SSID to the current configuration hostapd. Just add it to the end of the existing file hostapd.conf:

$ cat /etc/hostapd/hostapd.conf
### Virtual SSID(s) ###

In the example above, I used WPA2 encryption, but most of the radio interface options are available (for example, channel). You can add more virtual SSIDs by simply adding lines in the configuration file, according to the declared and properly configured virtual interfaces.

Now we reboot and we see our new SSID along with the new wireless interface (note the MAC address):

$ ip addr show wlan0 | grep link | awk '{print $2}'

That's it guys!

Also popular now: