The goal is to connect a non-WiFi computer to a WiFi network using a Raspberry Pi. We will use a Raspberry Pi 4 Model B as a bridge between the non-WiFi computer and the WiFi network. The Raspberry Pi connects to WiFi and shares its connection with other computers over Ethernet.

diagram of the network topology

These instructions were only tested and verified using:

  • Fresh install of Raspberry Pi OS Lite (bookworm, 64-bit, 2024-03-15)
  • Raspberry Pi 4 Model B
  • Typical home WiFi network using WPA2

This Stack Overflow answer and accompanying script as well as this proxy arp approach and Debian’s Bridging Network Connections with Proxy ARP are the primary sources for how I got this working and are the inspiration for this guide.

Bridged clients connected to the Pi should behave as if they were connected directly to the upstream network. This is based off a very helpful Stack Overflow answer.

Some network configurations will not allow these instructions to work. This guide and all related works come with no guarantee. Your mileage may vary.

For several months around the start of 2024 this guide was modified to work with the default NetworkManager stack on the latest Raspberry Pi OS. I’ve since found NetworkManager to be heavy-handed and it proved unreliable for the sake of this specific guide. The latest iteration of this script turns off NetworkManager in favor of dhcpcd with its wpa_supplicant hook. This is the only configuration I’ve found works reliably.

Step 1: Connect to WiFi on the Pi as directed in the official documentation.

Step 2: Save this script as a file named bridge.sh on the Pi.

#!/usr/bin/env bash

set -e

[ $EUID -ne 0 ] && echo "run as root" >&2 && exit 1

##########################################################
# You should not need to update anything below this line #
##########################################################

# parprouted       - Proxy ARP IP bridging daemon
# dhcp-helper      - DHCP/BOOTP relay agent
# dhcpcd           - Roy Marples' DHCP client daemon
# systemd-resolved - systemd network name resolution

apt update && apt install -y parprouted dhcp-helper dhcpcd systemd-resolved

systemctl stop dhcpcd dhcp-helper systemd-resolved
systemctl enable dhcpcd dhcp-helper systemd-resolved

# Enable the wpa_supplicant hook for dhcpcd.
ln -s /usr/share/dhcpcd/hooks/10-wpa_supplicant /usr/lib/dhcpcd/dhcpcd-hooks/

# Enable ipv4 forwarding.
sed -i'' s/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/ /etc/sysctl.conf

# Disable dhcpcd control of eth0.
grep '^denyinterfaces eth0$' /etc/dhcpcd.conf || printf "denyinterfaces eth0\n" >> /etc/dhcpcd.conf

# Configure dhcp-helper.
cat > /etc/default/dhcp-helper <<EOF
DHCPHELPER_OPTS="-b wlan0"
EOF

# Import the NetworkManager wifi connection profile.
ssid="$(grep -r '^ssid=' /etc/NetworkManager/system-connections/preconfigured.nmconnection | awk '{print $2}' FS='[=]')"
psk="$(grep -r '^psk=' /etc/NetworkManager/system-connections/preconfigured.nmconnection | awk '{print $2}' FS='[=]')"

cat << EOF > "/etc/wpa_supplicant/wpa_supplicant.conf"
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US

network={
  ssid="${ssid}"
  psk=${psk}
  key_mgmt=WPA-PSK
}
EOF

# Enable avahi reflector if it's not already enabled.
sed -i'' 's/#enable-reflector=no/enable-reflector=yes/' /etc/avahi/avahi-daemon.conf
grep '^enable-reflector=yes$' /etc/avahi/avahi-daemon.conf || {
  printf "something went wrong...\n\n"
  printf "Manually set 'enable-reflector=yes in /etc/avahi/avahi-daemon.conf'\n"
}

# I have to admit, I do not understand ARP and IP forwarding enough to explain
# exactly what is happening here. I am building off the work of others. In short
# this is a service to forward traffic from WiFi to Ethernet.
cat <<'EOF' >/usr/lib/systemd/system/parprouted.service
[Unit]
Description=proxy arp routing service
Documentation=https://raspberrypi.stackexchange.com/q/88954/79866
Requires=sys-subsystem-net-devices-wlan0.device dhcpcd.service
After=sys-subsystem-net-devices-wlan0.device dhcpcd.service

[Service]
Type=forking
# Restart until wlan0 gained carrier
Restart=on-failure
RestartSec=5
TimeoutStartSec=30
# clone the dhcp-allocated IP to eth0 so dhcp-helper will relay for the correct subnet
ExecStartPre=/bin/bash -c '/sbin/ip addr add $(/sbin/ip -4 -br addr show wlan0 | /bin/grep -Po "\\d+\\.\\d+\\.\\d+\\.\\d+")/32 dev eth0'
ExecStartPre=/sbin/ip link set dev eth0 up
ExecStartPre=/sbin/ip link set wlan0 promisc on
ExecStart=-/usr/sbin/parprouted eth0 wlan0
ExecStopPost=/sbin/ip link set wlan0 promisc off
ExecStopPost=/sbin/ip link set dev eth0 down
ExecStopPost=/bin/bash -c '/sbin/ip addr del $(/sbin/ip -4 -br addr show wlan0 | /bin/grep -Po "\\d+\\.\\d+\\.\\d+\\.\\d+")/32 dev eth0'

[Install]
WantedBy=wpa_supplicant.service
EOF

systemctl disable NetworkManager

systemctl daemon-reload
systemctl enable parprouted

Step 3: Run the script on the Pi.

sudo bash bridge.sh

Step 4: Reboot the Pi and plug it in to the non-WiFi computer using an ethernet cable.

Done!

It may take a moment for the Pi to connect to WiFi, but once it does (and on subsequent reboots) it should be able to start forwarding traffic over the ethernet port.

Conclusion

I ran some very basic speed tests for my desktop connected through the Raspberry Pi bridge and was pleasantly surprised by the results.

I cannot guarantee how reliable this is for every possible use case, but it works well for me.

$ iperf3 --reverse --format m --version4 --client iperf.he.net
Connecting to host iperf.he.net, port 5201
Reverse mode, remote host iperf.he.net is sending
[  6] local 10.1.1.187 port 52264 connected to 216.218.227.10 port 5201
[ ID] Interval           Transfer     Bitrate
[  6]   0.00-1.00   sec  2.06 MBytes  17.3 Mbits/sec
[  6]   1.00-2.00   sec  7.57 MBytes  63.5 Mbits/sec
[  6]   2.00-3.00   sec  8.46 MBytes  71.0 Mbits/sec
[  6]   3.00-4.00   sec  8.48 MBytes  71.2 Mbits/sec
[  6]   4.00-5.00   sec  8.49 MBytes  71.2 Mbits/sec
[  6]   5.00-6.00   sec  6.32 MBytes  53.0 Mbits/sec
[  6]   6.00-7.00   sec  8.54 MBytes  71.6 Mbits/sec
[  6]   7.00-8.00   sec  8.06 MBytes  67.6 Mbits/sec
[  6]   8.00-9.00   sec  6.92 MBytes  58.0 Mbits/sec
[  6]   9.00-10.00  sec  7.73 MBytes  64.9 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  6]   0.00-10.00  sec  77.6 MBytes  65.1 Mbits/sec  809             sender
[  6]   0.00-10.00  sec  72.6 MBytes  60.9 Mbits/sec                  receiver

iperf Done.

speed results