I have an OpenWRT gateway (self-built 19.07, kernel 4.14.156) that sits on a public IP address in front of my private network. I am using nftables (not iptables).

I would like to expose a non-standard port on the public address, and forward it to a standard port on a machine behind the gateway. I think this used to be called port forwarding: it would look like your gateway machine was providing, say, http service, but it was really a machine behind the gateway on a private address.

Here is my nftables configuration. For these purposes, my "standard service" is on port 1234, and I want to allow the public to access it at gateway:4321.

#!/usr/sbin/nft -ef
# nftables configuration for my gateway

flush ruleset

table raw {
        chain prerouting {
                type filter hook prerouting priority -300;
                tcp dport 4321 tcp dport set 1234 log prefix "raw " notrack;

table ip filter {
        chain output {
                type filter hook output priority 100; policy accept;
                tcp dport { 1234, 4321 } log prefix "output ";

        chain input {
                type filter hook input priority 0; policy accept;
                tcp dport { 1234, 4321 } log prefix "input " accept;

        chain forward {
                type filter hook forward priority 0; policy accept;
                tcp dport { 1234, 4321 } log prefix "forward " accept;

table ip nat {
        chain prerouting {
                type nat hook prerouting priority 0; policy accept;
                tcp dport { 1234, 4321 } log prefix "nat-pre " dnat;

        chain postrouting {
                type nat hook postrouting priority 100; policy accept;
                tcp dport { 1234, 4321 } log prefix "nat-post ";
                oifname "eth0" masquerade;

Using this setup, external machines can access the private machine at gateway:1234. Logging shows nat-pre SYN packet from external to gateway IP, then forward from external to internal IP, then nat-post from external to internal, and 'existing-connection` takes care of the rest of the packets.

External machines connecting to gateway:4321 log as raw, where the 4321 gets changed to 1234. Then the SYN packet gets forwarded to the internal server, the reply SYN packet comes back, and ... nothing!

The problem, I think, is that I'm not doing the nftables configuration that would change the internal:1234 back to gateway:4321, which the remote machine is expecting. Even if masquerade changes internal:1234 to gateway:1234, the remote machine is not expecting that, and will probably dump it.

Any ideas for this configuration?

1 Answer 1


You are not translating the port number. When the external connection is to port 1234, this is not a problem. But when it is to 4321, the dnat passes through to port 4321 on the internal server, not port 1234. Try

tcp dport { 1234, 4321 } log prefix "nat-pre " dnat;

You do not need to translate the reply packets coming back from your internal server. This is done automagically using the entry in the connection tracking table that is created on the first syn packet.

  • Sh!t, I didn't realize dnat would take a destination address and port! That's a big help! However, when I connect to public:4321 (from outside), it looks like there's a loop at the gateway -- I get a bunch of identical log messages that look like the startup SYN packet from my-test-machine:random-port to gw-public:2222. The dnat destination box never receives anything. I'll have to look into this further tomorrow.
    – Dave M.
    Dec 4, 2019 at 6:53
  • Whoops, there it is...I wasn't allowing forwards correctly. This solves my problem, thanks very much!
    – Dave M.
    Dec 4, 2019 at 6:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .