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:
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:
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.
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
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
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.
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
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
masquaraderewrites packets so they appear to come from the Pi using some standard NAT tricks.
forwardallows packets which are part of ongoing connections to be passed through to the ethernet.
forwardallows any packets from the ethernet to be sent to the AP.
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
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=220.127.116.11 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.
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
I found that debugging routing isues was greatly simplified with judicious use of
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 18.104.22.168 allowed me to detect whether the ping was being forwarded to the internet, or whether it was being filtered on the return journey.