Linux – How to route packets from bridged network interfaces to localhost

bridgeiptableslinuxnetworking

I am setting up an Orange Pi to bridge two network interfaces. One of them is connected to a gateway, the other to the network interface of a thermal receipt printer. I would like to capture packets send from the network which are destined for the printer on port 9100 so I can modify them before sending them to the printer.

My hardware setup looks as follows: eth1 is connected to the gateway, eth2 is connected to a thermal receipt printer with static ip 192.168.0.20.

On my Orange pi I run Linux 4.18.7, build from Yocto with the OpenEmbedded board support package.

I take the following steps to initalize a bridge:

brctl addbr kc_bridge
brctl addif kc_bridge eth1
brctl addif kc_bridge eth2
ifconfig kc_bridge up

At this point I can ping the printer from any device in the same network, and send it things to print (e.g. echo "Hello World" | nc 192.168.0.20 9100)

Then, I enable routing to localhost, enable ip forwarding and modprobe br_netfilter to enable routing on bridges:

echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
echo 1 > /proc/sys/net/ipv4/ip_forward
modprobe br_netfilter

Next I add the iptables rule that should change the destination address to localhost of any packet that is destined for the printer on port 9100, and I start listening to this port:

iptables -t nat -A PREROUTING -d 192.168.0.20/32 -i kc_bridge -p tcp --dport 9100 -j DNAT --to-destination 127.0.0.1:9100
nc -l 9100

I would expect this to show any data that is routed to localhost, but when I send data to the printer from another device on the same network, it is neither shown here nor printed.

Using tcpdump I can see that the packets are not routed to localhost and no connection seems to be initiated:

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
09:31:14.821538 IP 192.168.0.50.43362 > 192.168.0.20.9100: Flags [S], seq 2236984100, win 64240, options [mss 1460,sackOK,TS val 3578716123 ecr 0,nop,wscale 7], length 0
09:31:15.839876 IP 192.168.0.50.43362 > 192.168.0.20.9100: Flags [S], seq 2236984100, win 64240, options [mss 1460,sackOK,TS val 3578717141 ecr 0,nop,wscale 7], length 0
09:31:17.856296 IP 192.168.0.50.43362 > 192.168.0.20.9100: Flags [S], seq 2236984100, win 64240, options [mss 1460,sackOK,TS val 3578719157 ecr 0,nop,wscale 7], length 0
09:31:22.020901 IP 192.168.0.50.43362 > 192.168.0.20.9100: Flags [S], seq 2236984100, win 64240, options [mss 1460,sackOK,TS val 3578723321 ecr 0,nop,wscale 7], length 0

What am I missing in order to route packets from the bridged network interfaces to localhost?

Update

Using iptables command I can confirm that the iptables rules are being hit:

iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 400 packets, 77939 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    6   360 DNAT       tcp  --  kc_bridge *       0.0.0.0/0            192.168.0.20         tcp dpt:9100 to:127.0.0.1:9100

Chain INPUT (policy ACCEPT 2 packets, 120 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 291 packets, 25966 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 405 packets, 49112 bytes)
 pkts bytes target     prot opt in     out     source               destination   

Packets are accepted on all ports:

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -p tcp -m tcp --dport 9100 -j ACCEPT

Best Answer

  1. Check the counters of the DNAT rule with iptables-save -c -t nat command. It shouldn't be zero.
  2. Also you should allow the incoming packets for this port.
iptables -A INPUT -p tcp --dport 9100 -j ACCEPT
  1. Use the conntrack tool to list the conntrack table. You can use conntrack -E to monitor conntrack events in the real time.
  2. Check output of ip route get 127.0.0.1 from 192.168.0.50 iif kc_bridge command.