Bypassing At&t U-verse hardware NAT table limits

At&t U-verse comes with a vendor-supplied router that is designed for home-use, even when having a "small business" contract. This shows in the router's config interface, designed for non-tech-savvy people, but also in terms of a bunch of limitations. The main problem, especially when used in a business context, is a limitation of the router's NAT table, capped at 2048 sessions (with the At&t support claiming already flaky behaviour when over 1000). Not sure why At&t, defining small business as up to 50 people, is even selling this, as a lot of them will end up calling support very soon, complaining about intermittent packet loss.

The box provides some passthrough modes for people that run their own routers, but although it sounds like this would easily avoid that limitation, it doesn't. For whatever not-so apparent reason, all of those passthrough modes actually just route, still filling up the NAT table. Some older firmwares had an exploitable vulnerability, which allowed to root some models and enable a true bridge mode, but newer versions plugged this; and ideally we would like to have a solution for a business environment, that doesn't need tampering with At&t's equipment, anyways.

Looks like many others are having the same problem (across different router models), debating workarounds, but no real hands-off solution was found that doesn't involve to either root the router or having to re-rig things occasionally.

One solution that does work and is easy to set up is to tunnel all the office's outgoing traffic, exactly generating one entry in the At&t box' NAT table. However, although a valid solution, this post will try to focus on an internal-network-only workaround. Also, such a tunnel wouldn't necessarily fix the problem for inbound connections, either.

Why can't we just use our own hardware? Well, the U-verse home-use uplinks require 802.1X authentication, with therefore a certificate that is on the router. It additionally sends every 24h a CWMP periodic inform message to At&t, which we probably should keep sending, also. I think it would be possible to open the router and dump the box' ROM contents to get a hold of the cert, then reimplement the logic on a better box. Or, if the limitation is software based, we could even attempt flashing the router with a modified version, as the firmwares used seem to be all open source and available (excluding the cert, of course). However, both approaches would be tampering with their equipment.

All of this means, that since we don't want to tamper with the router, it has to stay part of the equation.

So, let's try to have the At&t router still connected to the uplink to do the authentication and heartbeat, but not pass any real traffic through it, that doesn't need to. Basically, we want the following:

                 ┌──── magic ────┐
                 │               │
                 │             At&t
                 │            router

Note that the traffic that goes from uplink to the intranet does not pass through the At&t box, at all. The latter will simply stay attached to the uplink (whether you use fiber, ONT, etc., in my case it was a fiber cable), but that's the only link connected to it. No other cable will be attached to it.

So, magic now has to split the traffic into:

  • all 802.1X/EAP and management traffic should be allowed to and from the At&t box
  • everything else should flow between uplink and the intranet

Let's hook up a box in between the At&t router and the uplink, first, so we can intercept the traffic. It doesn't really matter what type of box it is, but needs to be more programmable/flexible than your standard managed switch's or router's vendor GUI/CLI. In my case, an Ubiquiti EdgeRouterPro was used, as it allows full Linux shell access.

So, with the At&t box connected to... let's say eth6, and the uplink connected to eth7, let's just bridge those interfaces (br0), so the At&t box can talk to the uplink. Turns out that the 802.1D standard (which is about bridging), says that standards compliant bridges aren't supposed to pass MAC addresses in the range of 01:80:c2:00:00:00 to 01:80:c2:00:00:0f. 802.1X uses 01:80:c2:00:00:03, so it's effectively not going through the bridge. So much about the general assumption that a bridge is just a "virtual switch"... not.

Anyways, looks like we can enable this on our bridge br0, so if this is a >=3.2 kernel:

echo 8 > /sys/class/net/br0/bridge/group_fwd_mask

If this is a pre-3.2 kernel, some folks maintained a patch to achieve the same.

With this out of the way, the At&t router, with an uplink connection going through magic, should now sync with the At&t uplink just fine (and all sync/broadband LEDs turn steady green).

On to the next step. What's basically missing is to now direct all incoming traffic to the correct next hop, depending on whether it's for the At&t box itself (802.1X, management traffic, etc.), or else. This is luckily fairly easy to figure out, on paper. The way this is setup for a U-verse "small business" contract that comes with 5 public IPs, is the following:

  • the At&t box gets provisioned with a static IP, which the support guy called "street IP", which "usually doesn't change, except if the phyisical wiring changes or something like that"; note that that IP is different from our 5 public ones
  • this is the IP assigned to the box itself, so everything sent to the box itself is using that one
  • the rest, with one of our 5 public IPs as destination, is also sent to the At&t box, for further routing
  • in other words, everything from the outside is passed to the At&t box, as either destination or to be routed for our public IP subnet; this means in layer 2 terms, that every incoming ethernet frame has the At&t box' MAC as destination address
  • for outgoing traffic, everything coming from the intranet needs to go directly to the uplink (which sounds obvious, but given that the gateway address for our public IP block ARPs to the At&t router, we also need a rule on magic to redirect those packets, so they actually go out and don't get dropped on the WAN interface of the At&t router)

Note that the last bullet point mentioned the gateway address - as a sidenote here: we will use the same gateway address with this setup as we would by using the At&t box the default way (running all traffic through it). This will be the address used by the intranet as internet gateway. This address can be looked up in the At&t box' configuration, if you don't know it - it definitely has to match the one in the configuration, though, or the below won't work. The benefit here is that this allows for removing magic at any time from the setup, replugging everything in the old way, and it will work (except for then having NAT table limitations again).

So, what we want to do is to filter on layer 2 by matching on destination IP address, and rewrite the MACs to either go to the At&t box, or to the intranet. Well, for the latter, we need a destination now, so we need a box there, somewhere, as router, with one of our public IPs set. This would be your main firewall (in my case it runs simply on the EdgeRouterPRO, also, but virtually separated):

                 ┌──── magic ────┐
                 │               │
              router/fw        At&t
                 │            router

We can use ebtables to do the traffic-splitting, as this is layer 2 logic. Unfortunately, there is another stumbling block. At&t seems to use (not sure if always) a VLAN, here, probably per customer.
The current version of ebtables can either match layer 3 addresses but not VLAN tags, or vice versa, but not both at the same time. So we need to strip the VLAN/802.1Q tag from the ethernet frames, first, to be able to make use of ebtables. To do that, we need to figure out the tag they assigned to us, first.

So, on magic's eth7, run a tcpdump with -e, and make sure some traffic goes through there (e.g. plug a machine into the LAN ports of the At&t router, and visit a website, or so):

tcpdump -ei eth7

Output will be something like this, with the VLAN tag displayed

16:36:56.631607 10:20:30:40:50:60 (oui Unknown) > 60:50:40:30:20:10 (oui Unknown), ethertype 802.1Q (0x8100), length 147: vlan 2, p 0, ethertype IPv4, > UDP, length 101

So, in our case it's VLAN 2 that At&t assigned to us. Let's create VLAN interfaces eth6.2 and eth7.2, and also add them to br0. Now we can run ebtables rules on eth7.2 with matching of IP address, as this interface receives the incoming eth7 traffic with the VLAN tag removed. Before we get to the rules, we need to additionally add to our bridge the interface that links to the intranet. Let's say this is eth5, so add eth5 to br0 for our example. Then for ebtables:

# This dnats destination MAC addresses on eth5 for our public IP subnet to the uplink MAC.
ebtables -t nat -A PREROUTING -p IPv4 -i eth5 --ip-src $OUR_PUB_IP_RANGE -j dnat --to-dst $MAC_ATT_UPLINK --dnat-target ACCEPT

# This will set the firewall box' MAC as destination for packets coming from the ISP. Do
# it on eth7.vlan, so ebtables can match on IP (wich it can only on packets with VLAN
# tag stripped).
ebtables -t nat -A PREROUTING -p IPv4 -i eth7.2 --ip-dst $OUR_PUB_IP_RANGE -j dnat --to-dst $MAC_INTERNALFW --dnat-target ACCEPT

# This snats source MACs that go to the uplink (all of them) to the at&t box' MAC,
# to spoof it coming from there. This makes the packet go out to the ISP.
ebtables -t nat -A POSTROUTING -d $MAC_ATT_UPLINK -j snat --to-src $MAC_ATT_RG_WAN --snat-target ACCEPT

As you can see, there are some blanks to fill in, namely the following variables:

OUR_PUB_IP_RANGEthe public IP block given to you from At&t, e.g.
MAC_INTERNALFW MAC address of interface connected to eth5, the interface of the internal firewall box
MAC_ATT_RG_WAN MAC address of the At&t box' WAN interface; usually printed on At&t box' back
MAC_ATT_UPLINK MAC address of uplink hop's equipment, see below

In order to figure out MAC_ATT_UPLINK, which is the MAC address of the device at the other end of the cable coming out of your wall, you can use the following on magic:

brctl showmacs br0

This will list all interfaces that are part, or directly attached to br0:

port no mac addr                is local?       ageing timer
  5     44:77:44:77:44:77       no                 0.00
  1     10:20:30:40:50:60       no                 0.00
  1     00:11:77:44:22:05       yes                0.00
  2     00:11:77:44:22:06       yes                0.00
  4     00:11:77:44:22:07       yes                0.00
  3     a4:7a:77:a7:7a:77       no                44.82

Filtering out the three local addresses, which are the bridge interfaces eth5, eth6 and eth7 (note that eth6.2 and eth7.2 use the same MACs as eth6 and eth7), three others are left. Two of those are addresses we know, namely the firewall's (here: 10:20:30:40:50:60) and the At&t box' MAC (let's say this is a4:7a:77:a7:7a:77). The only one left is 44:77:44:77:44:77 in our example, which is the one we are looking for. (If you have more than one remaining line, this might come from having had something else plugged in, which the bridge learned. In that case, just check the ageing timer column for an ever increasing value, and let it time out. Eventually there should be only one address left.)

Now, put together your ebtables rules and give it a test.

This is basically it. You might want to think of the following though:

  • At&t might change the uplink hardware, so MAC_ATT_UPLINK might change; so it's a good idea to have your brctl showmacs br0 in some cronjob to update the uplink MAC in case of it changing
  • theoretically, At&t might also change the VLAN id, however, I don't think this is realistic as it's already an abstraction and easier to keep for them than when replacing hardware
  • be aware that every time you destroy and recreate br0, the group_fwd_mask needs to be reset to let 802.1X traffic pass

UPDATE (2016-07-22): new methods to root the At&t box were discovered, in the meantime (see comments, below); so the statement in the introduction doesn't fully hold, anymore

UPDATE (2016-10-02): as reported by others in the comments below, there are uplink setups not using any VLAN tagging, but still using 802.1Q frames with special value of 0 for the VLAN ID, using it as a priority tag, only. See comments below for more info.