Let's say you want to run a non-OpenWRT application such Adguard Home or Pi-Hole as an add-on to your router.
But on the one hand you don't want to dedicate another system to it, using up a switch port and ethernet cabling and yet another wall wart.
On the other hand you don't want to or can't install it on your router because of its limited storage, memory and CPU. To say nothing of the fact that it'll get erased when you upgrade OpenWRT.
Enter the Raspberry Pi Zero ethernet gadget. The Zero is a $10 [$15 with wifi] computer less than half the size of a business card, and properly configured it can be plugged directly into your router's USB port and act as an ethernet-connected computer, implementing both the host and the client interface. The power provided by the USB host is usually sufficient and it won't need its own power supply.
What that means is when you turn on your router, on comes a second, extremely efficient Linux computer with 512MB of RAM and as much storage a you're willing to buy in the form of a MicroSD card. (Last time I looked the sweet spot was 32GB in a quality brand like SanDisk for well under $10.)
Instructions for getting AdGuard Home up and running on a Pi Zero attached to a USB-equipped OpenWRT router
Scope:
This is mostly specific to this application, of configuring the Pi Zero as an ethernet gadget, and your router to support and connect to it. Detailed instructions on the various ways of using AdGuard can be found, for example, here:
Other subjects such as how to write an OS to an SD card are well-documented elsewhere.
Notes and Warnings
Difficulty: I've tried to be thorough enough that beginner can follow this, but this is really fairly ADVANCED, and uses a feature which is considered experimental. Among other things it proposes configuring a headless system for network-only access, without ever attaching a keyboard or display. If you're not comfortable working with the Linux shell, modifying your network and editing configuration files then this can be an adventure, and you'll want to play around on another computer (another Pi such as the 3 or 4 B makes a good host for experimenting) rather than commit your router to this and possibly break your network.
Suitability for production use: I did this for fun and have not used it long-term. It works well with all three routers I tried, but that's a tiny sample, and only one of them is a retail embedded router, a Linksys EA6350 v3; the others were a Pi 4 and an x86_64 machine.
- Your router might not supply sufficient stable power (which is still okay if you hook up a wall wart to the power port). All the ones I tested did, but again, only one was a retail embedded router.
- The ethernet gadget mode of the Zero is an experimental or "hobbyist" feature, not commonly used in production nor considered critical by maintainers, it is possible that it might not work reliably on your router's platform.
Configuring the Pi Zero to be an ethernet gadget
There are basically two ways to configure a Pi Zero. The standard way is to hook up a keyboard and monitor to it, boot it from a newly-written MicroSD card and configure it directly. The problem with this is it requires an OTG USB adapter or hub and a mini-HDMI cable. If you already have those things, great, do it that way. But here we're going to configure a Micro SD card on another machine, then slip it into the Pi Zero, plug that into a router's USB port and just expect everything to work, because we're clever and brave. To follow these instructions, you'll need the ability to mount a MicroSD card on a Linux machine. If nothing else your OpenWRT router will serve this purpose, if you have a USB to [SD to] MicroSD adapter.
Download the latest Raspbios image -- I recommend the Lite edition -- for the Pi Zero, unzip it and write it to a MicroSD card. Instructions at the Raspberry Pi site and all over the web.
Next, connect the card via a reader or adapter to the running Linux system you're going to use to configure it, then type:
dmesg | tail
to see what device it was allocated in the /dev/sd? hierarchy. I'm just going to say /dev/sdX
here, substitute the correct one. Now mount the first partition:
mount /dev/sdX1 /mnt
Create an empty file called "ssh"; this tells a Pi at boot to start an ssh daemon:
touch /mnt/ssh
To the file /mnt/config.txt, add a line consisting of "dtoverlay=dwc2"
echo -e "\ndtoverlay=dwc2" >> /mnt/config.txt
(The first newline is usually a good idea in case the file doesn't already end with one.)
Now umount the first partition and mount the second.
umount /mnt
mount /dev/sdX2 /mnt
Add dwc2 and g_ether to /mnt/etc/modules.
echo -e "\ndwc2\ng_ether" >> /mnt/etc/modules
Now, invent a pair of mac addresses. They'll each consist of six 8-bit (i.e. two digit) hex numbers separated by colons. One of them will become the hardware address of your new USB ethernet interface on the router (cleverly provided by the Pi Zero), the other will become the mac also used by the Pi Zero as the hardware address of its client interface. The ones shown below are just examples, come up with your own. I'll use these:
00:6d:eb:a8:44:7f (host)
1a:0d:c4:d1:19:9b (client)
Create the file /mnt/etc/modprobe.d/g_ether.conf with the following line:
options g_ether host_addr=00:6d:eb:a8:44:7f dev_addr=1a:0d:c4:d1:19:9b
For example (and using your own choice of mac addresses):
echo "options g_ether host_addr=00:6d:eb:a8:44:7f dev_addr=1a:0d:c4:d1:19:9b" > /mnt/etc/modprobe.d/g_ether.conf
Decide what network you want the new interface on your router to use. You can either A) add it to the conventional LAN bridge and give the client an IP address in the LAN subnet, or B) define it as a separate network in its own firewall zone. (A) is much simpler to define and use, and requires little instruction, so below we're going to do (B) because it's useful if you have multiple internal networks and you need to manage connectivity to the DNS server as a special case, in which case there are more steps that need to be spelled out.
Let's say it's 192.168.4.0/24.
Option 1
Disable the dhcp client by removing the systemd symlinks:
rm /mnt/etc/systemd/system/dhcpcd5.service
rm /mnt/etc/systemd/system/multi-user.target.wants/dhcpcd.service
Using an editor like vi or nano, or just cat
if you're familiar with it, create a file /mnt/etc/network/interfaces.d/usb0
. You're going to give the Pi Zero a static address. If your router will be at 192.168.4.1 on this interface, and you want the Pi Zero to have the address 192.168.4.144, it'll consist of the following lines:
allow-hotplug usb0
iface usb0 inet static
address 192.168.4.144
netmask 255.255.255.0
gateway 192.168.4.1
Option 2 (recommended on Raspberry Pi OS bullseye or later):
Tell the dhcp client to use a static address. Add the following to /etc/dhcpcd.conf
:
interface usb0
static ip_address=192.168.4.144
static routers=192.168.4.1
static domain_name_servers=192.168.4.1
(Thanks to @Norde for verifying this approach on a current release!)
Unmount the SD card again:
umount /mnt
You're done and can remove it.
Configuring the router
In an OpenWRT ssh session:
opkg update
opkg install kmod-usb-net-cdc-ether
Okay we're nearly there. With the Pi Zero UNPOWERED and DISCONNECTED (yes, you can physically damage, not just destroy data on, an SD card by inserting/removing it from a powered Pi, I've done it), insert the Micro SD card into the Pi Zero, and connect it via a standard USB A to micro-B cable to the router using the MIDDLE port on the Zero, the one near the mini-HDMI port, not the one at the end. This port can take both power and data; the one at the end only takes power.
Waich for a green LED on the Zero, and wait about one minute while it goes through its first boot, which does several first-boot tasks automatically. Then, still on the router, type
dmesg | tail
Two of the latest lines should look something like this (substituting your own host_addr value):
[ 2048.489085] cdc_ether 1-1:1.0 eth1: register 'cdc_ether' at usb-xhci-hcd.0.auto-1, CDC Ethernet Device, 00:6d:eb:a8:44:7f
[ 2048.489403] usbcore: registered new interface driver cdc_ether
Okay, this indicates you have a new ethernet device on your router. It will be ethN, where N is the highest-numbered ethernet device now present.
If you are planning to simply add this to your LAN bridge so that the Zero becomes another device on your LAN, you can do this now in Network -> Interfaces, Devices tab. The next section is only if you have a complex local network and have decided to go with choice (B) above, to place it in a distinct network and firewall zone.
In Luci, add a network interface.
Name: (anything you like. Call it "zero", for example)
Static address: 192.168.4.1
Device: (select the new interface from the drop list.)
Netmask: 255.255.255.0
Firewall zone: same as your lan, usually "LAN". Make sure forward policy for this zone is allow, as this controls whether traffic can move between interfaces within the zone.
Save and apply all your changes.
NOTE: If you currently have any firewall rules hijacking or preventing lan devices from using their own public DNS servers, now would be a good time to enter an exception for 192.168.4.0/24. It's going to need to be able to access public dns servers.
Now back at the command line, you should be able to ping the Pi Zero:
ping 192.168.4.144
If you see ping results you're golden. Try ssh (again, using your own chosen network/IP):
ssh 192.168.4.144 -l pi
Default password will be raspberry
. A notice will be displayed encouraging you to change it. You should do so at some point using the passwd
command.
Become root
sudo -s
Temporarily get yourself some DNS service from the router (again, substitute your router's address on the new network):
echo "nameserver 192.168.4.1" >> /etc/resolv.conf
Test it
ping download.openwrt.org
Assuming everything's fine and that worked, (first place to look if it didn't is at your firewall policies and rules),
Probably not a bad idea to upgrade it to get all the patches:
apt-get update
apt-get dist-upgrade
Wait for that to complete without error, then
reboot
You'll be kicked off the ssh session and back to OpenWRT on the router. Use dmesg | tail to see when the interface comes back. In most cases it will be restored and you'll be able to ssh in again, but I've seen one case where it did not and you have to reboot the router whenever you reboot the Zero if you want this device to reinitialize. If that's the case, it's up to you whether you want to A) accept the situation, B) persue a solution, C) give up, D) use a different router, or whatever.
If it's back, ssh back into the Zero.
ssh 192.168.1.144 -l pi
sudo -s
Again, get temporary name resolution
echo "nameserver 192.168.4.1" >> /etc/resolv.conf
Now we're going to install AdGuard Home:
cd /usr/local
wget https://github.com/AdguardTeam/AdGuardHome/releases/download/v0.107.0-b.14/AdGuardHome_linux_armv6.tar.gz
(change this to a newer version if available)
tar zxvf AdGuardHome_linux_armv6.tar.gz
Assuming it all unzipped just fine, you can get rid of the tar file if you want:
rm AdGuardHome_linux_armv6.tar.gz
Now install it
AdGuardHome/AdGuardHome -s "install"
Now AdGuard will start on the Zero at boot time. Make sure it's running now:
service AdGuard start
By default AdGuard's web interface listens on port 3000 and its DNS interface listens on port 5353.
On the Zero's ssh session, edit /usr/local/AdGuardHome/AdGuardHome.yaml
Change the first bind_port entry to 80
Under DNS, change the port entry to 53
Save and restart AdGuardHome. You can now configure it via http://192.168.4.144/ (or whatever IP net/address you chose). See other sources for how to do that. One specific thing you'll want to do for this use case is add your OpenWRT router as the resolver for your local domain, and also for plain hostnames with no domain. At the end of the list of upstream DNS servers:
[/lan/]192.168.4.1
[//]192.168.4.1
Assuming "/lan/" and "lan" are the values you have for your local domain in OpenWRT's local domain fields in the general dhcp/dns section. If you use your own domain, substitute that.
Test that this can resolve before proceeding further
ping forum.openwrt.org
Also try manually setting a laptop or something to use 192.168.4.144 (or whatever) as its DNS server, and start visiting sites you haven't visited recently. If this doesn't work, it's time to go back and figure out what we missed. It's probably your firewall policies.
Set AdGuard Home to be the main DNS server for the network
In OpenWRT, modify /etc/config/dhcp
In the section "config dhcp lan", assuming that is your local network name:
list dhcp_option '6,192.168.4.144'
If you have any upstream resolvers in OpenWRT ("list server") you can remove them, but you might want to wait: it doesn't matter much because they're not going to get used except when the router itself is accessing the internet, and we don't ever do that except when updating our packages, right? It's up to you whether to add the Pi Zero as your sole upstream here. In theory that can create a circular delegation, but since the Pi should never delegate lookups to the router for anything but the local domain, in practice something would have to be pretty broken for that problem to occur. (Nevertheless I have not done it; my router uses 9.9.9.9 as its upstream for its own use, to eliminate a dependency on the Pi Zero being running when I'm doing router maintenance.)
If you've left your upstream routers defined in OpenWRT, any client machines on your netwrok that have not yet acquired the new DNS address will still use the old one successfully. Once the client systems have the new IP address -- they should get it the next time they make a DHCP query -- they should start using it.
If you try this and have issues (probably because I missed something working from memory, or maybe your firewall is sufficiently different from mine that something isn't getting through), I'll look into this forum now and then and help out if I can but I may not be immediately available. Chances are someone can help, though.