Isolating your home IPv6 network with NPTv6 on EdgeRouter Lite

by cmur2 on 2016-11-27 in Linux

My motivation for this endeavor as summarized by user rps on a Ubiquity EdgeRouter forum thread on ‎06-11-2015:

The biggest use case for NPTv6 is for SMB networking where you want to have multiple providers but maintain a “one host, one address” model for your internal network, or you simply want to use a predictable network prefix for internal addressing for things like static address configuration that is independent of what prefix you get from your provider.

Another post by user csch in the same thread roughly gives the idea how to do NPTv6 with ip6tables and that EdgeOS is already up to the task by means of available kernel modules etc. In this blog post I want to show the glue necessary to integrate NPTv6 (IPv6 network prefix translation) with my setup.

Setup

My home network is managed by a Ubiquity EdgeRouter Lite (very nice device btw!) and connected via eth1. The WAN uplink port eth2 connects to the ISP and receives native IPv4 via DHCP and native IPv6 via DHCPv6-PD (prefix delegation) - standard dual stack stuff.

Sadly said ISP does not (want to) provide static IPv6 prefixes so you cannot be sure to receive the same prefix via DHCPv6-PD on subsequent requests or after a power loss. Since I want a predictable (private) network prefix that is provider independent and remains constant when e.g. changing providers I chose to deploy a ULA prefix via SLAAC to all LAN hosts:

# show interfaces ethernet eth1
address fdxx::1/64
description lan
ipv6 {
dup-addr-detect-transmits 1
router-advert {
cur-hop-limit 64
link-mtu 0
managed-flag false
max-interval 600
other-config-flag false
prefix fdxx::/64 {
autonomous-flag true
on-link-flag true
valid-lifetime 43200
}
reachable-time 0
retrans-timer 0
send-advert true
}
}

Now I need to retrieve a DHCPv6-PD prefix from my ISP and then use that and the ULA prefix to do network prefix translation when forwarding traffic from LAN hosts to WAN. Since already ships the wide-dhcpv6-client package and uses dhcp6c itself for pure prefix delegation to LAN hosts I will use it, too.

As already observed by Koos van den Hout, the dhcp6c client needs a network interface to assign (an address from) the retrieved prefix to for which I use a dummy interface, via /etc/network/interfaces:

auto dummypd0
iface dummypd0 inet manual
pre-up ip link add name $IFACE type dummy

Then the dhcp6c config file for my setup (requesting only a single /64 prefix via DHCPv6-PD and fully assigning it to dummypd0 interface) located at /root/my-npt-dhcp6c.conf looks like this:

interface eth2 {
send rapid-commit;
send ia-pd 0;
# send ia-na 0;
script "/root/my-npt-dhcp6c-script";
};
#id-assoc na 0 {
#};
id-assoc pd 0 {
prefix ::/64 infinity;
prefix-interface dummypd0 {
sla-id 0;
sla-len 0;
ifid 1;
};
};

Since I want to configure NPTv6 every time a new IPv6 was received I configure dhcp6c to run a script located at /root/my-npt-dhcp6c-script every time it receives (new) DHCPv6-PD information. This custom script checks whether the last known prefix (cached in $OUTSIDE_PREFIX_FILE) is different from the one currently used on the dummypd0 interface and when that’s the case it refreshes the ip6tables configuration accordingly.

Update 2: I updated the script (available as a gist here) to handle incoming traffic via a second NPTv6 rule that translates the outside prefix to the inside prefix. I am not sure whether this is the correct way to do it with ip6tables but as of now it works and I still have to discover any negative side effects…

#!/bin/bash
INSIDE_PREFIX="fdxx::/64"
OUTSIDE_INTERFACE="eth2"
DUMMY_INTERFACE="dummypd0"
OUTSIDE_PREFIX_FILE="/var/run/my-npt-outside-prefix"
logger -p info -t my-npt "my-npt-dhcp6c-script invoked"
OLD_OUTSIDE_PREFIX=""
if [ -f "$OUTSIDE_PREFIX_FILE" ]; then
OLD_OUTSIDE_PREFIX=`cat $OUTSIDE_PREFIX_FILE`
fi
# assumes IP on dummy interface ends with ::1/64
NEW_OUTSIDE_PREFIX=`ip -6 -o addr show $DUMMY_INTERFACE | cut -d\ -f 7 | sed 's,::1/64,::/64,'`
# on prefix change
if [ "$OLD_OUTSIDE_PREFIX" != "$NEW_OUTSIDE_PREFIX" ]; then
# delete old NPTv6 firewall rule if present
if [ -n "$OLD_OUTSIDE_PREFIX" ]; then
logger -p notice -t my-npt "deleting old DHCPv6-PD prefix $OLD_OUTSIDE_PREFIX from NPTv6"
ip6tables -t nat -D PREROUTING -i $OUTSIDE_INTERFACE -d $OLD_OUTSIDE_PREFIX -j NETMAP --to $INSIDE_PREFIX
ip6tables -t nat -D POSTROUTING -o $OUTSIDE_INTERFACE -s $INSIDE_PREFIX -j NETMAP --to $OLD_OUTSIDE_PREFIX
rm $OUTSIDE_PREFIX_FILE
fi
# add new NPTv6 firewall rule if new prefix
if [ -n "$NEW_OUTSIDE_PREFIX" ]; then
echo -n "$NEW_OUTSIDE_PREFIX" > $OUTSIDE_PREFIX_FILE
logger -p notice -t my-npt "adding new DHCPv6-PD prefix $NEW_OUTSIDE_PREFIX to NPTv6"
ip6tables -t nat -A PREROUTING -i $OUTSIDE_INTERFACE -d $NEW_OUTSIDE_PREFIX -j NETMAP --to $INSIDE_PREFIX
ip6tables -t nat -A POSTROUTING -o $OUTSIDE_INTERFACE -s $INSIDE_PREFIX -j NETMAP --to $NEW_OUTSIDE_PREFIX
fi
fi
# show debug: ip6tables -S -t raw; ip6tables -S -t nat
# do debug: conntrack -f ipv6 -L; ip6tables -t raw -D OUTPUT -j NOTRACK; ip6tables -t raw -D PREROUTING -j NOTRACK
# to expire and renew prefix: kill -HUP $(cat /var/run/dhcp6c.pid)
exit 0

To enable the DHCPv6-PD client on boot I added the following to /etc/rc.local:

echo 2 > /proc/sys/net/ipv6/conf/eth2/accept_ra # required for successful DHCPv6-PD communication
dhcp6c -c /root/my-npt-dhcp6c.conf eth2

Note: It may be necessary to open up your firewall on the WAN interface to allow for DHCPv6 and ICMPv6 in the appropriate EdgeOS rulesets adding something like (via):

set rule 30 action accept
set rule 30 description "allow ICMPv6"
set rule 30 protocol icmpv6
set rule 40 action accept
set rule 40 description "allow DHCPv6 client/server"
set rule 40 destination port 546
set rule 40 source port 547
set rule 40 protocol udp

With the described setup my LAN host address scheme can remain constant and in theory I should not be bothered to touch the SLAAC config distributing the ULA prefix again even when the ISP changes it’s addressing habit or protocols or when I switch the provider.

Dual-Stack Issues (Update)

As per RFC 6724 the approach pictured above only works as intended in IPv6-only networks since outbound traffic will use IPv4 addresses when present instead of IPv6 ULAs. Darn. (IPv6 ULAs are designated to be used only for contacting internal services.)

A temporary solution (without eliminating the dual-stack setup) for me is to use an unused static IPv6 prefix assigned by another ISP (tunnel broker) instead of an IPv4 ULA prefix for SLAAC addressing on the LAN interface. It is not that independent anymore but at least predictable.

Todos

  • unclear whether dhcp6c deletes old IPv6 address from dummypd0 interface itself
  • getting default IPv6 route on WAN interface depending on setup, I use set protocols static route6 ::/0 next-hop MY_NEXT_HOP_IPV6
  • document matching firewall configuration
  • not stress tested in production, beware rough edges
The post »Isolating your home IPv6 network with NPTv6 on EdgeRouter Lite«
is licensed under Creative Commons BY-NC-SA 3.0.

cmur2

https://www.mycrobase.de/

GitHub