Thursday, September 18, 2008

DD-WRT client bridge mode

A couple of weeks ago we moved to a lovely beachside place in Black Rock. It's right on the beach, and you can see out to the bay.

But, the ADSL port is in the dining room and the study's at the other end of the house! To get a cable to the study, it'd have to go either through a wall (landlords don't like such things.. can't imagine why..) or else there'd be an ugly cable snaking around the place. NOBODY likes snakes in an apartment.

I had a few non-cabling options:

1. Buy a wireless bridge and connect to the network
2. Buy 2 powerline adapters and connect over power wiring
3. Buy a cheap router and run DD-WRT in client bridge mode (ie. a cheap wireless bridge)

Option 1 was expensive (80+ AUD) and designed to have 1 device on the bridged segment, so it was out. Option 2 (powerline adapters) was also expensive (120+ AUD) but apparently lag goes up to a whole second depending what's on the circuit, desk fans having the most noticeable effect. Not good for playing TF2!

The cheap router + DD-WRT ended up being the best option. So I bought a Dlink DIR-300, got it home, flashed DD-WRT and put it in client bridge mode. All good right?

Wrong. (cue hours of staring at wireshark and praying at the altar of google.)

From here on in it gets pretty complex. If you're trying to get your bridge to work, telnet or SSH into DD-WRT and run the "ebtables" commands below.

The client bridge mode, in its default configuration, only forwards broadcast frames originating outside the bridged segment (ie. originating from the wireless). This is bad because devices behind the bridge can never ask devices on the rest of the network for their MAC addresses! The network just falls in a heap because no-one can talk to each other.

(Irrelevant but interesting: if a device behind the bridge gets a chance to reply to a non-bridge device's ARP request, it can then reach that device. This is because when a device replies to ARP it takes the chance to add the counter-party to its own ARP cache. But there is only a few minutes of connectivity - pretty soon the entry expires from cache. Then we go back to waiting. Not a very efficient way to use a network, is it!)

When DD-WRT is in client bridge mode the bridged segment is hidden behind an "ARP NAT". This makes DD-WRT MAC address seem to own "many IP addresses". The IP addresses are all the IPs of the devices behind the bridge. To achieve this DD-WRT uses ebtables to modify both outgoing ethernet frames and outgoing ARP replies. The effect is very similar to IP NAT - while IP NAT alters network traffic so everything comes from one IP, MAC NAT alters network traffic so everything comes from one MAC address.

DD-WRT client bridge configuration is not quite right. Imagine a device outside the bridged segment sends a packet to a device inside the bridged segment. It will send with the destination MAC address pointing to DD-WRT. DD-WRT's job is to inspect the IP address, rewrite the destination MAC address (based on a stored mapping of IP addresses to MAC addresses - in other words the ARP cache) and forward the packet on the correct interface of the bridge (based on a stored mapping of MAC addresses to ports). But DD-WRT is greedy: it swallows the packets and doesn't send them on.

If your bridged devices are not seeing replies to their ARP requests, you can set up the other interfaces on the bridge as ARP NAT gateways. Add extra arpnat rules for the LAN interfaces that connect to your bridged devices. Here's how.

(note: I have WAN port disabled on my DD-WRT, which causes vlan1 interface to be down.)

# brctl show br0
bridge name bridge id STP enabled interfaces
br0 8000.001e58bcbd5b no vlan0 <-- add arpnat for vlan0 (normally on DIR-300 this is WLAN, and LAN port1-3)
vlan2 <-- add arpnat for vlan2 (normally on DIR-300 this is LAN port4)
ath0
# ebtables -t nat -A PREROUTING -i vlan0 -j arpnat --arpnat-target ACCEPT
# ebtables -t nat -A PREROUTING -i vlan2 -j arpnat --arpnat-target ACCEPT
# ebtables -t nat -A POSTROUTING -o vlan0 -j arpnat --arpnat-target ACCEPT
# ebtables -t nat -A POSTROUTING -o vlan2 -j arpnat --arpnat-target ACCEPT

Now DD-WRT is a 2-way ARP NAT bridge. Devices on each side of the bridge think every device on the other side shares a single MAC address. The single MAC address just happens to be that of (the nearest side of) the bridge.

With this enabled, the bridge becomes a very low bandwidth, expensive, "wireless wire"! Yes, wireless wires exist and they're darn useful too!