PiFi: The poorman's Ethernet-to-WiFi adapter

Recently I had a need to perform some reasonably in depth traffic shaping and monitoring for one of my desktop systems under a variety of software configurations. Ordinarily I would have broadened my knowledge of iptables/nftables a little and done the processing under my usual Linux setup. However in this case I needed to analysis traffic from my Windows installation as well.

Fortuitously I had a `spare' Raspberry Pi 3 laying about which I could draft into service as an overly complicated, though intelligent, wireless adapter. This allowed me to run all my monitoring processes on the Pi regardless of what operating system was booted or (mostly) how it was configured.

Previously my traffic flowed directly via WiFi from my desktop to the AP:

Desktop to AP

We will connect the Pi to the WiFi and construct a second network behind the Pi. Ideally we would simply bridge the traffic from the Pi’s ethernet to the Pi’s WiFi however most of the advice I saw suggested that many access points will drop traffic from devices that forward packets without rewriting the MAC addresses. This leaves us with the following topology:

Desktop to Pi to AP

Pi

We will connect the wireless adapter on the Pi as per usual; it will automatically bring this up at boot and associate. The ethernet adapter will use a fixed address on a new internal network it defines.

wlan0

The details of the wireless configuration are largely uninteresting but shown here for completeness. Feel free to use whatever mechanism your system provides.

Description="Home"
Interface=wlan0
Connection=wireless
Security=wpa
IP=dhcp
ESSID=homewifi
Key=hunter2

eth0

The important detail for the ethernet device are the fixed address (anything will do). I chose the 10.0.0.0/24 because it isn’t used on any network I frequent.

# Put it on an internal subnet
[Match]
Name=eth0

[Network]
Address=10.0.0.1/24
Gateway=10.0.0.0

Forwarding

At this point any device behind the Pi’s ethernet should be able to connect to it if a static configuration is made. But it still needs to be told how to forward packets between the interfaces.

IP

We configure persistent sysctl variables to forward IPv4 and IPv6 packets between any interface.

net.ipv4.ip_forward=1
net.ipv6.default.forwarding=1
net.ipv6.conf.all.forwarding=1

iptables

Only three iptables rules are required.

iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
iptables -A FORWARD -i wlan0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT
  1. masquarade rewrites packets so they appear to come from the Pi using some standard NAT tricks.
  2. forward allows packets which are part of ongoing connections to be passed through to the ethernet.
  3. forward allows any packets from the ethernet to be sent to the AP.

routing

I use a simple static route to ensure all translated traffic destined for the ethernet device is sent to that device. Depending on how you configured the ethernet link this may not be required.

ip route add 10.0.0.0/24 dev eth0

dnsmasq

For convenience sake I created a DHCP server that listens on the ethernet device. I chose dnsmasq because it was present on Arch and Gentoo, and it’s reasonably well documented.

interface=eth0
server=8.8.8.8
dhcp-range=10.0.0.1,10.0.0.128,12h

It is critical that the address that has been assigned to eth0 form part of the range specified in 'dhcp-range' otherwise dnsmasq will silently drop all requests.

Desktop

The setup on the desktop side should be remarkably straightforward and should simply be a matter of request DHCP setup. Feel free to use whatever mechanism your system supports; I used systemd as below.

# As per-usual

[Match]
Name=en*

[Network]
DHCP=yes

Testing

I found that debugging routing isues was greatly simplified with judicious use of tcpump and ping.

We can probably connect to SSH on at least one of the interfaces of the Pi. Running tcpdump icmp on the Pi will echo the detials of all ping packets that traverse the system. Pinging from the desktop system using ping -I eth0 8.8.8.8 allowed me to detect whether the ping was being forwarded to the internet, or whether it was being filtered on the return journey.