How to Build your Own Wireless Router (Part 2)

Renaud Cerrato
5 min readOct 22, 2018

This post is the second part of the serie. The first part covered the hardware, that part will show you how to setup your network interfaces and enable a minimal 802.11n access-point.

The Software

No surprise, Linux is the de-facto choice. Depending on the hardware you previously picked, it may be an optimized distribution like Raspbian (for Raspberry Pi) or any other Linux distro you’re comfortable with. Since I’m an Ubuntu user for years, I chose Ubuntu Server 18.04 LTS for a butter smooth experience and long term support.

The remaining parts of the serie will assume you’re running a Debian-derived Linux distribution.

Assuming the installation went right and you’re given access to a shell, let’s first identify the interfaces names:

$ ip -br a | awk '{print $1}'
lo
enp1s0
enp2s0
wlp5s0

The motherboard have 2 built-in NIC named enp1s0 and enp2s0. The wireless card is showing as wlp5s0, and support AP mode as expected:

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

We are now able to draw what we want to achieve: the first NIC will serve as the WAN port, while the remaining one will be bridged to the wireless interface:

Networking

If you’re running Ubuntu 18.04, let’s immediately get rid of netplan to get back /etc/network/interfaces support:

$ 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

We’ll pick dnsmasq as DHCP/DNS server:

$ sudo apt-get install dnsmasq

Since we’ll be running and configuring the dnsmasq process through the post-up hook, don’t forget to disable the daemon on startup:

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

Let’s write a preliminary network interfaces configuration matching the diagram, including a minimal dnsmasq setup:

$ 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
address 192.168.1.1
network 192.168.1.0
netmask 255.255.255.0
broadcast 192.168.1.255
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 \
--dhcp-range=192.168.1.10,192.168.1.150,24h
pre-down cat /var/run/dnsmasq.$IFACE.pid | xargs kill

/etc/network/interfaces documentation is available on the man page.

As you may have noticed, dnsmasq will be started as soon as the bridge is up thanks to the post-up section. Its configuration is done through command line arguments only (--conf-file=/dev/null) and the process will be shutdown when the interface go down.

The bridge_ports field is intentionally missing wlp5s0 since that interface will be added to the bridge automatically by hostapd (brctl may refuse to do this before hostapd has been started to change the interface mode).

dnsmasq documentation is available on the man page.

You can now restart your networking (sudo service networking restart) or simply reboot to check if your network configuration is properly setup.

However, please note that while you should be able to get DHCP leases from enp2s0 at this point, you won’t be able to connect wirelessly (more on this later), nor able to connect to the internet yet (see below).

Routing

At this point, we need to route packets between the LAN (enp2s0) and WAN (enp1s0) interfaces, and enable masquerading on it.

Enabling 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 latter command will ensure that the configuration will survive to the next reboot.

Enabling packet masquerading is another story, and usually requires to deal (or rather fight) with iptables. Thankfully, stone-age has long passed by, and the guys at FireHol put a lot of efforts adding the required level of abstraction to that:

$ sudo apt-get install firehol

FireHOL is a language which builds secure, stateful firewalls from easy to understand, human-readable configurations. Humans are not required to write iptables statements anymore: your configuration file will be translated to iptables statements and applied before quitting. No daemon running in the background.

Enabling masquerading for the LAN interfaces while adding minimum firewall rules using firehol is as simple as:

$ 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
masquerade
route all accept

firehol has been made for humans by humans. Take a look at the documentation.

You can test the above setup by starting firehol manually (sudo firehol start) and by connecting a laptop to the LAN port: you should now be able to browse the internet if the WAN port is plugged.

Before rebooting, do not forget to edit /etc/default/firehol to allow FireHol to start on boot:

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

I won’t go into details about the whole firehol syntax, the configuration file should be almost self-explanatory and I recommend giving a look at the documentation for a more complex setup. If you’re really curious about what firehol did to your iptables, just drop sudo firehol status on the command line.

Wireless Access Point

With no surprise, we’ll use hostapd to manage your access-point:

$ sudo apt-get install hostapd

Before going further, I’d recommend to uninstall NetworkManager since we don't need it anymore but mostly because of a known bug:

$ sudo nmcli radio wifi off && sudo rfkill unblock wlp5s0
$ sudo apt-get remove network-manager

You’ll find below a minimal and almost self-explanatory 802.11n/2.4Ghz/WPA2-AES configuration file:

$ cat /etc/hostapd/hostapd-simple.conf 
#### Interface configuration ####
interface=wlp5s0
bridge=br0
driver=nl80211
##### IEEE 802.11 related configuration #####
ssid=iCanHearYouHavingSex
hw_mode=g
channel=1
auth_algs=1
wmm_enabled=1
##### IEEE 802.11n related configuration #####
ieee80211n=1
##### WPA/IEEE 802.11i configuration #####
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=YouCantGuess

hostpad.conf documentation can be found inline at /usr/share/doc/hostapd/examples/hostapd.conf

The configuration above can be tested by hand:

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

If everything goes well, you should now be able to connect wirelessly. If you’re satisfied with the result, do not forget to edit the interface configuration to start hostapd right after the interface goes up (as seen below).

Here’s 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
address 192.168.1.1
network 192.168.1.0
netmask 255.255.255.0
broadcast 192.168.1.255
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 \
--dhcp-range=192.168.1.10,192.168.1.150,24h
pre-down cat /var/run/dnsmasq.$IFACE.pid | xargs kill
pre-down cat /var/run/hostapd.$IFACE.pid | xargs kill

Conclusion

In this part, we learned how to properly setup your network interfaces using /etc/network/interfaces, introducing dnsmasq as DHCP server. We leveraged firehol to declare your firewall and routing rules before setting-up hostapd. At this point, your router is accepting and properly routing traffic between its interfaces, delivering DHCP leases on the LAN and broadcasting some SSID on the 2.4Ghz channels.

The next part will enable 802.11ac (5Ghz).

--

--

Renaud Cerrato

Analog at birth but digital by design. Hardcore Android Developer. Linux devotee. Came back once from /dev/null.