Raspberry Pi Zero as a router-attached ethernet gadget

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.

5 Likes

Very helpful, I can ping the pi from openwrt but when I try to log in to if using ssh 192.168.x.xxx -l pi my password is not accepted. If I plug the pi into a usb port on a pc I can ssh into it as pi with the usual password. Any ideas?

Try ssh -vvv ... (make ssh really verbose) and see where it's getting hung up. Might be a policy issue, make sure that INPUT and OUTPUT are accepted on that interface.

Edit: just want to be sure that you're certain that the problem is it's not accepting the password when connected to the router, but it accepts it when connected to a PC. Because that's a real brain-bender; whether a host accepts an ssh password is purely up to that host and it's difficult to hypothesize a probable, real-world reason it would work differently based on the connecting client.

Quick note for anyone using AdGuard Home as their primary resolver, and counting on it forwarding requests to the local domain to OpenWRT's dnsmasq:

Not all clients will add the local domain to the request for a naked hostname, even if dnsmasq's DHCP passes it to the host as the search domain. It is up to the application to do this, not the host, and some applications don't. Test this if you like with "dig" (which doesn't) and nslookup (which does, at least on MacOS)

So if you've got this in your AdGuard Home list of resolvers:

[/myfabulousdomain.tld/]192.168.0.1:5353

(Where "myfabulousdomain.tld" is whatever you've in your local domain settings for DNS/DHCP in OpenWRT, 192.168.0.1 is your OpenWRT router's IP, and (optionally -- this is not necessary if they're on different hosts) :5353 points it to the port you've got OpenWRT's dnsmasq listening on)

This will only work for appliances and applications which append the local domain to requests for undecorated hostnames, either automatically or when the hostname alone returned NXDOMAIN. Some don't. Notably, a lot of IoT devices don't.

The answer is to add this to your list of resolvers in AdGuardHome:

[//]192.168.0.1:5353

Or whatever. For example, my setup is

tls://dns.quad9.net
tls://ordns.he.net
[/mydoghasfleas.org/]192.168.128.10  <-- my openwrt router
[//]192.168.128.10

Well, not really; I made up that domain name and IP. The point is the pattern in that last one -- which as far as I can tell is undocumented -- apparently tells AdGuard Home to delegate undecorated hostnames (i.e. no domain at all) to the IP address[:port] indicated. Hope this helps someone.

1 Like

zero2w works like a charm!! My only 'problem' was that if macbook went to sleep the usb0 wasnt coming back after waking up. Maybe I need a script to restart things.

I now got a gpio tft from adafruit so I can activate scripts when headless but Im still trying to make the tft work (with the python libs, not kernel framebuffer)

2 Likes

Thank you for sharing the detailed write up!

I am just curious to know how is it different from installing AdGuard on RPi separately without making it as a router attached ethernet gadget.

The original pi zero only has usb, no ethernet or wifi, that is how it is different from the link you have provided.

Makes sense! I have pi zero W which has wifi capability so I could go with alternate approach.

Followup:

This is more than an experiment for me now, I've put it into production. However, raspios is a full systemd-based OS: it does arbitrary disk writes, requires a clean shutdown and is rather bloated for appliance use. I decided to put an appliance OS on it instead. My choices were Alpine and OpenWRT. Decided to try OpenWRT as it's much more lean and compact. If you do this, you'll want to disable dnsmasq, odhcp, firewall and wpad as they'll just be in the way.

Getting a functioning OpenWRT install with all necessary drivers on a non-wifi Zero without doing a custom build is pretty labor intensive; it probably took me 1-2 hours and required a keyboard, monitor, mini-HDMI cable, a USB OTG hub and a USB2-compatible ethernet dongle. But that's just for setup, you don't need any of those things once it's configured.

There are slight syntactic differences between OpenWRT and raspios in setting up ethernet gadget mode. Details if anyone wants to try it. It's working perfectly in production now in my 1G fiber multi-VLAN, multi-telework household.

(Edit: the ethernet dongle wasn't really needed for this install, it was just of a convenience to allow me to complete the setup over the network rather than from the keyboard and monitor. This approach will work without it.)

2 Likes

Thank you for this very useful guide!

I'm blocked when giving access to the net to my pizero (just after giving temporary name resolution using new network ip attributed to the router).

The resolution works but I can't reach any IP:

 ping openwrt.org'
 PING openwrt.org (139.59.209.225) 56(84) bytes of data.'
 From rasperrypi.local (169.254.46.150) icmp_seq=1 Destination Host Unreachable'

In doubt, I started with an "out of the box" configuration of OpenWRT to exclude any firewall rule problem.

Any ideas?

That's a puzzling response: the 169.254.* address suggests that the PI is failing to get an IP address: most DHCP clients come up with an address in that range when they are unable to get a response from a DHCP server. What makes that really puzzling how you're connecting to the Pi if it doesn't have a working IP address.

Anyway, need a lot of info to resolve this. Post the output of the following commands
ifconfig
ip route
On both the Pi Zero and the router.
Also post the contents of these files
Pi Zero:

/etc/network/interfaces.d/usb0
/etc/modprobe.d/g_ether.conf

Router:
/etc/config/network
and all the config zone and config forwarding stanzas from /etc/config/firewall; probably don't need all the rules.

Everything seems in order, except the ip route on the pizero who mentions this address as 169.254.x.x
(to be honest I didn't pay attention to it because I don't know what it means... my bad)

default dev usb0 scope link src 169.254.46.150 metric 202
169.254.0.0/16 dev usb0 scope link src 169.254.46.150 metric 202
192.168.4.0/24 dev usb0 proto kernel scope link src 192.168.4.144

Result of all queries:

Summary

pizero

ifconfig

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 735  bytes 78817 (76.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 735  bytes 78817 (76.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

usb0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.4.144  netmask 255.255.255.0  broadcast 192.168.4.255
        inet6 fe80::180d:c4ff:fed1:199b  prefixlen 64  scopeid 0x20<link>
        ether 1a:0d:c4:d1:19:9b  txqueuelen 1000  (Ethernet)
        RX packets 2229  bytes 141112 (137.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2995  bytes 337048 (329.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ip route

default dev usb0 scope link src 169.254.46.150 metric 202
169.254.0.0/16 dev usb0 scope link src 169.254.46.150 metric 202
192.168.4.0/24 dev usb0 proto kernel scope link src 192.168.4.144

/etc/network/interfaces.d/usb0

iface usb0 inet static
  address 192.168.4.144
  netmask 255.255.255.0
  gateway 192.168.4.1

/etc/modprobe.d/g_ether.conf

options g_ether host_addr=00:6D:EB:A8:44:7F dev_addr=1a:0d:c4:d1:19:9b`

openwrt router

ifconfig

br-lan    Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          inet addr:192.168.10.1  Bcast:192.168.10.255  Mask:255.255.255.0
          inet6 addr: fd83:dcdd:a988::1/60 Scope:Global
          inet6 addr: fe80::d6ee:7ff:fe61:ac86/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:125479 errors:0 dropped:0 overruns:0 frame:0
          TX packets:214116 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:21414535 (20.4 MiB)  TX bytes:249154457 (237.6 MiB)

eth0      Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          inet6 addr: fe80::d6ee:7ff:fe61:ac86/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1504  Metric:1
          RX packets:338614 errors:0 dropped:0 overruns:0 frame:0
          TX packets:321968 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:251577475 (239.9 MiB)  TX bytes:269573599 (257.0 MiB)
          Interrupt:21

eth1      Link encap:Ethernet  HWaddr 00:6D:EB:A8:44:7F
          inet addr:192.168.4.1  Bcast:192.168.4.255  Mask:255.255.255.0
          inet6 addr: fe80::26d:ebff:fea8:447f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3145 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2402 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:313814 (306.4 KiB)  TX bytes:185072 (180.7 KiB)

lan1      Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:126466 errors:0 dropped:12 overruns:0 frame:0
          TX packets:214121 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:21455875 (20.4 MiB)  TX bytes:249154519 (237.6 MiB)

lan2      Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lan3      Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lan4      Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:13565 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13565 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1123183 (1.0 MiB)  TX bytes:1123183 (1.0 MiB)

wan       Link encap:Ethernet  HWaddr D4:EE:07:61:AC:87
          inet addr:192.168.2.16  Bcast:192.168.2.255  Mask:255.255.255.0
          inet6 addr: fe80::d6ee:7ff:fe61:ac87/64 Scope:Link
          inet6 addr: 2a02:8070:8787:3fa0:d6ee:7ff:fe61:ac87/64 Scope:Global
          inet6 addr: 2a02:8070:8787:3fa0:badf:4da4:8320:ae15/128 Scope:Global
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:212148 errors:0 dropped:0 overruns:0 frame:0
          TX packets:77940 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:224026548 (213.6 MiB)  TX bytes:15623957 (14.8 MiB)

wlan0     Link encap:Ethernet  HWaddr D4:EE:07:61:AC:86
          inet6 addr: fe80::d6ee:7ff:fe61:ac86/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:28774 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:4725748 (4.5 MiB)

wlan1     Link encap:Ethernet  HWaddr D0:EE:07:61:AC:86
          inet6 addr: fe80::d2ee:7ff:fe61:ac86/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:27882 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:4556373 (4.3 MiB)

ip route

default via 192.168.10.1 dev wan  src 192.168.10.16
192.168.2.0/24 dev wan scope link  src 192.168.2.16
192.168.10.0/24 dev br-lan scope link  src 192.168.10.1
192.168.4.0/24 dev eth1 scope link  src 192.168.4.1

/etc/config/network

config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option packet_steering '1'
        option ula_prefix 'fd83:dcdd:a988::/48'

config device
        option name 'br-lan'
        option type 'bridge'
        list ports 'lan1'
        list ports 'lan2'
        list ports 'lan3'
        list ports 'lan4'

config interface 'lan'
        option device 'br-lan'
        option proto 'static'
        option ipaddr '192.168.10.1'
        option netmask '255.255.255.0'
        option ip6assign '60'

config interface 'wan'
        option device 'wan'
        option proto 'dhcp'

config interface 'wan6'
        option device 'wan'
        option proto 'dhcpv6'

config device
        option name 'eth1'

config interface 'zero'
        option proto 'static'
        option device 'eth1'
        option ipaddr '192.168.4.1'
        option netmask '255.255.255.0'

The results of ip route suggest that for some reason the 'gateway' line in the interface definition is not being followed. Nothing much occurs to me as to why that would be. All your files look right. Try forcing the route by hand (this is on the Zero):

ip route del default dev usb0
(if it complains try just ip route del default)
If no complaints, run
ip route add default via 192.168.5.1

Try ip route again and see how that looks. See if you have any luck accessing a remote site.

Note: still need to make sure your firewall allows forwarding to WAN from the zone the zero is in.

EDIT: (Note2: I need to revisit this document with respect to setting a static IP address: I thoughtlessly based it on an installation of Raspbian Buster from well over a year ago, which still used the old-school Debian approach. The current way is documented here https://www.raspberrypi.com/documentation/computers/configuration.html#static-ip-addresses In this approach dhcp is used even for static addresses. I suspect this may be why this is happening.)

1 Like

EDIT: (Note2: I need to revisit this document with respect to setting a static IP address: I thoughtlessly based it on an installation of Raspbian Buster from well over a year ago, which still used the old-school Debian approach. The current way is documented here https://www.raspberrypi.com/documentation/computers/configuration.html#static-ip-addresses In this approach dhcp is used even for static addresses. I suspect this may be why this is happening.)

Well seen, it is indeed a conflict with dhcpcd, thanks a lot!

Two working solution:

Disabling dhcpcd:

sudo systemctl disable dhcpcd

Or in a more correct way (following the recommendation for latest raspberrypios) :
Add in /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

Edit:
I also faced a bug with g_ether which did not apply certain MAC addresses and generate a random one instead.
cf issue: https://github.com/raspberrypi/firmware/issues/843
The workaround is to use one of these randomly generated MAC addresses (which do not trigger the bug) instead of the one originally specified in "/etc/modprobe.d/g_ether.conf".

1 Like

Great, thanks for confirming. I'll update the guide.

Thanks also for identifying that bug, it hasn't bit me. Since we're making life easier on ourselves by hard-coding the host and client IP addresses, the mac shouldn't matter for our purposes. (And it's kind of silly to enable dhcp service on a router interface that will only ever have one client whose purpose calls for a fixed, well known address!)

1 Like

Thanks for the guide update!

Now that I've finished my setup, here is three more comments:
(please tel me if I'm somehow wrong)

  1. As the goal is to tel AdGuardHome to use conditional forwarding, the DNS must be specified in config dhcp 'lan' section of OpenWRT dhcp config.

In /etc/config/dhcp:

In the section "config dhcp 'lan'"
list dhcp_option '6,192.168.4.144'
  1. For some devices, a defined DNS server may be hardcoded or impossible to modify (especially IoT). In this case it may be wise to create a DNS interception rule*:

*."option src_ip '192.168.1.0/24'" is here to define the rule only for your lan network (we don't want to apply it to our Pi zero AdGuard's network). Adapt 192.168.1.0 to your corresponding local network.

In /etc/config/firewall, add:

config redirect
        option target 'DNAT'
        option name 'Intercept-DNS'
        option src 'lan'
        option src_dport '53'
        option dest_ip '192.168.4.144'
        option src_ip '192.168.1.0/24'
  1. Adguard Home (v0.107.15) default web and DNS interface are now 80 and 53.
    Also the /usr/local/AdGuardHome/AdGuardHome.yaml file is created only after completion of the initial setup through web UI.
    So basically, once AdGuardHome is installed you can directly connect to 192.168.4.144:80 to configure it.

And you're right for the MAC adress problem. This is not really an issue in our actual configuration (more a curiosity ^^")

Yes, I omitted that. Quite right, I named the wrong section.

And yes, DNS interception is a good idea for all the reasons you specify. It won't help with devices wired to do DoH (DNS over HTTPS) unfortunately. The only way to stop that is with blacklists of the hosts and IP addresses that they're known to use.

1 Like

Unfortunately, yes. It would also be interesting to consider intercepting DoT on ports 853 and 5353.
I personally use some blocklists for known DoH servers (necessarily imperfect, but it's a start).

I cannot update my previous post with so here are updated instructions for those who are interested:

DNS interception (normal and DoT):
In /etc/config/firewall, add:

config redirect
        option target 'DNAT'
        option name 'Intercept-DNS port 53'
        option src 'lan'
        option src_dport '53'
        option dest_ip '192.168.4.144'
        option src_ip '192.168.1.0/24'

config redirect
        option target 'DNAT'
        option name 'Intercept-DNS port 853'
        option src 'lan'
        option src_dport '853'
        option dest_ip '192.168.4.144'
        option src_ip '192.168.1.0/24'

config redirect
        option target 'DNAT'
        option name 'Intercept-DNS port 5353'
        option src 'lan'
        option src_dport '5353'
        option dest_ip '192.168.4.144'
        option src_ip '192.168.1.0/24'

And some blocklist for known DoH servers:

https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-domains.txt
https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv4.txt
https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv6.txt
ttps://gist.githubusercontent.com/ckuethe/f71185f604be9cde370e702aa179fc2e/raw/53fe52046836ac3009e9505b7b8b8b5de42d84e3/doh-blocklist.txt
https://raw.githubusercontent.com/Sekhan/TheGreatWall/master/TheGreatWall.txt
https://raw.githubusercontent.com/Sekhan/TheGreatWall/master/TheGreatWall_ipv4
https://raw.githubusercontent.com/Sekhan/TheGreatWall/master/TheGreatWall_ipv6
1 Like

A couple of additional notes, which I will try to work into the top post at some point.

  1. This ethernet gadget, or indeed any USB-connected ethernet interface, may have issues when used in conjunction with hardware NAT offloading enabled on a compatible platform, e.g. on mt7621-based routers. This is only anecdotal, but I found that under high query load conditions, TCP connections to upstream TLS servers started hanging, until hundreds were open and remaining open. AdGuard Home would stop responding when the number equaled the goroutines limit (default 300) set in AdGuardHome.yaml. Sometimes it would recover, sometimes AdGuard Home needed to be restarted, and even then you'd have to wait a minute or so for TIME_WAIT to expire on the connections.

I have moved this device to another router -- one which I use only as an AP and managed switch, ipq4019-based which doesn't support hw NAT offloading (or need it in this role); and I can no longer reproduce the problem. I also never had this problem when attached to an x86_64 system, though that isn't a conclusive negative.

  1. It isn't necessary to create a separate address, subnet and firewall zone for this host side of this device, it works fine if you add it (e.g. eth1 or whatever it registers as on the router) to a lan bridge (e.g. the conventional br-lan) and give the gadget client side an address in the LAN subnet. This is a convenient simplification.
1 Like

A couple of additional notes:

  1. This works well with the Pi Zero 2 W, which is now becoming available again.
  2. If for some reason you don't want to use Raspbian (personally I'm not fond of systemd on appliances) this also works perfectly with Void Linux.