NPT6 as a core feature

The official mwan3 documentation says:

Using mwan3 with IPv6 requires additional configuration such as IPv6 masquerading through methods like NETMAP or NAT6. This is currently not implemented in mwan3 directly and requires additional configuration. ... This is something that needs to be configured outside of mwan3 itself.

Currently, what's expressible using the core firewall features is NAT6, where all LAN hosts are masqueraded behind the router IPv6 address, just like it is done with IPv4. This is suboptimal. That's why the feature request to make network prefix translation for IPv6 a core feature, configurable through Luci without the need to write any scripts.

Just for the reference, here is what I do, and what I want to be fully achievable without custom scripts:


source /lib/functions/

# IPv6 NAT (horrible)
ip6tables -t nat -F PREROUTING
ip6tables -t nat -F POSTROUTING
ULA=$(uci get network.globals.ula_prefix)
for IFACE in $(uci show mwan3 | sed -n '/=interface/s/^mwan3\.\(.*\)=interface/\1/ p') ; do
  network_get_device DEVICE $IFACE || continue
  network_get_prefix6 PREFIX $IFACE || continue
  if [ "$BITS" -le 48 ] ; then BITS=48 ; fi
  echo "Mapping $ULA_PART <-> $PREFIX for $IFACE (on $DEVICE)"
  ip6tables -t nat -A PREROUTING -d $FIRST_IP -j REDIRECT
  ip6tables -t nat -A PREROUTING -d $PREFIX -j NETMAP --to $ULA_PART
  ip6tables -t nat -A POSTROUTING -s $ULA_PART -m conntrack --ctorigdst $PREFIX -j NETMAP --to $PREFIX
  ip6tables -t nat -A POSTROUTING -s $ULA_PART -o $DEVICE -j NETMAP --to $PREFIX
  ip6tables -t nat -A POSTROUTING -s $ULA -o $DEVICE -j MASQUERADE

This tries to use NETMAP where possible, and resorts to MASQUERADE only if the upstream prefix is too small. Example: if the ISP only offers a /64, then the LAN would go through NETMAP, and the gguest WiFi (if I create one), or whatever delegated prefixes my hypervsor hosts obtain through DHCPv6, will still get IPv6 connectivity, in a masqueraded form.

1 Like

Given native connectivity is the standard - would this break IPv6 for those who get native connectivity?

The proposal is to make sure that NETMAP is available through Luci, optional and default off.

Actually, if the LAN is bigger than what the ISP supports, I think that by default it should use native connectivity where possible and MASQUERADE those parts of the LAN which don't fit.

1 Like

Does this concept actually work? I thought that endpoints which were issued a ULA are not supposed to use it to originate traffic to a GUA, i.e. an Internet site. Because unless they get translated somewhere in the LAN, ULAs will not route through the Internet.

So to have a fixed prefix on the LAN which can be NPTd to the ISP prefix that keeps changing, you have to make up a fake GUA prefix which you usually don't really own and thus you don't know if it is going to conflict with something on the Internet making it unreachable.

Yes it does work without any GUA, and the script sets up the NETMAP and MASQUERADE rules that do the translation. Without the script, of course the ULA-only setup doesn't work.

The problem is with the priorities - so IPv4 will be used to talk to dual-stack hosts. IPv6 is used in this setup only to talk to IPv6-only hosts.

Assuming it would be supported, how would/should the corresponding uci look like?

I think this would consist of three parts.

First, the new ip6class_npt option, to be possibly set on LAN interfaces. It is a list of IPv6 "classes" (but really upstream interface names), subprefixes of which should be allocated for the LAN, but not advertised on the LAN. Instead, a NPT rule would be created that translates between the GUAs available in that prefix, and ULAs available on the LAN, for traffic that comes in/out of the WAN interface corresponding to the mentioned class. I understand that there are people who want to set up NPT between two GUA prefixes, so maybe some syntax like lte->gpon (denoting the interface/class names to be translated) would also be needed.

Second, the meaning of the existing ip6class option is to be refined, because of the interaction with the ip6class_npt option. You can't both announce a prefix and have it translated. So perhaps, if ip6class is not mentioned, and ip6class_npt is, then assign only ULAs. If the same class is mentioned in explicitly both ip6class and ip6class_npt, it's ideally an error, but we could e.g. prefer ip6class in case of such a conflict.

Third, a new boolean option, masquarade_what_does_not_fit, would need to be introduced for use on WAN interfaces. It would masquerade traffic that goes through the WAN if the source IP is somehow wrong, instead of discarding such traffic. Or maybe the meaning of the existing masquerade option should be changed, so that it only applies to traffic with a "wrong" source IP.

Hm, sounds too complicated for my taste. Was more thinking about some extended support for defining netmap rules in uci. The above reads as if you try to somehow shoehorn a very specific nat approach into the generic system configuration, we should focus on providing the primitives required to realize such scenarios but defer the overall scenario implementation to higher level packages like mwan3 or pbr

It seems like something like option npt6 64 on the outgoing zone would be sufficient to define the configuration. This means for any forwarded IPv6 packet, rewrite the first 64 bits to the prefix of the outgoing interface.

This needs to be written in nftables first then it could be incorporated into fw4.

1 Like

I don't think that option npt6 64 is expressive enough. Suppose that I have a /56 from the ISP. Also let's suppose that I have two LANs: LAN and LAB. I may want to allocate /60 of the delegated prefix to the LAN, and /60 to the LAB, and to use NPT in the LAN (because of mwan3 and fail-over) but not in the LAB.

Because of the scenario above, the NPT should be associated not with the WAN interface, but with a pair of LAN and WAN interfaces - which is exactly where ip6class-like options fit.