Iptables not working as expected

I can not for the life of me get --to-destination to work when creating a DNAT rule. Whether I try it with iptables (nft) or iptables (legacy).

This cmd:

iptables -t nat -A PREROUTING -d 10.7.0.2/32 -i client3 -p tcp -m tcp --dport 34 -j DNAT --to-destination 192.168.2.218:34

always yields:

iptables v1.8.7 (nf_tables): unknown option "--to-destination"
Try `iptables -h' or 'iptables --help' for more information.

I also see:


root@OpenWrt:~# iptables --table nat -A PREROUTING -j DNAT -i client3 -p tcp -m
tcp --dport 34
iptables v1.8.7 (nf_tables): Chain 'DNAT' does not exist

how is one to make the --to-destination example work from https://openwrt.org/docs/guide-user/firewall/fw3_configurations/fw3_nat

What version of OpenWrt are you using?

My version is 22.03.2

22.0 is using fw4, so the chain names are different than fw3. I think this is what you want:

$ nft add rule inet fw4 dstnat   ip saddr 10.7.0.2   tcp dport 34   dnat to 192.168.2.218:34

$ nft -a list chain inet fw4 dstnat
table inet fw4 {
        chain dstnat { # handle 19
                type nat hook prerouting priority dstnat; policy accept;
 ...
                ip saddr 10.7.0.2 tcp dport 34 dnat ip to 192.168.2.218:34 # handle 396
        }
}

You can remove it by observing the handle number (396 for my example), then:

$ nft delete rule inet fw4 dstnat handle 396

Edit: The '-a' switch turns on display of the handle numbers for all the rules. Use nft list chains to see all the chains.

1 Like

Thank you, will try this now and get back to you.

Doesn't look like I am seeing any traffic on my LAN host with this rule, yes I'm aware this would only allow one way communication but I should still see some form of event on my server.

Here is the output:

root@OpenWrt:~# nft -a list chain inet fw4 dstnat
table inet fw4 {
       chain dstnat { # handle 25
               type nat hook prerouting priority dstnat; policy accept;
               iifname "client3" jump dstnat_vpn comment "!fw4: Handle vpn IPv4/IPv6 dstnat traffic" # handle 101
               ip saddr 10.7.0.2 tcp dport 34 dnat ip to 192.168.2.218:34 # handle 114

So this is only redirecting destination addr when the saddr of packet header is 10.7.0.2?

In the rule I provided I believe it is rewriting the destination addr based on the destination addr, protocol, and interface, not the src addr. Also it is occurring during prerouting.

Are these conditions the same with the rule you suggested?

How exactly is this rule determined as I was hoping to modify only the destination addr using prerouting on my vpn server. Then do something similar where I match the packet incoming to the vpn client on the router and again switch the destination IP so it routes to the LAN host.

I believe this means I will have never modified the source IP along the incoming route which means the server running on the LAN host will know how to address any outgoing packets to the client IP on the other side of the VPN.

OOPS. Yes, 'saddr' is looking at the incoming source address, that should be 'daddr' to look at the packet's original destination. Sorry about that!

This is the nat prerouting hook, so that part looks right.

That iifname selection just above your new rule indicates that this new rule should probably be in the dstnat_vpn chain, so remove handle 114 and add your new rule there:

nft delete rule  inet fw4 dstnat  handle 114
nft add rule  inet fw4 dstnat_vpn  ip daddr 10.7.0.2  tcp dport 34  dnat to 192.168.2.218:34
nft list chain  inet fw4 dstnat_vpn

Is there also a srcnat_vpn chain, where you'd say something like (assuming the VPN is on 10.7.0.0/24?):

nft add rule  inet fw4 srcnat_vpn  ip saddr 10.7.0.0/24  ???  snat to ???

I'm getting outside my comfort zone here, so...

If you're out of your comfort zone, I'm in way over my head :smile:

Here is the output of nft list chains

root@OpenWrt:~# nft list chain inet fw4 srcnat_vpn
Error: No such file or directory
list chain inet fw4 srcnat_vpn
                    ^^^^^^^^^^
root@OpenWrt:~# nft list chains
table inet fw4 {
        chain input {
                type filter hook input priority filter; policy accept;
        }
        chain forward {
                type filter hook forward priority filter; policy drop;
        }
        chain output {
                type filter hook output priority filter; policy accept;
        }
        chain prerouting {
                type filter hook prerouting priority filter; policy accept;
        }
        chain handle_reject {
        }
        chain syn_flood {
        }
        chain input_lan {
        }
        chain output_lan {
        }
        chain forward_lan {
        }
        chain helper_lan {
        }
        chain accept_from_lan {
        }
        chain accept_to_lan {
        }
        chain input_wan {
        }
        chain output_wan {
        }
        chain forward_wan {
        }
        chain accept_to_wan {
        }
        chain reject_from_wan {
        }
        chain reject_to_wan {
        }
        chain input_vpn {
        }
        chain output_vpn {
        }
        chain forward_vpn {
        }
        chain helper_vpn {
        }
        chain accept_from_vpn {
        }
        chain accept_to_vpn {
        }
        chain dstnat {
                type nat hook prerouting priority dstnat; policy accept;
        }
        chain srcnat {
                type nat hook postrouting priority srcnat; policy accept;
        }
        chain srcnat_wan {
        }
        chain dstnat_vpn {
        }
        chain raw_prerouting {
                type filter hook prerouting priority raw; policy accept;
        }
        chain raw_output {
                type filter hook output priority raw; policy accept;
        }
        chain mangle_prerouting {
                type filter hook prerouting priority mangle; policy accept;
        }
        chain mangle_postrouting {
                type filter hook postrouting priority mangle; policy accept;
        }
        chain mangle_input {
                type filter hook input priority mangle; policy accept;
        }
        chain mangle_output {
                type route hook output priority mangle; policy accept;
        }
        chain mangle_forward {
                type filter hook forward priority mangle; policy accept;
        }
        chain pbr_forward {
        }
        chain pbr_input {
        }
        chain pbr_output {
        }
        chain pbr_prerouting {
        }
        chain pbr_postrouting {
        }
}

Here is the output of nft -a list chain inet fw4 dstnat_vpn:

root@OpenWrt:~# nft -a list chain inet fw4 dstnat_vpn
table inet fw4 {
        chain dstnat_vpn { # handle 28
                meta nfproto ipv4 tcp dport 34 counter packets 4 bytes 240 dnat ip to 192.168.2.218:34 comment "!fw4: test" # handle 104
                ip daddr 10.7.0.2 tcp dport 34 dnat ip to 192.168.2.218:34 # handle 115
        }
}

That said I'm still not seeing any traffic incoming on my LAN host with the rule you suggested. Is there any way to see when packets come into the vpn interface on the router, then to see if nf_tables is picking them up? I'm unfamiliar with firewall zones in openwrt but is there some nuance I am missing where the interface needs to be supplied or some blocking between interfaces/zones?

There's some useful info for setting up a monitor on a rule here:

I haven't tried it, but it looks like it might be just the thing to see what's going on.

1 Like

In addition to the above rule tracing, you could also use tcpdump to watch raw packet traffic. (If you're not familiar with it, it's basically wireshark's internals.)

The -n turns off name resolution (like with many other network/port tools), so we just see IP addresses in the output. -i takes an interface name parameter, so here we're seeing just packets crossing br-lan. It has a rich filter language, close enough to iptables and nft to be recognizable, but different enough to be annoying.

$ tcpdump -n -i br-lan
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br-lan, link-type EN10MB (Ethernet), snapshot length 262144 bytes
06:31:28.878378 IP6 fd91:f453:ed1f::1.22 > fd91:f453:ed1f:0:ec3d:ad74:4033:fdb3.53837: Flags [P.], seq 408652691:408652887, ack 856843803, win 1002, length 196
06:31:28.922412 IP6 fd91:f453:ed1f:0:ec3d:ad74:4033:fdb3.53837 > fd91:f453:ed1f::1.22: Flags [.], ack 196, win 1026, length 0
06:31:30.190480 IP 10.1.2.192.63730 > 10.1.2.1.53: 52699+ A? api.dropboxapi.com. (36)
06:31:30.190738 IP 10.1.2.192.59187 > 10.1.2.1.53: 51229+ AAAA? api.dropboxapi.com. (36)

Note that port number is mashed onto the end of the IP address using a dot as a separator.

To see all the packets on your client3 interface that have port 34 either source or destination, you could say this and wait until something happens. (You can prefix 'port' with either 'src' or 'dst' to look at just one direction.)

$ tcpdump -i client3 'port 34'

The man page (https://www.tcpdump.org/manpages/tcpdump.1.html) has some examples, but there are a lot more (and better) examples in various blogs and tutorials out there.

You probably don't need to use this next one, but a way to track connections is to use the conntrack tool, which must be user-installed (opkg install conntrack). It will show you the meta-context information that netfilter stashes for packet flows. Using conntrack -E, you can watch as connections change state from new to established and so on. Like tcpdump it has its own filter language, again simultaneously similar and different. (If you find yourself debugging nft expressions ct state or ct status, or their iptables equivalent, this is the tool to use.)

Wow Eric!

That is so helpful. I'll have to play around with it today and see what I find. I assume it must be installed with opkg.

Hey Eric, good news I was able to play around with forwarding traffic using your advice and see traffic being forwarded all the way to my LAN host with what appears to be the correct source IP (via wireshark), so I'm getting close!

Using those conntrack/tcpdump has been super handy, thanks a bunch.

You're on the right track when suggesting I add some SNAT rule, however that is done at the exit node (VPN Server) as it is the IP that must be replied to from the client.

My last challenge is creating some rule that forwards the traffic coming from LAN to VPN interface based on sourceIP:port, i.e. LANhostIP:34 using nft. I obviously don't want it to be a situation where I am rewriting the destination IP anywhere along the route as this would cause the packet to lose track of its final destination IP (the client).

Excellent! If you can figure out which chain the (srcnat_wan???) then looking at that chain's existing rules that the VPN generated should give you a big hint as to what's needed.

Have you considered how to make these rules persistent? Doing nft add ... on the command line is only modifying the in-RAM ruleset, and will go away at the next reboot (or fw4 reload if you poke at something in LuCI).

I've noticed that the rules aren't persistent. Eventually I'll try and figure out how to save them when it's working, I'd assume there's something like iptables-save with nft/OpenWRT

As it stands I dont know if there are any rules for routing through the VPN as by default there is no traffic routed to it.

I'll check the srcnat_wan anyway to see if there's anything useful.

Just wanted to let you know your help taught me a lot and since the original problem I encountered had been solved by you some time ago, I'm marking the thread as solved.

Thanks again, for your closure I'll let you know I'm using the pbr package to selectively route traffic to the VPN interface as simply using DNAT would result in the loss of the intended destination when sent out on the VPN servers WAN interface.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.