The goal is to connect a non-WiFi computer to a WiFI network via 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 using Ethernet.

diagram of the network topology

The Raspberry Pi 4 Model B has 802.11ac WiFi, and so seems well suited to this task. 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.

I have two separate guides below. Follow either the Same Subnet steps or the Separate Subnet steps below based on the configuration you would prefer. My process has evolved over time and I prefer the Same Subnet approach as it is meant to be a seamless bridge for clients.

These instructions were only tested and verified on a fresh install of 2021-01-11-raspios-buster-armhf-lite.img, which is the headless version of Raspberry Pi OS. It is reccomended to use a fresh install of Raspberry Pi OS or to back up your Pi before attempting this.

Some routers and networks will not allow these instructions to work. Some networks will actively block devices from sharing network access. Your mileage may vary. If one option fails to work reliably, please try the other.

This option is complex, but provides a more seamless experience. Bridged clients connected to the Pi should behave as if they were connected directly to the upstream network.

The following script configures everything in one go for a Pi with a standard Raspberry Pi OS install. This script is based off a very helpful Stack Overflow answer.

Step 0: Connect to WiFi on the Pi like you normally would.

Step 1: Save this script as a file named bridge.sh on your 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 apt update && apt install -y parprouted dhcp-helper systemctl stop dhcp-helper
systemctl enable dhcp-helper # Enable ipv4 forwarding.
sed -i'' s/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/ /etc/sysctl.conf # Service configuration for standard WiFi connection. Connectivity will
# be lost if the username and password are incorrect.
systemctl restart wpa_supplicant.service # Enable IP forwarding for wlan0 if it's not already enabled.
grep '^option ip-forwarding 1$' /etc/dhcpcd.conf || printf "option ip-forwarding 1\n" >> /etc/dhcpcd.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 # 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 daemon-reload
systemctl enable parprouted
systemctl start parprouted dhcp-helper

Step 2: Execute the script on your Pi like so.

sudo bash bridge.sh

Step 3: Reboot.

sudo reboot

Done!

It may take a moment for your 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.

This option is simpler than the first option. However, this option results in a more limited setup.

Bridged clients will be on a separate subnet so the network configuration may not work like you expect. This option is fine if all you can about is connecting a bridged client to the Internet. Note that this script is a bit opinionated and chooses DNS servers and the subnet IP range for you. Update the script as needed.

Step 0: Connect to WiFi on the Pi like you normally would.

Step 1: Save this script as a file named bridge.sh on your Pi.

#!/usr/bin/env bash set -e [ $EUID -ne 0 ] && echo "run as root" >&2 && exit 1 apt update && \ DEBIAN_FRONTEND=noninteractive apt install -y \ dnsmasq netfilter-persistent iptables-persistent # Create and persist iptables rule.
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
netfilter-persistent save # Enable ipv4 forwarding.
sed -i'' s/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/ /etc/sysctl.conf # The Ethernet adapter will use a static IP of 10.1.1.1 on this new subnet.
cat <<'EOF' >/etc/network/interfaces.d/eth0
auto eth0
allow-hotplug eth0
iface eth0 inet static address 10.1.1.1 netmask 255.255.255.0 gateway 10.1.1.1
EOF # Create a dnsmasq DHCP config at /etc/dnsmasq.d/bridge.conf. The Raspberry Pi
# will act as a DHCP server to the client connected over ethernet.
cat <<'EOF' >/etc/dnsmasq.d/bridge.conf
interface=eth0
bind-interfaces
server=8.8.8.8
domain-needed
bogus-priv
dhcp-range=10.1.1.2,10.1.1.254,12h
EOF systemctl mask networking.service

Step 2: Execute the script on your Pi like so.

sudo bash bridge.sh

Step 3: Reboot.

sudo reboot

Done!

It may take a moment for your 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

You should now be able to connect a device to the ethernet port on the Raspberry Pi and receive an IP address.

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