Teksavvy IPv6

Teksavvy now has a beta of IPv6 service over DSL. It seems to work reasonably well. As of today, it appears the IPv6 transit is provided by Hurricane Electric. No surprise.

From what I can tell, Teksavvy’s beta gives you a new PPPoE account configured for multilink, dynamic IPv4, and static IPv6. IPv4 is handled as before, with a single /32 address routed over the PPP link. Currently, a /64 subnet and a /56 IPv6 subnet are routed to the client over the PPP link. There is auto-configuration of the /64 subnet using ICMPv6 Neighbor Discovery Protocol (NDP), but the /56 subnet is manually configured. Both subnets are routed over the PPP link, probably for convenience of NDP auto-configuration, which works only on /64 subnets.

These are approximately the things that need to happen:

My current setup is a SpeedStream 5260/5660 ADSL modem connected to a Mandriva Linux 2010.1 box with two ethernet interfaces serving as a PPPoE endpoint (RP-PPPoE), router, NAT, and server box. The methodology and scripts target my machine, and from what I’ve seen, other distributions often do things very differently.

Setup Two PPPoE Connections

In the basic case, this is a simple matter of changing the PPPoE username to __@hsiservice.net (in /etc/ppp/pppoe.conf and /etc/ppp/pap-secrets). Because I wanted to keep the static IPv4 address with my __@wiredhighspeed.com login, I use two simultaneous PPPoE sessions, one for IPv4 and one for IPv6.

Using simultaneous PPPoE sessions with RP-PPPoE’s adsl-start (or pppoe-start) script is possible by using separate pppoe.conf files for each connection. I copied pppoe.conf to pppoe2.conf, and changed the following lines:

#/etc/ppp/pppoe2.conf changes from pppoe.conf
USER=__@hsiservice.net
DEFAULTROUTE=no
PIDFILE="/var/run/$CF_BASE-pppoe2.pid"
PPPD_EXTRA="unit 1 ipparam ppp1 noip"

An entry for the new USER was also added to /etc/ppp/pap-secrets. I set DEFAULTROUTE to no because I don’t want my IPv4 default route through the new PPP link, though I’m not sure it’s necessary. PIDFILE was changed to be unique. In PPPD_EXTRA, unit 1 and ipparam ppp1 specifies that I want to use ppp1 for this connection (the IPv4 PPP link uses “unit 0 ipparam ppp0“). The noip parameter disables IPCP negotiation for this link so it won’t configure an IPv4 address for this link. The net result is that my second PPP link will use the ppp1 device, have no IPv4 address, and will have an IPv6 address via IPv6CP.

In Mandriva Linux, the normal way of setting up network interfaces seems to be to use a collection of /etc/sysconfig/network-scripts/ifcfg-___ configuration files, one for each interface. Naturally, I now want both ifcfg-ppp0 and ifcfg-ppp1. For ifcfg-ppp1, set IPV6INIT=yes to make sure /etc/ppp/ipv6-up gets run.

#/etc/sysconfig/network-scripts/ifcfg-ppp1
DEVICE=ppp1
ONBOOT=yes
TYPE=xDSL
USERCTL=no
IPV6INIT=yes

# The following are new settings, explained later. They are not standard.
PPPOECONFIGFILE=/etc/ppp/pppoe2.conf
IPV6PPPAUTOCONF=yes
IPV6ADDR_UNROUTES="2607:f2c0:f00e:6a00::/56"

The Initscripts (/etc/sysconfig/network-scripts/ifup-ppp and ifdown-ppp) never pass a configuration file parameter to adsl-start, so if[up/down]-ppp need changing too. This change accepts the PPPOECONFIGFILE option and passes it to adsl-start so we can specify PPPoE configuration files from ifcfg-ppp1.

--- ifup-ppp.orig	2010-09-05 00:41:43.086475954 -0400
+++ ifup-ppp	2010-09-04 16:20:51.000000000 -0400
@@ -57,7 +57,7 @@
 # check that xDSL connection
 if [ "$TYPE" = "xDSL" ] ; then
     if [ -x /sbin/adsl-start -o -x /usr/sbin/adsl-start ] ; then
-        adsl-start
+        adsl-start "$PPPOECONFIGFILE"
         exit $?
     else
         /usr/bin/logger -p daemon.info -t ifup-ppp \
--- ifdown-ppp.orig	2010-09-05 00:44:58.000000000 -0400
+++ ifdown-ppp	2010-09-04 16:21:18.000000000 -0400
@@ -8,7 +8,7 @@
 source_config
 
 if [ "$TYPE" = "xDSL" ] && [ -x /sbin/adsl-stop -o -x /usr/sbin/adsl-stop ] ; then
-    adsl-stop
+    adsl-stop "$PPPOECONFIGFILE"
     exit $?
 fi

At this point, with two pppoe*.conf and ifcfg-ppp* files with the PPPOECONFIGFILE option, it should be possible to bring up and down each PPPoE connection independently using ifup ppp0 or ifdown ppp1.

Enabling IPv6CP Negotiation

Enabling IPv6CP negotiation for the PPP link is simple enough. Add ipv6 , to /etc/ppp/options to enable IPv6CP negotiation with random interface identifiers. The pppd man page says using ipv6cp-use-persistent and ipv6cp-use-ipaddr can also be used for MAC-derived and IPv4 address-derived interface identifiers instead, but I haven’t tried them.

With IPv6CP negotiation, the PPP link should now have a link-local address assigned, e.g., inet6 addr: fe80::31e4:cf97:998:7484/10 Scope:Link. Note that IPv6CP only configures a link-local address, and is not supposed to (or able to) configure a global address, unlike IPCP (v4).

Auto-Configuration using Neighbor Discovery Protocol

NDP is used to advertise subnet (/64) prefixes to end hosts so the end hosts can form its own IPv6 address by appending a 64-bit host identifier (usually MAC-related). Teksavvy advertises the assigned /64 over the PPP link, but notice that they don’t advertise their recursive DNS servers using the RNDSS option.

#
# radvd configuration generated by radvdump 1.6
# based on Router Advertisement from fe80::90:1a00:4243:14a8
# received by interface ppp1
#

interface ppp1
{
	AdvSendAdvert on;
	# Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
	AdvManagedFlag on;
	AdvOtherConfigFlag on;
	AdvReachableTime 0;
	AdvRetransTimer 0;
	AdvCurHopLimit 0;
	AdvDefaultLifetime 1800;
	AdvHomeAgentFlag off;
	AdvDefaultPreference medium;
	AdvLinkMTU 1486;

	prefix 2607:f2c0:a000:170::/64
	{
		AdvValidLifetime 2592000;
		AdvPreferredLifetime 604800;
		AdvOnLink on;
		AdvAutonomous on;
		AdvRouterAddr off;
	}; # End of prefix definition

}; # End of interface definition

In theory, after the PPP connection is established with a link-local IPv6 address, a global IPv6 address should be picked from within subnet 2607:f2c0:a000:170::/64 and assigned to the local host. Although router advertisements are received, auto-configuration doesn’t happen, probably a missing feature in pppd or the kernel. (I think this is mentioned in the initscripts-ipv6 page as a missing feature in ppp.)

One workaround to auto-configuration is to use the /etc/ppp/ipv6-up.local script to do router discovery (using rdisc6, part of the ndisc6 package) and manually set an IPv6 address. The script uses a IPV6PPPAUTOCONF=yes option in the ifcfg-__ files to enable this workaround. The host ID from the lower 64-bits of the local link-local address (passed as parameter 4 from pppd) is appended to the global prefix advertised by the ISP. It also adds an unreachable route to the subnet to avoid routing loops. The ipv6-down.local script remove routes added by ipv6-up.local.

I tried echo 1 > /proc/sys/net/ipv6/conf/ppp1/accept_ra, but auto-configuration didn’t seem to work on its own either.

#!/bin/sh
# /etc/ppp/ipv6-up.local
#
# Calling parameters:
#  $1: interface name
#  $4: local link-local address
#  $6: logical interface name (set by pppd option ipparam)
#
# ppp interfaces don't auto-configure using IPv6 stateless auto-configuration.
# Do it manually using rdisc6 (in the ndisc6 package).

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

LOGDEVICE=$6
REALDEVICE=$1

[ -f /etc/sysconfig/network ] || exit 0
. /etc/sysconfig/network

cd /etc/sysconfig/network-scripts
. ./network-functions
. ./network-functions-ipv6

CONFIG=$LOGDEVICE
[ -f "$CONFIG" ] || CONFIG=ifcfg-$CONFIG
source_config



if [ "$IPV6PPPAUTOCONF" == "yes" ]; then
	ADVPREFIX=`rdisc6 -1q $REALDEVICE` || ADVPREFIX=""

	address_implicit="$(echo $ADVPREFIX | awk -F/ '{ print $1 }' | awk -F:: '{ print $1 }' | cut -d: -f1-4 )"
	hostid=`echo $4 | awk -F:: '{print $2}' | rev | cut -d: -f1-4 | rev`
	ADVADDRESS="$address_implicit":"$hostid"/128


	if [ -n "$ADVADDRESS" ]; then
		ipv6_add_addr_on_device $REALDEVICE $ADVADDRESS || exit 1

		#We get the whole prefix to ourselves. Make the rest of
		#it unreachable to avoid routing loops.
		ip -6 route add unreach $ADVPREFIX
		echo $ADVPREFIX > /var/run/"$REALDEVICE".autoroute
	fi

fi



# Setup IPv6 unroutes from list, if given
if [ -n "$IPV6ADDR_UNROUTES" ]; then
	for ipv6addr in $IPV6ADDR_UNROUTES; do
		ip -6 route add unreach $ipv6addr
	done
fi
#!/bin/sh
# /etc/ppp/ipv6-down.local
#
# Calling parameters:
#  $1: interface name
#  $6: logical interface name (set by pppd option ipparam)
#
# ppp interfaces don't auto-configure using IPv6 stateless auto-configuration.
# Do it manually using rdisc6 (in the ndisc6 package).

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

LOGDEVICE=$6
REALDEVICE=$1


[ -f /etc/sysconfig/network ] || exit 0
. /etc/sysconfig/network

cd /etc/sysconfig/network-scripts
. ./network-functions
. ./network-functions-ipv6

CONFIG=$LOGDEVICE
[ -f "$CONFIG" ] || CONFIG=ifcfg-$CONFIG
source_config

if [ -f /var/run/"$REALDEVICE".autoroute ]; then
	ADVPREFIX=`cat /var/run/"$REALDEVICE".autoroute`

	if [ -n "$ADVPREFIX" ]; then
		ip -6 route del unreach "$ADVPREFIX"
	fi
	rm -f /var/run/"$REALDEVICE".autoroute
fi



# Setup IPv6 unroutes from list, if given
if [ -n "$IPV6ADDR_UNROUTES" ]; then
	for ipv6addr in $IPV6ADDR_UNROUTES; do
			ip -6 route del unreach $ipv6addr
	done
fi

Another issue was that bringing down the PPP interface using /usr/sbin/adsl-stop (pppoe-stop) did not cause ipv6-down to be executed. It appears that sending pppd a SIGKILL kills it immediately, but sending it a SIGHUP(1) causes it to run the shutdown scripts first before exiting. The sleep 1 is necessary to give pppd time to run the shutdown scripts because pppd gets killed again at the end of adsl-stop.

--- adsl-stop.orig	2010-09-05 11:11:20.000000000 -0400
+++ adsl-stop	2010-09-04 16:38:18.000000000 -0400
@@ -65,7 +65,8 @@
 	PPPD_PID=`cat "$PPPD_PIDFILE"`
 	$LOGGER -p daemon.notice "Killing pppd"
 	echo "Killing pppd ($PPPD_PID)"
-	kill $PPPD_PID > /dev/null 2>&1 || exit 1
+	kill -1 $PPPD_PID > /dev/null 2>&1 || exit 1
+	sleep 1
     fi
 
     # Kill pppoe-start

Set up Default Route and Unreachable Routes

There are two sets of routes to worry about: Outgoing, and incoming. Outgoing packets use a default route to the PPP link. initscripts-ipv6 already handles that, by setting IPV6_DEFAULTDEV=ppp1 in /etc/sysconfig/network (used in /etc/sysconfig/network-scripts/ifup-ipv6). I won’t discuss route optimizations here (e.g., routing 2002::/16 over tun6to4.)

Incoming packets need to be handled for all subnets that will have incoming packets. In my case, Teksavvy forwards both 2607:f2c0:a000:170::/64 and 2607:f2c0:f00e:6a00::/56 to me over the PPP link, so I will need to handle routes for both those ranges. In my auto-configuration script (ipv6-up.local), I grab one address (/128) for the PPP interface, and then add an unreachable route for the rest of the /64. The ipv6-up.local script also reads the IPV6ADDR_UNROUTES variable and adds unreachable routes for those too, which contains my /56, set in ifcfg-ppp1. Having unreachable routes avoids routing loops formed by repeatedly forwarding your own subnets out the ppp1 default route and having them routed back by the ISP because you own those subnets. Unreachable routes claims ownership of your own subnets and returns an unreachable error instead. Running ping6 to a non-existent address within your subnets should return unreachable, rather than Time exceeded: Hop limit.

Auto-configuration for a home router might choose to assign the global address with a /64 netmask to the LAN interface in addition to the PPP interface, routing the entire /64 to the LAN, and then using radvd to advertise the /64 prefix to the LAN. I don’t think there is a need for the /56 unless you need more than one auto-configured subnet.

Set up Routing for LAN

My current setup leaves the /64 block mostly unused, subnets my assigned /56 block and distributes one /64 subnet from that block out to my LAN, leaving the rest of the /56 unreachable. I’ve also kept my 6to4 addresses around in the meantime. My LAN interface (br0 in my case) is configured as follows:

IPV6INIT=yes
IPV6TO4INIT=yes
IPV6TO4_IPV4ADDR=206.248.137.124
IPV6ADDR="2607:f2c0:f00e:6a00::1/64"
IPV6ADDR_SECONDARIES="2002:cef8:897c::1/64 2002:cef8:897c::2/64 fec0:0:0:ffff::1/64 fec0:0:0:ffff::2/64 fec0:0:0:ffff::3/64"

Using 2607:f2c0:f00e:6a00::1/64 as the address for the LAN interface causes a route to 2607:f2c0:f00e:6a00::/64 to be added to the LAN interface, which has precedence over the unreachable route for 2607:f2c0:f00e:6a00::/56 described above (longest prefix match). The fec0:0:0:ffff::[123]/64 addresses are hard-coded in Windows XP’s IPv6 stack as DNS servers (stateless DNS discovery), although this is probably deprecated by RFC3879.

The resulting relevant parts of the routing table looks like this:

# ip -6 route (edited)
            2002:cef8:897c::/64        dev br0
unreachable 2002:cef8:897c::/48        dev lo
            2002::/16                  dev tun6to4
            2607:f2c0:a000:170::2      dev ppp1
unreachable 2607:f2c0:a000:170::/64    dev lo
            2607:f2c0:f00e:6a00::/64   dev br0
unreachable 2607:f2c0:f00e:6a00::/56   dev lo
            fec0:0:0:ffff::/64         dev br0
            default                    dev ppp1

One 6to4 subnet is routed to my LAN (2002:cef8:897c::/64) with the rest of my allocation (/48) unreachable. Other 6to4 addresses (2002::/16) should be routed over IPv4 instead of IPv6 through a tunnel gateway for performance reasons. One address out of my /64 is assigned to ppp1 and used as the outgoing address (2607:f2c0:a000:170::2), with the rest unreachable. I route one /64 subnet of my /56 allocation out to my LAN (2607:f2c0:f00e:6a00::/64). The deprecated site-local addresses stay on my LAN. The rest of my IPv6 traffic is routed out the PPP link.

In order for client nodes on my LAN to get auto-configured IPv6 addresses, I advertise 2607:f2c0:f00e:6a00::/64 and a recursive DNS server using radvd.

# /etc/radvd.conf
interface br0
{
        AdvSendAdvert on;
	prefix 2607:f2c0:f00e:6a00::0/64
	{
		AdvOnLink on;
		AdvAutonomous on;
	};

	RDNSS 2607:f2c0:f00e:6a00::1
	{
	};
};

Once everything works, set ONBOOT=yes in /etc/sysconfig/network-scripts/ifcfg-ppp1, and everything should be persistent across reboots.

IPv6 Provisioning Thoughs

  • The ISP doesn’t really need to provision both a /56 and a /64. If they want to make it hard for end-users to subnetwork, provisioning a single /64 is sufficient. Otherwise, provisioning a single /56 is sufficient. Provisioning a single /56 does not hurt auto-configuration if one (e.g., the first) /64 subnet is NDP-advertised, while the rest is manually configured. Configuring routers for subnetting is always manual anyway.
  • For home routers, the most automated configuration scheme would probably involve listening for router advertisements for the ISP-assigned /64 prefix, taking one address for itself, then routing that subnet out the LAN interface and advertising the same subnet out the LAN port. This allows zero configuration for a router with multiple machines behind it if only one /64 subnet is required.

Comments are closed.