I'm trying to understand the physdev iptables module, and I'm having problems with nat table, especially with the POSTROUTING chain. So my goal is to SNAT outgoing traffic on bridge interface using physdev module.
Here is my mini network setup: PC1 - (enp6s0f0)BRIDGE(enp6s0f1) - PC2. PC1 is 192.168.100.1/24 and PC2 is 192.168.100.2/24.
My bridge setup is:
ifconfig enp6s0f0 down
ifconfig enp6s0f1 down
brctl addbr br0
brctl addif enp6s0f0 br0
brctl addif enp6s0f1 br0
ifconfig enp6s0f0 0.0.0.0 up
ifconfig enp6s0f1 0.0.0.0 up
ifconfig br0 169.254.100.1/32 up
this bridge setup works fine and PC1 and PC2 can communicate with each other and not with the bridge as expected.
Now, for example, I want to setup communication between PCs and the BRIDGE in such a way that PC1 sees BRIDGE as 192.168.100.100 and PC2 sees BRIDGE as 192.168.100.200.
So I add the route:
route add -net 192.168.100.0/24 dev br0
and now if I ping from BRIDGE to any of PCs, I can see outgoing traffic from bridge with source ip 169.254.100.1 as expected.
Now it's all about iptables. My rules:
iptables -t nat -A POSTROUTING -s 169.254.100.1 -m physdev --physdev-out enp6s0f0 -j SNAT --to-source 192.168.100.100
iptables -t nat -A POSTROUTING -s 169.254.100.1 -m physdev --physdev-out enp6s0f1 -j SNAT --to-source 192.168.100.200
After this I expect all traffic outgoing from the bridge to be SNATed to 192.168.100.100 or 192.168.100.200 depending on physdev-out, BUT this doesn't happen. Why? idk. What happens is that the rules are ignored. iptables -t nat -vnL shows 0 packets match the rule and tcpdump -i enp6s0f0 -p arp -n shows all arp requests have the old 169.254.100.1 source ip address.
It is also important to note that I have br-netfilter module loaded and bridge-nf-call-iptables set to 1.
The operating system I'm using is Ubuntu 20.04, but I tried others too. I also looked for information on the Internet, and I know there are several other ways to achieve the behavior I want, but I want to figure out why this particular method doesn’t work.
I also looked at https://ebtables.netfilter.org/br_fw_ia/br_fw_ia.html and after reading it I have a feeling that everything should work. Pls help mee
AFAICT,
--physdev-out
can only be used match traffics that are "bridged", i.e., traffics that are originated from another host and has entered the bridge via a bridge port. Traffics originated from the "bridge host" itself, and those from another host but are being L3-forwarded (i.e., those that have entered the "bridge host" from some interface and are leaving through the bridge interface), are not considered "bridged traffics". (It seems to me that it is some kind of technical limitation or design flaw, instead of a bug. You can see in the man page that theINPUT
chain is mentioned for--physdev-in
, while theOUTPUT
chain is not mentioned for--physdev-out
.)I'm not sure what exactly you are trying to do here, since normally it makes little to no sense to have different addresses on one host for the same network it attach to, especially when you are using one IP subnet and attempting to leverage source NAT for your goal. However, to achieve what you apparently want, the best/only way I could think of is leveraging netns and a veth pair. (Each netns is considered a host network-wise.)
I am not entirely sure if moving physical ethernet interfaces to a new netns is always possible, but assuming it is, you can create a netns and create the bridge inside it, then move the the ethernet interfaces that you want to make bridge ports/slaves to the netns and set their master there. Finally create a veth "pair" and move one "end" of the "pair" (e.g. the
peer
) to the netns and enslave it to the bridge as well. Instead of configuring IP address and routes on the bridge, configure them on the "end" of the veth "pair" that is in the "default" netns (i.e., the "end" that is not a bridge slave/port).Obviously the SNAT rule should be added to the iptables of the netns where the bridge resides.
See also
ip-link(8)
andip-netns(8)
.