Search
Building your own Router with a Raspberry Pi

Building your own Router with a Raspberry Pi

designing and building a raspberry pi router

Running a custom router gives unprecedented insight into everything happening in a network. Building your own router with a a Raspberry Pi may be a little daunting, but it’s surprisingly easy and rewarding to do… and the benefits are tremendous.

Many times, I’ve been able to diagnose problems with the internet more effectively thanks to the Rapsberry Pi router. No matter what the problem is — from slow internet to devices that cannot connect — a custom router will offer more insight into what’s going on (via logs), and more support (via the community).

This project will introduce key networking concepts and give you the tools to debug problems with a home network quickly. It’s a low-intermediate project with a Raspberry Pi, and assumes some basic knowledge of Linux. It was tested with Raspbian Buster Lite, but should work with all modern Debian distributions. It enables this related project (which more experienced readers may wish to skip to):

Related: Raspberry Pi Network Monitor (Open-Source)

When trying to diagnose why the internet is slow, it can be quite challenging to figure out exactly which device on the network is eating up all the bandwidth. Many solutions to this problem require software to be installed on every device to be monitored. Instead, I endeavored to build a Raspberry Pi network monitor.

Read More »

Building your own Router

A Raspberry Pi 4 is a quite capable router in the right circumstances.

But first, let’s be clear on terms.

A switch shuffles data around the network. A router helps direct that traffic.

Building a “router,” in this context, means that we will be implementing DHCP, DNS, and a Firewall.

If you don’t know what any of that means, don’t worry. For now, let’s just assume that you have a Raspberry Pi 4 (see parts list at the bottom). The “version 4” bit is important because it has a significantly better network card (eth0) than prior models (Gigabit). This points at the biggest limitation of using the RPi as a router: the bandwidth is capped at 1000 Mbps.

Bandwidth and throughput are often confused.

The band “width” is the maximum amount of data which may flow through the system (in this case, from the home to the internet). Throughput measures how much bandwidth is used at any moment.

Incidentally, this guide should work for any Debian-based linux system that you want to turn in to a router. You could use better hardware to create an even more effective solution. I chose a Pi4 because our internet speed is only 30Mbps anyway (DSL). As I described in the network monitoring post, this Raspberry Pi 4 is proven to be way more than enough for this case.

Have a DSL/PPP connection?

Read about the specific quirks of using a Raspberry Pi as a DSL router.

A second ethernet port is required (see the next section).

I used this USB 3.0 Gigabit connector:

Connecting to the Internet

I will refer to the network interfaces as wan and lan.

If you want things to be copy-and-paste, you can rename your network interfaces to match. For example, lan is the name for the fastest of the two interfaces (the onboard network interface, eth0, in my case). To create the static name, first type ifconfig get the MAC address for the interface (after ether). If you found ether aa:bb:cc:dd:ee:ff, then create or edit /etc/udev/rules.d/10-network.rules to include:

SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="aa:bb:cc:dd:ee:ff", NAME="lan"

… and then repeat the process for the wan interface.

The internal network (LAN) needs to deal with more throughput than the WAN.

This is why the faster interface is chosen for the LAN.

At this point, you can directly connect the wan interface to the modem provided by your ISP. You may want to put the modem into transparent bridge mode, if possible, since the Pi is taking over the routing functionality (more on this).

DHCP Server

With the Pi online, it’s time to let devices connect to the Pi.

In this example, the Raspberry Pi router will have an IP address of 192.168.0.1. This means first creating or editing /etc/network/interfaces.d/lan:

allow-hotplug lan
iface lan inet static
        address 192.168.0.1
        netmask 255.255.255.0
        gateway 192.168.0.1

Now the LAN interface will have a static IP and treat itself as the router. Now it’s time to make it capable of assigning IP addresses to other devices on the network. Running a DHCP server will therefore make it so many computers can connect to the Pi.

First, disable dhcpcd and install isc-dhcp-server:

sudo systemctl disable dhcpcd
sudo apt install isc-dhcp-server

Then edit /etc/default/isc-dhcp-server, replacing INTERFACESv4:

INTERFACESv4="lan"

We’ve told the DHCP server what interface contains the LAN. Now it needs to know the parameters for the home network. To continue with the example, we will let the server assign IP addresses in the range from 192.168.0.100 to 192.168.0.199 (more on why in a moment). Edit /etc/dhcp/dhcpd.conf to first change the domain name and domain name servers:

option domain-name "router.local";
option domain-name-servers 8.8.8.8, 1.1.1.1;

The .local suffix is convention for a LAN, but you’re welcome to replace the router name. The domain name servers will be changed soon, but are pointed at google for now.

Also add add the following:

authoritative;
subnet 192.168.0.0 netmask 255.255.255.0 {
        range 192.168.0.100 192.168.0.199;
        option routers 192.168.0.1;
        option subnet-mask 255.255.255.0;
}

This tells the DHCP server that it may assign IP addresses to the devices which connect to it within the given range. The reason for the relatively small range is that we can assign static IP addresses outside that range. For example, you could take the same MAC address for the lan above and add the following section:

host router {
        hardware ethernet aa:bb:cc:dd:ee:ff;
        fixed-address 192.168.0.1;
}

Adding the static IP for the router, as well, means that the DHCP server can never get confused and accidentally assign a different IP address. In general, encoding the IP addresses in the DHCP server also centralizes the authority (rather than coding static IPs on specific devices, which is far more brittle IME).

If you restart the Raspberry Pi now and connect a computer to the lan port, you should find an IP address in the appropriate range. Using Mac OSX, I opened the Network Preferences pane, and then the Hardware tab to find the MAC address of my laptop’s USB-C to Ethernet adapter. Then I assigned a static IP (just like above) to that address and restarted the DHCP server:

sudo systemctl restart isc-dhcp-server
connect to raspberry pi router
The MAC address came from the Hardware tab. Pressing the “Renew DHCP Lease” button will ask the DHCP server for a new IP address, effectively refreshing the connection.

Even though the fixed-address is outside the range above, this sort of static IP assignment will work with any device. I like to give static IPs to all my known, trusted devices that are outside the public range. This does the following:

If for some reason a computer can ever not connect to the home network, running your own DHCP server makes things easier to debug. Running journalctl -xe | grep dhcpd shows any attempts by devices to connect to the home network:

May 03 15:50:44 router dhcpd[2648]: DHCPREQUEST for 192.168.0.106 from xx:xx:xx:xx:xx via eth0
May 03 15:50:44 router dhcpd[2648]: DHCPACK on 192.168.0.106 to xx:xx:xx:xx:xx via eth0

And taking a peek at /var/lib/dhcp/dhcpd.leases reveals what devices have connected lately.

Connecting WiFi Devices

For this, you will need a WiFi router or access point.

The typical approach is to use the on-board WiFi. This mostly matches the above steps, except using the wlan0 device on the Raspberry Pi for the lan interface and setting up hostapd. However, that is not recommended in the context of this post. Letting the Raspberry Pi serve as a pass-through between two ethernet interfaces is a simpler and faster approach.

Instead, simply connect a WiFi router to the lan port on the Raspberry Pi and then put the router into Access Point (a.k.a. bridge) mode. Now, the router won’t care if clients connect via ethernet or WiFi.

I’m a big fan of the Orbi routers — not only are they extremely fast, but you can plug the base station’s “internet” port into the Raspberry Pi and have a dozen wired connections at your disposal around the house:

Forwarding Traffic

At this point…

  • The Raspberry Pi can talk to the internet (WAN).
  • The devices on the LAN can talk to each other.

… but there is no communication between the two.

Enter firewalld:

sudo apt install firewalld

Firewalld is the easiest solution I’ve found so far.

IP tables and firewalls can be a pain.

At a minimum, you will need a home and public zone, and then to masquerade the traffic between the two. This just tells the firewall that the lan interface is for the home, and the wan/ppp0 interfaces are for the public.

sudo firewall-cmd --zone=home --add-interface=lan
sudo firewall-cmd --zone=public --add-interface=ppp0
sudo firewall-cmd --zone=public --add-interface=wan
sudo firewall-cmd --zone=public --add-masquerade
sudo firewall-cmd --zone=home --add-service=dns
sudo firewall-cmd --zone=home --add-service=dhcp

At this point, computers connected to LAN should be able to access the internet. You could just run sudo firewall-cmd --runtime-to-permanent now to save it all, but there may be a few more useful steps (below). If at any time internet (or other) traffic is not working from within the network, run journalctl -xe and look for lines like this:

FINAL_REJECT: IN=ppp0 OUT= MAC= SRC=77.247.109.40 DST=...

In this case, the rule is working as expected. Someone from an unknown IP address attempted to contact my home network through the ppp0 interface (i.e., from the internet). In other cases, you may need to open up the firewall to allow certain traffic.

Firewalld is easiest when using zone-based rules. You will need at least two zones, which I have called home and public instead of lan and wan (to match firewalld’s conventions). I also have a third (optional) trusted zone in my configuration for the kubernetes/docker subnet. So when I type firewall-cmd --get-active-zones, I see:

home
  interfaces: lan lo
public
  interfaces: wan ppp0
trusted
  interfaces: docker0 cni0
  sources: 10.244.0.0/24 10.202.0.0/24 10.96.0.0/24

The definitions for zones are contained in xml files located at /etc/firewalld/zones/:

<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <interface name="wan"/>
  <interface name="ppp0"/>
  <service name="ssh"/>
  <service name="http"/>
  <service name="https"/>
  <masquerade/>
</zone>
<?xml version="1.0" encoding="utf-8"?>
<zone target="ACCEPT">
  <short>Home</short>
  <description>For use in home areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <interface name="lan"/>
  <interface name="lo"/>
  <service name="ssh"/>
  <service name="mdns"/>
  <service name="samba-client"/>
  <service name="dhcpv6-client"/>
  <service name="dhcp"/>
  <service name="dns"/>
  <service name="http"/>
  <service name="https"/>
  <port port="5000" protocol="tcp"/>
  <port port="3000" protocol="tcp"/>
  <port port="9999" protocol="tcp"/>
  <port port="10250" protocol="tcp"/>
  <port port="10250" protocol="udp"/>
</zone>
<?xml version="1.0" encoding="utf-8"?>
<zone target="ACCEPT">
  <short>Trusted</short>
  <description>All network connections are accepted.</description>
  <interface name="docker0"/>
  <interface name="cni0"/>
  <source address="10.244.0.0/24"/>
  <source address="10.202.0.0/24"/>
  <source address="10.96.0.0/24"/>
  <service name="dns"/>
  <service name="dhcp"/>
  <service name="dhcpv6-client"/>
  <service name="https"/>
  <service name="http"/>
</zone>

Traffic is categorized into zones based upon the interface or source, such as:

  • sudo firewall-cmd --zone=public --add-interface=ppp0
  • sudo firewall-cmd --zone=trusted --add-source=10.202.0.0/24

And a zone can receive traffic to a service or a port:

  • sudo firewall-cmd --zone=public --add-service=http
  • sudo firewall-cmd --zone=home --add-port=10250/tcp

Note that these changes are temporary. This allows for safe testing. To make them permanent, run sudo firewall-cmd --runtime-to-permanent. If there’s not a If you need more help with firewalld, check out this article.

DNS Server & Rewrites

A DNS server can make this little router even better.

The job of the DNS server is to respond with an IP address for a given domain name. For example, it knows what IP address your computer should connect to in order to reach google.com. Many people like to run their own for security and privacy reasons.

There are ad-blockers out there, for example, that work as DNS servers. By installing Pi Hole or AdGuard Home, you can prevent computers on the network from being able to locate advertising/spam/malware domains. Both of these two tools also support DNS rewrites, so that you can change the “location” of any site.

Once you’ve installed one of the two, just edit /etc/dhcp/dhcpd.conf: option domain-name-servers 192.168.0.1;

Now, all devices that connect to the network will be told to use this DNS server.

Why are rewrites so useful? Check out the following post on building a home server. Rewrites let the same URL be used internally and externally, enabling HTTPS anywhere. For example, by using AdGuard Home to rewrite *.snowy-cabin.com to 192.168.0.100, any traffic that originates within the home network to house.snowy-cabin.com will never leave the home network. This means that the same https:// URL can be used to reach the reverse-proxy, where SSL termination happens, ensuring secure communication for both public and private traffic.

Part List & Cheat-sheet

To recap, here are some key concepts for home servers:

  • Static IPs: edit /etc/dhcp/dhcpd.conf to add a new host, then restart isc-dhcp-server and renew the lease on the client.
  • Port forwarding: run sudo firewall-cmd --zone=public --add-forward-port=port=443:proto=tcp:toaddr=192.168.0.100
  • Custom domain names: use the DNS server to make your unique domain name work internally and externally.
  • Monitoring network traffic: instructions.

… and the parts I used:

Vanlife Checklist

Our packing list, including the things we cannot live without, and everything I wish I'd known before I bought a van. The best ways to find campsites, pitfalls when building a van, and more. Download the PDF with everything we learned on the road. You'll also get the Step-by-Step Build Guide.

... but this site has no paywalls. If you do choose to sign up for this mailing list I promise I'll keep the content worth your time.

Written by
(zane) / Technically Wizardry
Join the discussion

Around the Web