Confusion using Iptables, nat and bridge

Today chatting via IRC I remembered a problem that I had some years ago with virtualization, iptables, nat and bridge. The situation of the guy asking was pretty similar. He has a one virtual machine (Qemu/KVM) connected to the world using a bridge and its default gateway is the virtualization host. He was trying to apply destination NAT to the VM in the host machine but it didn’t work. The rule was simple:

iptables -t nat -A POSTROUTING -s -o eth0 -j MASQUERADE

It is perfect, there is nothing wrong there but he never saw the packet in the POSTROUTING chain. Why? The quick answer is “packets don’t cross nat table twice”. There is a flag in the Linux bridge to enable filtering with Iptables. Packets go to Iptables in the kernel when they are forwarded by the bridge. This includes the NAT table.

In the bridging process, you don’t know the outgoing interface so the previous rule doesn’t work. He needs the interface because he’s using MASQUERADE. In the routing process, the packets go to iptables but they never cross NAT tables because the packet already crossed the table in the bridging process.

How can we fix this? There are two options I think:

  • echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables #To disable Iptables in the bridge.
  • Raw table: Some years ago appeared a new tables in Iptables. This table can be used to avoid packets (connection really) to enter the NAT table: iptables -t raw -I PREROUTING -i BRIDGE -s x.x.x.x -j NOTRACK.
If you still don’t understand why this happens, I’ll try to explain one more thing. If you have an scenario with Virtualization and you host is your gateway, the packets follow this steps: [VM]->[bridge]->[virtual interface]->[host]->[physical interface]->[net]. When they cross the host, you have the routing process there.

Post to Twitter

11 thoughts on “Confusion using Iptables, nat and bridge”

  1. >he never saw the packet in the POSTROUTING chain. Why? The quick answer is “packets don’t cross nat table twice”.

    Packets do pass through the NF_INET_POST_ROUTING hook; it’s just “nat” that is special (it’s a config db of sorts). I dunno where the double invocation stems from, but remember that if one and the same packet enters the host twice, and thus NFCT twice, there will be surprises. (Note lo is exempt because such packets never enter the host because they never leave the host.) Because you gave the user no time and just slapped bridge-nf-call-iptables=0 into the room and he subsequently went his ways, there was no time to reason more about his network topology, and he may now have a half “broken” iptables setup — I’m waiting for the moment he returns to make aware that $something does not function as it traverses FORWARD on the bridge layer.

  2. The double invocation happens when you have bridging+routing the same host. In real life, I’ve only seen this in virtualization when you have the host as gateway. The packet is processed by Iptables in the bridge (bridge-nf-call-iptables=1) and routing. There is no problem with his network topoligy.

    This scenario is easy to reproduce, just configure a virtualization host as gateway in one of your VMs and insert a LOG rule in the nat table.

  3. This information was very useful, thank you!

    I was seeing the same issue with the following virtualisation setup all on one host:

    [KVM VM] ---br1--- [Dynamips VM] ---br0(int IP) ; wlan0(ext IP) ---> outside

    KVM VM pointed defaultroute at Dynamips VM, and Dynamips VM pointed default route at the host’s internal IP on br0.

    Problem: pings from Dynamips VM to outside were correctly NAT’d, but pings from KVM VM were not being NAT’d, so left the outside interface with the original (useless) source IP.

  4. Thank you for posting this. I didn’t realize a bridge uses iptables internally.

  5. I’m trying to figure how why traffic is coming in, being port forwarded, and reaching the SSH service, but traffic isn’t returning back to the external IP

    I’m comparing a vanilla Ubuntu 16.04 install to Proxmox. Proxmox doesn’t have any iptable rules, doesn’t use ebtables, and have all bridge settings in `/proc/sys/net/bridge` disabled, yet port forwarding from Untangled running in a KVM in Proxmox works.

    Where as, on Ubuntu 16.04 with all iptables Filtering on bridge devices disabled, no ebtable rules, doesn’t work.

Leave a Reply

Your email address will not be published. Required fields are marked *