1

i have a multihomed router with two external IPs. I would like to NAT incoming connections from both IPs to the same internal IP.

What I have so far

$ ip rule show
0:      from all lookup local
32761:  from 192.168.220.129 lookup ftto
32764:  from 192.168.220.128 lookup vdsl
32766:  from all lookup main
32767:  from all lookup default
$ ip route show default table ftto
default via fttogw dev ftto
support@qbs01:~$ ip route show default table vdsl
default via vdslgw dev vdsl
$ cat nftables.conf
...
table ip nat {
        chain prerouting {
                type nat hook prerouting priority 100; policy accept;
                iifname "vdsl" tcp dport { smtp } dnat 192.168.220.128;
                iifname "ftto" tcp dport { smtp } dnat 192.168.220.129;

        }
        chain postrouting {
                type nat hook postrouting priority 100; policy accept;
                # masquerade internal networks
                ip saddr 192.168.220.0/23 oifname "vdsl" masquerade;
                ip saddr 192.168.220.0/23 oifname "ftto" masquerade;
...
}

and this works. How do I do this without having to assign two internal IP addresses to every internal machine that needs such setup?

  • Just to be clear. You don't care in this question about the router itself, you care only about the server(s) behind it, right? – A.B Aug 08 '21 at 00:31
  • Correct, if I ever need to use those services from the router I can always use the internal addresses. I actually only care about the users in front of the router, I.e. on the open internet. – Aleksandar Ivanisevic Aug 09 '21 at 06:51
  • Any feed back on the answer given some time ago? – A.B Feb 23 '22 at 19:30

1 Answers1

1

Completing information not available in OP:

  • 192.168.220.0/23 is a LAN available on router's interface named lan0.

  • there is no other LAN in OP's setup.


Let's see what's needed for a flow initiated from outside:

  • original incoming

    Apparently nothing special: just dnat everything to the single destination

  • reply

    conntrack stored a conntrack lookup entry for the flow in the previous bullet. But what's missing is the interface it came from. As the same public IP address could have chosen to connect once through vdsl and an other time through ftto this is specific to each flow, not to the remote client. So conntrack needs to be assisted to keep this missing piece of information. For this role, a conntrack mark can be used. In this setup each mark value will be equivalent to an interface name.

    Its use is described for example in this blog: Netfilter Connmark. This allows to assign a specific mark at the previous step (incoming) to the flow (rather than just the packet) and use it as selector for outgoing.

    This selection can't be done in post routing: this is after the interface choice is needed. It must happen in pre routing and affect the routing stack. The ip rule s must be altered to use marks too

In the end here's what's done:

  • keep only 192.168.220.128 for the example server

  • add a type filter hook prerouting chain to handle marks. As it's a router-only case (OP is not interested in traffic initiated by or arriving to the router itself), nothing is needed in the output hook. Here how it will be working:

    • if the conntrack mark is set, that means this flow was already handled previously: set the packet mark and skip next settings
    • else if it's a new flow, if relevant, set a mark related to the incoming interface to the packet and also in the conntrack entry.
  • change ip rules to use marks instead of addresses, and complete routing tables

  • no need to use multiple tables for different hook types, that's an iptables restriction that limits features available. For example, sets and maps have a scope limited to a single table. To factorize rules by using a set, all chains using this set must be in the same table.

  • I'll use a map to store the association from port to backend server to simplify management.

  • Nitpicking: the legacy priority of nat/prerouting is -100 rather than 100, this doesn't really matter unless also interacting with iptables's nat.

First, change the ip rule s to the rules and routes below instead: a mark is used as selector instead of any IP address. It will be set for each new flow by nftables, conntrack mark for stateful mechanism and retrieved for each packet belonging to this flow.

ip rule add fwmark 101 lookup vdsl
ip rule add fwmark 102 lookup ftto

If Strict Reverse Path Forwarding is enabled, all routes should be available in each table (hence the duplicated LAN route added below for each routing table):

ip route add table vdsl 192.168.220.0/23 dev lan0
ip route add table vdsl default via vdslgw dev vdsl onlink
ip route add table ftto 192.168.220.0/23 dev lan0
ip route add table ftto default via fttogw dev ftto onlink

Still if in the case of SRPF, the 11 years old, only recently documented, src_valid_mark sysctl toggle must be enabled:

sysctl -w net.ipv4.conf.lan0.src_valid_mark=1
sysctl -w net.ipv4.conf.vdsl.src_valid_mark=1
sysctl -w net.ipv4.conf.ftto.src_valid_mark=1

New ruleset replacing OP's ruleset:

table ip multihomed {

    map port2ip {
        type inet_service : ipv4_addr
        elements = {
            25 : 192.168.220.128
        }
    }

    chain handlemarks {
        type filter hook prerouting priority -150; policy accept;
        ct mark != 0 meta mark set ct mark accept
        ct state new meta mark set iifname map { "vdsl" : 101, "ftto" : 102 } ct mark set meta mark accept
    }

    chain prenat {
        type nat hook prerouting priority -100; policy accept;
        iifname { "vdsl", "ftto" } dnat to tcp dport map @port2ip;
    }

    chain postnat {
        type nat hook postrouting priority 100; policy accept;
        ip saddr 192.168.220.0/23 oifname { "vdsl", "ftto" } masquerade;
    }
}

To easily add a (single) service reachable from both public addresses, one can add an entry in the port2ip map. For example the http port mapped to server 192.168.220.130:

nft add element ip multihomed port2ip '{ 80: 192.168.220.130 }'

Note: the last masquerade rule, for traffic initiated from LAN or router won't be subject to policy routing. Usual routing will apply always using the same single interface.

A.B
  • 5,338
  • 1
  • 17
  • 20
  • this was very helpful for a similar situation I had with 2 internet connections and allowing DNAT on inbound connections on both of them - thanks a lot for posting this – David Nichols Sep 04 '22 at 15:18