Using Systemd to run Openwrt on Raspberry Pi 4...welcome to join

Hi all,

I got a lot of help from the earlier posts in this thread - so thanks to all of you.

I started a separate thread for a specific problem I'm having running openwrt using nspawn on a Pi4 and was asked about if/how I got nspawn/openwrt working on the pi and perhaps contribute to this thread. I'm not actually sure if it is working properly - others have probably gotten further. There are some strange aspects I'll cover later, but it is definitely starting up, including the luci website, and basic router functionality also appears to be working (besides the problem I describe in the other thread) - so I'll share what I've managed so far. Perhaps others can confirm and/or give me appropriate tests to run.

The other thread I created is No IPV4 using Raspberry pi as router - if you have any suggestions for troubleshooting IPv4 / routing / firewall / iptable vs nftable issues please take a look.

In brief, I'd like to use my PI as a gigabit WAN router and if it really doesn't need too much CPU, then I'll also use it for Kodi and maybe office tasks at the same time (hey - 2 HDMIs - why not). I've been really happy with OpenWRT to date in running our hostel network, so I'm happy to keep using it - but my old TP-Link 1043NDs struggle to reach ~170MBps with SQM on my upgraded gigabit fiber.

For initial basic testing, I decided to dedicate a port on a 1043 and try to get the Pi running as a router. I'm using the pi's built-in eth0 with my existing local VLAN11 (eth0.11) acting as the WAN input to the Pi. The openwrt container should route VLAN11 onto VLAN16 and back onto the same physical cable to the same switch. So the basic topology is: Internet <-> ONT <-> OpenWRT TP-Link 1043nd router <-> a 2nd 1043nd running as a switch/AP <-> Raspberry pi <-> back to the 2nd 1043nd and a test WiFi on a different VLAN.

My steps were:

  1. Get the standard RPi4 Rasberry Pi 32-bit Buster image working (a full 64-bit installation should also work if you are willing to go without luci initially and also don't want, say, full Kodi on your pi)
  2. Install systemd / nspawn.
  3. Download the 32-bit ext4 Pi2 image (do not use squashfs images - another mistake I made):
    http://downloads.openwrt.org/releases/19.07.4/targets/brcm2708/bcm2709/openwrt-19.07.4-brcm2708-bcm2709-rpi-2-ext4-factory.img.gz
  4. Unzip and place the img file on the pi - I used: /home/pi/OpenWRT/openwrt-19.07.4-...
  5. Create the relevant outer mount point - I used: mkdir /mnt/openwrt
  6. (I forget if I also had to install losetup.)
  7. The boot folder needs to get mounted inside the openwrt folder - see @blee 's post (17 on this thread) - basically mount the main partition and create the boot folder, then mount the boot partition:
sudo losetup -P -f /home/pi/OpenWRT/openwrt-19.07.4-bcrm2708-bcm2709-rpi-2-ext4-factory.img
sudo mount /dev/loop0p2 /mnt/openwrt 
mkdir /mnt/openwrt/boot
sudo mount /dev/loop0p1 /mnt/openwrt/boot

(note: at this point you can edit the config files in /mnt/openwrt/etc/config)

  1. Assuming systemd-nspawn is installed and using the folders I describe above, the following line will then launch the openwrt container:
    sudo systemd-nspawn --directory=/mnt/openwrt --boot --network-interface=eth0 --network-veth

Be sure to use --boot. Without it, the terminal will display the basic openwrt text splash screen and login prompt - just as if you connected via ssh - but nothing works. With the --boot option, nspawn only displays basic information on how to kill the container, but (almost) everything just worked on the actual ethernet port - it's just a bit harder to test.

  1. I connected a cross-over cable between my laptop and the pi, set my laptop's IP-address as 192.168.1.10, browsed to 192.168.1.1 - and luci was there waiting for me. Note: veth works as well (a bit more on that later) but again you need to configure it in luci (or by editing the various config files) before you can use it.

  2. With luci, I could change the password, do basic configuration, and modify the interface to use DHCP and connect to my existing VLAN. At this point, the connection broke down, I plugged it into my already configured switch, checked the DHCP leases for openwrt, and reconnected to luci on that address - and after that everything gets a bit easier.

  3. For my test, I created a wan and wan6 interface on the pi to connect to the existing vlan 11 subnet which also connects to the internet (eth0.11). I then assigned lan to vlan 16 and added a static IP and DHCP, and added the normal firewall routing rules. On my existing openwrt 1043nd switch, I put VLAN 11 and 16 on the switch port to the Pi, added a test interface and wlan for vlan16 (just static IP with the Pi's IP as the gateway and no DHCP) and then got my laptop onto that wlan for further testing.

So that's where I am now. Basically - everything works except IPv4 beyond the local subnet. So DHCP, IPv6 DNS and the IPv6 internet all work, but I can't ping on IPv4 beyond the local VLAN16 subnet or browse the IPv4 internet - although these all work when I ssh into the pi's openwrt - more on that in the other thread.

A few minor notes:

  1. The pi has no switch device, so that configuration screen isn't available and adding a VLAN is less obvious. You can either directly edit the /etc/config/network file or use luci as follows: wherever luci displays a device connection drop-down, at the very bottom just type in the full vlan address (e.g. "eth0.16"). Afterwards, luci will display that VLAN option among the others.
  2. Inside openwrt, the veth virtual adapter appears as "host". I used luci to bridge it to my vlan16 interface. At this point you have a weird reverse host/guest situation where the guest is providing internet to the host - but it works. The little cable-connect icon will appear in the upper right corner of the raspberry and the network will start working as well as it works elsewhere (so no ipv4 yet).
  3. Almost all configuration changes stick - so they get written back to the image. But I can't change the name of the openwrt instance. I can edit it and it goes into the config file but luci and the ssh login continue to display "openwrt". There is a brief error which is displayed on the website indicating some type of read-only filesystem (but the name is changed in the file - I assume "openwrt" is the default and some error occurs during the initialization process).
  4. I also can't reboot inside the container. If the container tries to reboot, nspawn exits with a TTY error. That TTY error persists and nspawn fails until I reboot the pi host. If I simply kill the container using ctrl+] 3-times, I can restart it.

So basically, I'm not sure if the system is doing a proper boot - obviously the container does a lot of magic, and I'm not well versed in the linux or openwrt boot process.

My next goal is to get ipv4 working and then to install sqm and repeat all the basic configuration I have on my existing router.

I'll post again if I learn anything else I think may be useful or if I can help someone else get this far.

VMWare has announced ESX for ARM. I have no idea if it will run on a RPI4, or if OpenWRT will run under it, but it might be a viable alternative for what you are wanting to do.

It's a bit easier to do it like this in my opinion:

  1. Configure networkd bridge (I tested this on Armbian; on Ubuntu images you will have to get rid of netplan first) and install systemd-container
  2. Create /etc/systemd/nspawn/openwrt.nspawn with following content:
[Exec]
PrivateUsers=0

[Network]
Bridge=br0
  1. Untar OpenWRT rootfs into /var/lib/machines/openwrt (create openwrt subvolume if you are on btrfs, otherwise just "mkdir openwrt" there).
  2. Change pi_preinit_no_failsafe to "y" in /var/lib/machines/openwrt/lib/preinit/00_preinit.conf and launch container once with "machinectl start openwrt" to generate /etc/config/network inside container.
  3. Replace "eth0" with "host0" (or "host0.X where X is VLAN number, if you need VLAN) in /var/lib/machines/openwrt/etc/config/network and now you can create clones ("machinectl clone openwrt router") or enable autostart on boot with "machinectl enable router" without need to write your own systemd unit.

One could ask why I propose to clone openwrt container if you can use single openwrt container? In my opinion openwrt container with untouched default settings could be useful for future deployments on other RaspberryPi's or other ARM boards. To do so export it with "machinectl export-tar openwrt openwrt.tar" (you also could use this to backup whole thing) and import with "machinectl import-tar openwrt.tar" (although you still need to create /etc/systemd/nspawn/openwrt.nspawn manually).

I also have to note that if you would like to use WireGuard inside OpenWRT container - you have to put wireguard into /etc/modules on host, to make it available inside container.

Thanks for those suggestions. I may look into the initialization settings - perhaps it can help with the boot process. In the end - I was able to get OpenWRT running - as far as I can tell including the firewall and ipv4 comes up. I haven't had much time to work on it in the last month but the key point was to run OpenWRT on an iptables OS - I used UbuntuMATE instead of Raspberry OS. The other issue was getting the firewall settings to come up and persist - I'm still not sure on the cause but logging into LuCi and clicking on Status->Firewall would trigger the processing of the firewall configuration after which I could save it using iptables-save and then use iptables-restore during the boot process. So now I have a Pi that boots into MATE with OpenWRT running in a container and the firewall starts up. Pretty happy overall. Now to work on Kodi.
More at:
https://forum.openwrt.org/t/no-ipv4-using-raspberry-pi-as-router/80730/31

Thanks to all forum members.

It's about 2 years later... My how time flies... And OpenWrt now has nftables based firewall. Has anyone tried systemd-nspawn of nftables based OpenWrt from Debian or another nftables host?

I've got a system running Debian installed at a remote location and want to run routing in a container. I'm waffling between debootstrap of a machine with simple nftables firewall and systemd-networkd setup, or putting OpenWrt in a container and having a more router oriented OS with Luci.

Not exactly what you're asking, but for my core router I run OpenWRT as an LXD container inside Alpine Linux 3.17 on x86_64. Painless to set up and nftables works fine out of the box. So far the only stock thing that doesn't work is the "Connections" tab in luci-statistics, because CONFIG_NF_CONNTRACK_PROCFS isn't configured in the kernel by default in most general distributions these days.

Can anyone share the steps to setup openwrt 22 in a container?

I'm using the rootfs.tar.gz and upon booting there's that failsafe issue and dbus/uhttpd not started issue.

$ sudo systemd-nspawn -bD openwrt 
Spawning container openwrt on /mnt/ramdrive/openwrt.
Press Ctrl-] three times within 1s to kill container.
Failed to resize receive buffer: Operation not permitted
Press the [f] key and hit [enter] to enter failsafe mode
Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level
f
- failsafe -
Generating 1024 bit rsa key, this may take a while...
Public key portion is:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCmGIdChOrozvBFRwUqYoFrwAKDDVhG2vPcCsjlsrgHhy0tnmzyqdKc6rKD6gKhyPkExQtKjTjclGhBp63wYU6NTYbSOxz6BdsTIxr8bMPH/lhj4KCFbsxeSnZWbT6Ir9E0yoq2U50HwVPljcJ1S7tp+WMThEuJxP8btfMFlTIE1w== root@openwrt
Fingerprint: SHA256:o+qPEAQYeXDImigxDiYDFhMoI3myJFtb+Cpy3PGaZn4


BusyBox v1.35.0 (2023-01-03 00:24:21 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 22.03.3, r20028-43d71ad93e
 -----------------------------------------------------
================= FAILSAFE MODE active ================
special commands:
* firstboot	     reset settings to factory defaults
* mount_root	 mount root-partition with config files

after mount_root:
* passwd			 change root's password
* /etc/config		    directory with config files

for more help see:
https://openwrt.org/docs/guide-user/troubleshooting/
- failsafe_and_factory_reset
- root_password_reset
=======================================================

root@openwrt:/# ps
  PID USER       VSZ STAT COMMAND
    1 root       968 S    /sbin/init
    3 root      1040 S    /sbin/procd -h /etc/hotplug-preinit.json
    4 root      1508 S    /bin/sh /etc/preinit
   52 root      1124 S    lock /tmp/.failsafe
   60 root       940 S    dropbear -r /tmp/dropbear_failsafe_host_key
   66 root      1508 S    /bin/sh /etc/preinit
   67 root      1124 S    lock -w /tmp/.failsafe
   68 root      1136 S    ash --login
   78 root      1132 R    ps
root@openwrt:/#

I tested to use LXC to run OpenWrt which works, but with some limitations (probably setting issue which I haven't figured out)

Why not run with LXC/Docker?