Recently I replaced my home router with Raspberry Pi 4. My main goal was to increase throughput through my VPN. While at it, I also migrated from OpenVPN to WireGuard, and read their whole technical paper. This post sums up my insights with repurposing a Raspberry Pi into a network device.
Why having a home router in the first place?
When you sign a contract with your internet service provider, besides the Internet service, you usually lease a router too. It is certified for the ISP network and gives you wired and wireless connectivity for all devices in your home. In most cases, you can’t get rid of this router, and most people don’t bother. I think differently, and I believe it’s important to talk about its security risks.
Routers provided by your ISP have several drawbacks like:
- Their hardware is often very low-end, with insufficient RAM and flash storage
- As a consequence, their software is limited in functionality
- Sometimes, such routers have hardware modifications from variants made for the consumer market, and frequently they have custom firmware explicitly made for your ISP
- You, as a customer, have limited access to these devices. On the other hand, ISP often has remote access and full control over the equipment
- In the past, security experts found several backdoors allowing remote access to anyone who is motivated enough
- Such routers usually don’t get firmware updates, or they get it very rarely and only for a limited time
- As a consequence, if security experts find bugs in software libraries used in such routers, these bugs are never fixed
- You use such routers daily for years. Usually, until your ISP gives you an updated model, which is rare
For me, having a custom router connected behind the ISP router is a no-brainer. I like having things under control, and I also use non-standard services like VPN, which spans through my home and flat so that I can access my stuff everywhere I go.
From Mikrotik to Raspberry
I tried different brands of routers over the years, but one brand stuck with me for a long time — Mikrotik. A friend of mine recommended one to me, and since then, I passed that advice to others, having my own experience now.
In my home, I used the RB450G model, which has a MIPS-based 680 MHz single-core CPU with 256 MB of RAM, and 512 MB of flash storage. It was already a considerable upgrade from having a router from my ISP. At that time, a typical ISP router, or cheap home router, had about 32 MB of RAM and 4 MB of storage. You almost couldn’t fit any decent software there; there was just no space.
We were using ADSL for connectivity. To hook up this new router into my network, I connected it to the LAN port of the ADSL modem as the only client. Also, I turned off DHCP, set up static IP address, and turned off all “smart” functions, effectively making the modem just a bridge passing data from my router to the ISP, forth and back.
Each ISP router is different, but you should be able to set it up similarly. Also, try to look up for the DMZ option and set it to your home router IP address. That way, all data traffic is forwarded to your home router, and no special rules are applied. One last thing, if your ISP router has a WiFi, turn it off. You need to set up a WiFi on your home router, or a device behind it. Otherwise, all wireless traffic bypasses your home router, and that’s probably not what you want.
Mikrotik devices use RouterOS as their system. It gives users features found in much pricier network products. I especially love their WinBox app, which helps to manage my equipment through a simple-to-use UI. However, as I used it, I found several bugs, and some features I needed were missing. For example, RouterOS supports OpenVPN only through TCP, which adds overhead. Also, when my provider configured a DS-Lite IPv6 stack, RouterOS support was limited, and I had trouble accessing a native IPv6 network. Therefore, I decided to migrate to OpenWrt, which is an open-source Linux distribution aimed at routers. Due to this, many features were just there, including OpenVPN on UDP, and proper DSLite support.
OpenWrt had many software packages available out of the box. If I missed a feature, I could (and did) prepare a package, and if I found a bug, I could (and did) report an issue and provide a patch. One day, however, Linux kernel dropped support for my flash storage. Suddenly, the latest OpenWrt versions didn’t work correctly with my router, and I was stuck with an old version of the system. It was a problem from a security standpoint, as well as from a usability standpoint; I couldn’t use the latest software anymore. Because this was my main router, I couldn’t experiment with it and prepare patches. Testing my changes was not possible. I kept running this old OpenWrt distribution on my router for over a year. During that time, I was looking for an alternative solution, which is powerful enough to increase my VPN performance, and which can run customizable firmware.
Finding alternative hardware
In my other site, I use Turris Omnia, which uses a forked OpenWrt code. I thought about replacing my Omnia with a network developer board like MACCHIATObin Double Shot and replacing my Mikrotik with Omnia. Still, ultimately I abandoned the idea due to the higher price and lower availability of the MACCHIATObin board. From the practical point, I don’t need hardware capable of routing 10 Gbit/second if my ISP gives me only one Gigabit, but I must admit it would be cool. Later I shifted my focus to a hardware running pfSense or OPNSense. These distributions have minimal support for ARM-based boards, and finding x86 hardware with low power consumption, reasonable price, and decent performance is not that easy.
Then I remembered on my Odroid C2. It’s a developer board with a single Ethernet port and a bunch of USB2 ports. What’s important is that it can handle Ethernet through its RJ45 port at full speed of one Gigabit. If you’d like to use such a device as a router, you need to have at least two interfaces — one facing your ISP router (WAN) and one facing your devices (LAN). I was thinking of using this internal Ethernet port for LAN because the maximum speed on the local network is essential. However, for WAN, I needed much lower throughput because my Internet speed was only around 100 Mbit/second. I found some decent USB3 to Ethernet adapters for that purpose. Odroid C2 supports several Linux distributions, including Ubuntu and ArchLinux ARM. Therefore, transforming it into a router is possible, and I would certainly do that if there weren’t a new revision of Raspberry Pi. The main problem with Odroid C2 is that its kernel is not mainline, and it’s rather old. Also, ArchLinux ARM, which I planned to use, is not explicitly designed for routers. This solution could work, but it would most likely take a significant time to set up.
Last weekend I was determined to go with the Odroid C2 path. But then I looked at my shiny Raspberry Pi 4, and I changed my mind. I own almost all revisions of Raspberry Pi, and I use them in my work IoT projects. That was also the reason why I bought the Raspberry Pi 4. Previous generations of RPi were not that useful for use as a router because their Ethernet interface was internally routed through a USB2 hub, effectively limiting the throughput to around 200 Mbit/second. However, RPi4 can finally saturate a full Gigabit link because of its new I/O architecture. Also, it finally provides USB3 ports, which work great with my external USB-to-Ethernet adapter. The main advantage of RPi4 over Odroid C2 is the mainline kernel support. Therefore it’s easy to use close-to-latest Linux kernel. Due to its popularity, community support is also better. And, as a bonus, OpenWrt added support for it in their developer builds. Therefore, migration from my existing router to Raspberry Pi should be relatively painless. Let’s take a look at this process.
Turning Raspberry Pi into a router
As I mentioned, I needed to have two Ethernet interfaces, and Raspberry gives me only one. I decided to use the internal Ethernet port for my local LAN and a USB-to-Ethernet adapter for WAN. Of course, this assignment is not mandatory, and if you use a USB adapter for LAN instead, your setup should work too. In my case, the LAN port on RPi is connected to a 24-port Gigabit Ethernet switch to provide connectivity for more than one device. You can buy any cheap Gigabit Ethernet switch.
Without going into much detail about how an Ethernet works, a router serves two purposes — it resolves an IP address into device MAC using the ARP protocol, and it routes traffic into outside networks, like the Internet. When two of your devices want to communicate inside the same LAN network, their traffic won’t get through your router at all, only through the switch. Hence, if you buy a cheap Gigabit switch, you’ll get a decent network performance, and it doesn’t matter what kind of router you use.
Therefore, if you have some spare Raspberry Pi 3, it can still serve you well as a router, just hook up some switch behind it, and make sure your Internet link is slower than 200 Mbit/second. Otherwise, your speed is limited. Raspberry Pi 3 won’t handle more traffic because of the internal USB2 hub mentioned earlier. RPi4 is a better choice in that case. I don’t have exact stats, but USB 3.0 throughput is 5 Gbit/s, so I believe the latest RPi should have no problem saturating an outgoing link.
The installation of the system was pretty straight-forward. I followed the official instructions. Note that if you have Raspberry Pi 4, its support is not yet in stable OpenWrt builds, and you need to download the latest development build. The image is gzip-compressed; first you need to unpack the file. I explain the required steps using the Linux command line:
As a result, you’ll get a file without the
*.gz extension. You can now flash this file into a MicroSDXC card. I’m using the
1dd if=rpi-4-ext4-factory.img of=/dev/mmcblk0 bs=40962sync
You specify source file as the
if parameter, and target memory card as the
of parameter. Block size,
bs, is optional to speed things up, and
sync command makes sure everything is written into the card.
Unlike distributions like Raspbian, OpenWrt doesn’t expand your system partition to fit the whole space. Typically, you’ll get only 100 MB of space for your apps, even if your memory card has 64 GB of storage. I resized the partition manually on my PC. First, I edited the partition table with
fdisk, and then used the
resize2fs command. The animation below illustrates all these steps:
First, you need to change the partition table so that the second,
rootfs, partition fits all available space. You need to delete the partition first and then create a new one. The key is to make sure the new larger partition starts at the same offset. This operation might sound dangerous, but it’s not. Of course, you need to make sure that you’ve picked the correct device. When you change the partition table, then remove and insert the memory card back so that Linux uses the updated partition table. Next, check the file system for errors, and finally, initiate the
The first run
Raspberry Pi has assigned the IP address of
192.168.1.1. To get access to it, connect your PC into the Ethernet port on RPi, and set its IP address to
192.168.1.2. Then you should be able to access OpenWrt through SSH:
234BusyBox v1.31.1 () built-in shell (ash)56 _______ ________ __7 | |.-----.-----.-----.| | | |.----.| |_8 | - || _ | -__| || | | || _|| _|9 |_______|| __|_____|__|__||________||__| |____|10 |__| W I R E L E S S F R E E D O M11 -----------------------------------------------------12 OpenWrt SNAPSHOT, r12945-0aa2ecf5b213 -----------------------------------------------------14[email protected]:~#
As a next step, it’s a good practice to set up a password:
To proceed further, we need to have Internet connectivity inside OpenWrt. In my case, I changed the Raspberry Pi IP address inside the
/etc/config/network file. If you statically assign an IP from a range matching your LAN, you can hook up the Raspberry as a regular client behind your existing router. It makes further steps easier.
To edit the
network file, you can use the pre-installed
vi text editor. If you never used
vim, here’s a brief overview. When you open a file, you’re in a command mode. Use your arrow keys to move the cursor to the point you want to change. Then, press “i” to switch to the editing mode, in which you can write a text and delete an existing text using the
Delete key. After you make your changes, press
Escape to get back into the command mode, and store your file by typing
:wq <Enter> (i.e., command: write and quit).
Start editing the
1[email protected]:~# vim /etc/config/network
wan section to match your current LAN config, i.e., assign to RPi a static IP, which is not used in your network, set a proper default gateway address pointing to your current router, and set a DNS. Here’s an example from my configuration:
1config interface 'wan'2 option ifname 'eth1'3 option proto 'static'4 option ipaddr '192.168.0.2'5 option netmask '255.255.255.0'6 option gateway '192.168.0.1'7 option ipv6 'auto'8 option peerdns '0'9 option dns '22.214.171.124 126.96.36.199'
After you write your changes, apply your new configuration by rebooting your RPi or typing the following command:
1[email protected]:~# /etc/init.d/network restart2'radio0' is disabled
Now your RPi should be connected to the Internet, which you can confirm with the
ping command. First, ping a well-known public IP address:
1[email protected]:~# ping 188.8.131.52PING 184.108.40.206 (220.127.116.11): 56 data bytes364 bytes from 18.104.22.168: seq=0 ttl=118 time=35.707 ms464 bytes from 22.214.171.124: seq=1 ttl=118 time=35.544 ms5^C6--- 126.96.36.199 ping statistics ---72 packets transmitted, 2 packets received, 0% packet loss
If this command worked, your RPi has a connection. Let’s also verify your DNS name resolution works, and that you’re able to connect to services using their domain names:
1[email protected]:~# ping www.google.com2PING www.google.com (188.8.131.52): 56 data bytes364 bytes from 184.108.40.206: seq=0 ttl=118 time=33.452 ms464 bytes from 220.127.116.11: seq=1 ttl=118 time=33.279 ms5^C6--- www.google.com ping statistics ---72 packets transmitted, 2 packets received, 0% packet loss
When you have a working Internet connection, feel free to install any text editor, which suits you better than
vi, or whatever package you need. I recommend
nano. First, update your package list:
1opkg update2Downloading http://downloads.openwrt.org/snapshots/targets/bcm27xx/bcm2711/packages/Packages.gz3...
And then install
1[email protected]:~# opkg install nano2Installing nano (5.2-1) to root...3...
From now on, you can edit your files like so:
At this point, you should be ready to proceed with the next steps. If you get stuck somewhere, shoot me a comment below this post.
Enabling USB-to-Ethernet adapter
If you check your available interfaces, you’ll most likely see only the internal
eth0 interface, even if your USB adapter is connected. In my case, I had to find out which chipset my adapter is using and install the appropriate kernel module.
1[email protected]:~# opkg list kmod-usb-net*23# Install the Realtek module4[email protected]:~# opkg install kmod-usb-net-rtl81525[email protected]:~# reboot
After next reboot, you should see your USB Ethernet interface initialized:
1...2[ 7.159985] usbcore: registered new interface driver r81523[ 7.370114] r8152 2-2:1.0 eth1: v1.10.114[ 10.665622] IPv6: ADDRCONF(NETDEV_CHANGE): eth1: link becomes ready5[ 10.673312] r8152 2-2:1.0 eth1: carrier on6...
Update network configuration
As I mentioned previously, I decided to use a USB adapter for traffic going to the Internet. Raspberry Pi uses the internal
eth0 port for that by default. We need to do changes inside the
/etc/config/network file. This time, however, we can apply the final configuration, including the final IP address for the device. You just need to make sure that you don’t apply the configuration, e.g., by rebooting the device.
My file looks like this:
1...2# My LAN network, where Raspberry Pi serves as a router.3# Uses the internal Ethernet port on Raspberry Pi.4config interface 'lan'5 option type 'bridge'6 option ifname 'eth0'7 option proto 'static'8 # Static IP address of your router.9 # It must be unique across your LAN network.10 option ipaddr '10.0.0.1'11 option netmask '255.255.255.0'12 option ip6assign '60'1314# WAN network configuration, e.g., to reach15# the ISP router and the Internet.16# Uses the USB-to-Ethernet adapter.17config interface 'wan'18 option ifname 'eth1'19 option proto 'static'20 # Assuming your ISP router has IP 192.168.1.121 option ipaddr '192.168.1.2'22 option netmask '255.255.255.0'23 option gateway '192.168.1.1'24 option ipv6 'auto'25 option peerdns '0'26 # You can specify custom DNS servers27 option dns '18.104.22.168 22.214.171.124'28...
Install webserver and GUI
OpenWrt provides LuCI as a web UI. It allows you to manage all these settings through a simple webpage. On low-end OpenWrt routers, it usually uses
uhttpd as a webserver. However, Raspberry Pi is powerful enough to handle a full-fledged Nginx server.
You can install UI with these commands:
1opkg update2opkg install luci-ssl-nginx
Optionally, you can install language packs to switch UI into your preferred language. See the official documentation for the instructions.
To enable the UI, you need to do the following:
1[email protected]:~# /etc/init.d/nginx enable
That’s pretty much it! Raspberry Pi is ready now to serve as a router. If you have another home router with OpenWrt (like the Mikrotik in my case), you can transfer the remaining configuration, like firewall rules, DHCP and DNS entries, and then you can turn Raspberry Pi down. It’s ready to be placed as a router now.
After reboot, you can access the web interface in your browser using the static IP set in the network configuration.
In my case, the experiment to use Raspberry Pi as my router turned out well, and I decided to make this setup permanent. After a long time, I have OpenWrt with the latest patches. My VPN performance is significantly higher, and not to mention this solution gives me 4 GB of RAM and almost 60 GB of storage, which brings new possibilities, like running my proxy server or configuring more demanding firewall rules. I’m pleased that, finally, we have development boards capable of replacing network equipment. Such boards may shape the segment of home networking significantly in upcoming years. If you have a spare Raspberry Pi, I encourage you to try to use it as a router too. It’s a fun experience.
Update 11/01/2020: Recently, the Raspberry Pi Foundation introduced a new compute module based on Raspberry Pi 4. It has the same CPU and most of the features of the RPi4, but it also offers some extra goodies, mainly the PCIe x1 port. A few days ago, I read on Hacker News a post similar to mine. However, instead of using an external USB-to-Ethernet adapter, the author used an Intel NIC. He was able to attain a throughput of 3 Gbit/s, which is the maximum of the PCIe x1 bus. Making the NIC work was not difficult at all. He used a cheap adapter to PCIe x16 and built the Intel NIC driver from sources. I think it’s worth reading and a decent alternative to my approach if you want to squeeze even more performance out of your RPi.