Raspberry Pi Wi-Fi Bridge for an Ethernet-Only Printer
Or: how to give an old Ethernet-only printer a home on your Wi-Fi network - with no NAT, no port forwards, and no touching the printer's config.
The setup
I had an HP printer that only speaks Ethernet, sitting nowhere near my router. It has a static IP - 10.0.1.10/24 - baked into its config, and every machine in the house already prints to it by that address. I didn't want to change any of that. I just wanted it reachable over Wi-Fi.
A Raspberry Pi solves this nicely: plug the printer straight into the Pi's eth0 with a single Ethernet cable, let the Pi join the Wi-Fi (wlan0), and have the Pi shuttle packets between the two. The goal is total transparency - every device on the Wi-Fi subnet should think the printer is just another host sitting on the same LAN.
Wi-Fi 10.0.1.0/24 direct cable
┌─────────────┐ wlan0 eth0 ┌──────────────┐
│ phones/laptops│~~~~~~~~ [ Raspberry Pi ] ─────────── │ HP printer │
│ 10.0.1.x │ 10.0.1.225 │ │ 10.0.1.10 │
└──────────────┘ └──────────────┘
Why not a normal bridge?
Classic Layer 2 bridging (brctl / a bridge netdev spanning wlan0 and eth0) does not work with Wi-Fi. A Wi-Fi client interface can't put arbitrary foreign MAC addresses on the air - the AP only expects frames from the MAC that associated to it. So the printer's MAC behind eth0 never makes it across the wireless link. This is the single most common wall people hit, and it's a hardware/802.11 limitation, not a config mistake.
The answer is Layer 3 proxy-ARP bridging with parprouted. Instead of bridging MAC frames, the Pi answers ARP requests on behalf of the printer and routes the actual IP packets. Your other devices ARP for 10.0.1.10, the Pi replies "that's me" (with its own Wi-Fi MAC), the packets get routed out eth0 to the printer, and return traffic comes back the same way. No NAT, no subnet on the printer side, no changes to the printer.
The key insight for this particular build: the Wi-Fi subnet and the printer are the same subnet -
10.0.1.0/24. That's exactly what makes proxy-ARP the right tool: we're not routing between two networks, we're stretching one network across the Pi so the printer keeps its existing IP and everyone reaches it directly.
Because the printer has a static IP, this is actually easier than the typical parprouted tutorial - you can skip the DHCP relay (dhcp-helper / dhcrelay) entirely. There's no DHCP to bridge.
Step 0: Recon
Before changing anything, figure out what you're working with. On the Pi:
ip -brief addr # interfaces + their IPs
ip route # routing table
cat /proc/sys/net/ipv4/ip_forward # forwarding on?
cat /sys/class/net/eth0/carrier # is the printer cable actually live? (1 = yes)
iw dev wlan0 get power_save # Wi-Fi power management state
What I found on mine:
wlan0had10.0.1.225/24, default route via10.0.1.1- a normal DHCP client on the LAN.eth0was up with carrier (cable to the printer live) but had no IPv4 address.ip_forwardwas already1.- Wi-Fi power management was ON - a classic source of "the printer randomly disappears" bugs.
Sanity-check that the printer isn't reachable yet, and that nothing on the Wi-Fi side is already answering for its IP:
ping -c2 10.0.1.10 # should fail right now
ip neigh show 10.0.1.10 # 'incomplete' on wlan0 = nobody's claiming it. good.
Step 1: Install parprouted and enable forwarding
sudo apt install parprouted
# Turn on IPv4 forwarding, persistently
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-forward.conf
sudo sysctl --system
Step 2: Give eth0 an address (the clone trick)
parprouted needs some IP on each interface it bridges so it can source ARP requests. The documented trick is to clone the Wi-Fi address onto eth0 as a /32:
# Grab wlan0's current IP and put it on eth0 as a host address
WIP=$(ip -4 addr show wlan0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
sudo ip addr add ${WIP}/32 dev eth0
Why this works: when the Pi sends a packet toward the printer, eth0 sources it from 10.0.1.225. The printer thinks the whole /24 is on its local wire, so it ARPs for 10.0.1.225 on the Ethernet segment - and eth0, now holding that address, answers. Two-way reachability, established.
Step 3: A belt-and-suspenders route to the printer
parprouted learns hosts from observed ARP traffic and installs /32 routes automatically. But if the printer sits silent after boot, there's nothing to learn from yet. A static host route removes that chicken-and-egg problem:
sudo ip route add 10.0.1.10/32 dev eth0
Now the Pi will always route 10.0.1.10 out eth0, even before parprouted has seen a single ARP packet. Note this /32 beats the connected 10.0.1.0/24 dev wlan0 route by longest-prefix match, so the Pi itself reaches the printer over the cable rather than trying the air.
First light 🎉
$ ping -c3 10.0.1.10
64 bytes from 10.0.1.10: icmp_seq=1 ttl=60 time=1.40 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=60 time=1.27 ms
--- 10.0.1.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss
$ ip neigh show 10.0.1.10
10.0.1.10 dev eth0 lladdr 00:30:c1:55:62:b0 REACHABLE # 00:30:c1 = Hewlett-Packard OUI ✓
The Pi can talk to the printer. Now to make the rest of the network able to.
Step 4: Start parprouted
sudo parprouted eth0 wlan0
That's it. parprouted now:
- listens for ARP requests for
10.0.1.10arriving onwlan0and answers them with the Pi's Wi-Fi MAC, - installs
/32routes (metric 50) for hosts it learns on each side, - refreshes those ARP entries every ~50 s so the kernel doesn't expire them.
Your phones and laptops now believe the printer is a normal neighbor on the Wi-Fi. Verify from another device (this can't be tested from the Pi itself):
# from a laptop on the Wi-Fi
ping 10.0.1.10
Step 5: Kill Wi-Fi power management
This one bites people days later, when the printer becomes intermittently unreachable. The Pi's Wi-Fi radio dozes off and drops the proxy-ARP responsiveness. Turn it off:
sudo iw dev wlan0 set power_save off
To make it stick across reboots under NetworkManager, set a global default (value 2 = disable) rather than fighting per-connection settings:
sudo tee /etc/NetworkManager/conf.d/99-wifi-powersave-off.conf <<'EOF'
[connection]
wifi.powersave = 2
EOF
(On a dhcpcd/wpa_supplicant system, drop iwconfig wlan0 power off into /etc/rc.local or a dispatcher hook instead.)
Step 6: Make it survive a reboot
Three things need to persist: forwarding (done in Step 1), the eth0 clone + printer route, and the parprouted daemon itself. There's also a subtle trap to defuse first.
Trap: stop the OS from doing DHCP on eth0
My Pi runs NetworkManager (rendered from netplan), and eth0 was configured with dhcp4: true. But there's no DHCP server on the printer cable - so at every boot NM would pointlessly retry DHCP on eth0 and could race our manual /32, flushing it out from under parprouted. Turn eth0's addressing off entirely and let our service own it:
# /etc/netplan/*-eth0.yaml (back up the original first!)
network:
version: 2
ethernets:
eth0:
renderer: NetworkManager
match: {}
dhcp4: false
dhcp6: false
link-local: [] # no 169.254.x.x either
optional: true # don't block boot waiting on this link
# ...keep your existing networkmanager: uuid/name block...
sudo netplan generate && sudo netplan apply
The pre-start script
This re-derives wlan0's current IP on every boot (so a changed DHCP lease won't break anything), clones it onto eth0, and re-installs the printer route:
# /usr/local/sbin/parprouted-bridge-pre.sh
#!/bin/bash
set -u
PRINTER_IP="10.0.1.10"
# Wait (up to ~30s) for wlan0 to get an IPv4 address.
WIP=""
for _ in $(seq 1 30); do
WIP=$(ip -4 addr show wlan0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)
[ -n "$WIP" ] && break
sleep 1
done
[ -z "$WIP" ] && { echo "wlan0 has no IPv4; aborting" >&2; exit 1; }
ip link set eth0 up
ip addr flush dev eth0 scope global 2>/dev/null || true
ip addr add "${WIP}/32" dev eth0
ip route replace "${PRINTER_IP}/32" dev eth0
echo 1 > /proc/sys/net/ipv4/ip_forward
echo "parprouted-bridge: eth0 cloned ${WIP}/32, route to ${PRINTER_IP} ready"
And a matching teardown script:
# /usr/local/sbin/parprouted-bridge-post.sh
#!/bin/bash
ip route del 10.0.1.10/32 dev eth0 2>/dev/null || true
ip addr flush dev eth0 scope global 2>/dev/null || true
exit 0
sudo chmod +x /usr/local/sbin/parprouted-bridge-*.sh
The systemd unit
# /etc/systemd/system/parprouted-bridge.service
[Unit]
Description=Proxy-ARP bridge (wlan0 <-> eth0) via parprouted for direct-attached printer
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
GuessMainPID=yes
ExecStartPre=/usr/local/sbin/parprouted-bridge-pre.sh
ExecStart=/usr/sbin/parprouted eth0 wlan0
ExecStopPost=/usr/local/sbin/parprouted-bridge-post.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
parprouted daemonizes itself (double-fork), so Type=forking is correct - no need for the -d debug/foreground flag, which just spams the journal. Enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now parprouted-bridge.service
sudo systemctl status parprouted-bridge.service
You should see Active: active (running), the pre-script's success line in the logs, and parprouted eth0 wlan0 as the tracked main PID.
The final smoke test
# on the Pi
ping -c3 10.0.1.10 # printer reachable
ping -c1 10.0.1.1 # Wi-Fi gateway still fine (didn't break normal networking)
ip route | grep 10.0.1.10 # /32 via eth0 present (yours + parprouted's metric 50)
iw dev wlan0 get power_save # -> "Power save: off"
systemctl is-enabled parprouted-bridge # -> enabled
# from a phone/laptop on the Wi-Fi
ping 10.0.1.10 # transparent reachability 🎉
Print a test page by IP and you're done.
Gotchas, recapped
- L2 bridging can't work over Wi-Fi. Don't waste an afternoon on
brctl. Proxy-ARP (parprouted) is the way. - The printer must talk occasionally for
parproutedto learn its route from ARP. The staticip route add <printer>/32 dev eth0fallback sidesteps a silent-at-boot printer entirely. - Wi-Fi power management will make the printer flaky hours later. Disable it, and make the disable persistent.
- Your OS may DHCP on
eth0behind your back. On a dead-end printer cable that's a race waiting to happen - turn eth0 addressing off in netplan/your network manager and let your service own the interface. proxy_arp=0on the interfaces is expected.parproutedanswers ARP from userspace; it does not use the kernel's per-interfaceproxy_arpflag. Don't "fix" it.
Optional: AirPrint / auto-discovery
Because the printer has a static IP and everyone already prints to it by address, discovery is irrelevant here - printing works the moment ARP resolves. But if you want phones to auto-discover it (AirPrint/mDNS), install avahi-daemon and enable the reflector so multicast DNS crosses the two interfaces:
# /etc/avahi/avahi-daemon.conf
[reflector]
enable-reflector=yes
That's the whole build: a $35 board turns a wired-only printer into a first-class citizen of the Wi-Fi network, transparently, with a handful of config files and no compromises on the printer side.