Raspberry Pi as WiFi Bridge

Sometimes, you need to provide network access to wired devices through a WiFi network. If you have an unused Raspberry Pi (e.g. a ver. 3B+, which is what I used for this guide), it can be used for this very purpose.

Why this page? Well, it seems there is a handful of guides out here on WWW on how to do this, but all I have found only show how to set things up manually, hackishly, through custom scripts and not in a reboot-and-it-will-still-work way. Furthermore, they seem to be outdated in many ways. This guide aims to set things up using regular tools and frameworks, in a way that will also survive a reboot.

It is assumed that you are running a standard Raspbian (32 or 64 bit doesn't matter).

Alt. 1: Router with NAT and DHCP

Here we let the WiFi be the WAN and the RPI's Ethernet NIC serve an internal network with DHCP and then do NAT.

Set up the WiFi

Use one of various methods to configure the WiFi. Having done that, you will have (at least) one entry in /etc/wpa_supplicant/wpa_supplicant.conf which is the place to keep WiFi configuration. (E.g. setting it in /etc/network/interfaces would limit you to support only 1 wireless network and you might to want several configured.)

Enable IP forwarding

To enable IP forwarding in a persistent way that will survive reboots, edit /etc/sysctl.conf and make sure you have the entry:

net.ipv4.ip_forward=1

Run sysctl to apply the update:

# sysctl -p

Verify that it worked by making sure /proc/sys/net/ipv4/ip_forward contains 1:

# cat /proc/sys/net/ipv4/ip_forward
1

/etc/network/interfaces

Find out your device's NIC names, e.g. by running ifconfig or ip:

# ifconfig
# ip link show

On contemporary (when this was written) versions of Raspbian, the WiFi NIC has the name wlan0 whereas the Ethernet NIC gets a name according to the Predictable Interface Names naming scheme: enx<macaddress>. (E.g. enxb8ac6fc0ffee; if you truly hate the Predictable Interface Names you can configure udev to go with the old school ethN scheme.)

Edit /etc/network/interfaces to configure your NICs:

auto wlan0
iface wlan0 inet dhcp
	wpa-driver nl80211
	wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
	up iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE
	down iptables -t nat -D POSTROUTING -o $IFACE -j MASQUERADE
	metric 0

auto enxb8ac6fc0ffee
iface enxb8ac6fc0ffee inet static
	address 192.168.0.1
	netmask 255.255.255.0
	up iptables -A FORWARD -i wlan0 -o $IFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
	up iptables -A FORWARD -i $IFACE -o wlan0 -j ACCEPT
	down iptables -D FORWARD -i $IFACE -o wlan0 -j ACCEPT
	down iptables -D FORWARD -i wlan0 -o $IFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
	metric 10

For wlan0, we tell it to use the nl80211 WiFi driver and the WPA supplicant configuration to connect to a suitable WiFi network. We expect to get our network info via DHCP. Furthermore, we activate postrouting when the interface is brought up, and then clean up that very rule when the interface is taken down. The $IFACE variable expands to the name of the interface, i.e. wlan0 here. To prevent the system from routing Internet traffic on the Ethernet interface, we set the metric 0 to show that wlan0 is the most prioritized NIC.

For the Ethernet interface, we set the static IP address 192.168.0.1 and the netmask 255.255.255.0. Furthermore, we set up forwarding IP rules that will allow your local devices to contact whatever, but devices from the outside will only be able to reach local devices that have made the first move, which is good ol' best practice for these setups. The down entries make sure the iptables rules are properly cleaned up when the interface is taken down. The $IFACE variable expands to the name of the interface, i.e. enxb8ac6fc0ffee here. Finally, we set the metric 10 (could have been any number >0) to make sure that wlan0 is the main NIC for routing traffic.

Set up DHCP

I use the isc-dhcp-server package for DHCP:

# apt-get install -y isc-dhcp-server
Configure it with /etc/dhcp/dhcpd.conf.
# Must be set to get the clients' /etc/resolv.conf right, but can be anything
option domain-name "my.domain";
# Use Google's DNS servers, they work everywhere
option domain-name-servers 8.8.4.4, 8.8.8.8;

# These lines are probably already enabled in the default config
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none;

# Declare your subnet, must match your static configuration for the Ethernet
# NIC in /etc/network/interfaces
subnet 192.168.0.0 netmask 255.255.255.0 {
	option routers 192.168.0.1;
	option subnet-mask 255.255.255.0;
	range 192.168.0.2 192.168.0.254;
	allow unknown-clients;
}

Add your Ethernet NIC to the INTERFACESv4 entry in /etc/default/isc-dhcp-server

INTERFACESv4="enxb8ac6fc0ffee"

Fire up the DHCP server:

# systemctl restart isc-dhcp-server

Verify that the DHCP server is up and running:

# systemctl status isc-dhcp-server

Apply your new network configuration

Run

# systemctl restart networking

and your device should be up and running (and work just the same even after a reboot).

Alt. 2: Bridge

Another variant would of course to bridge the WiFi NIC and the Ethernet NIC. In theory, that would be quite convenient and only a quick brige setup with the WiFi NIC and the Ethernet NIC. But it is not as simple as that. Actually, things get ugly and complicated pretty fast with this approach, so I will simply recommend you to go with the first alternative.