PPP the next Generation
Just like any other link layer that is able to transport TCP/IP, the point-to-point protocol (PPP) was enhanced to transport IPv6. I will describe here how to set it up. The setup I describe will first set up IPv4 transport and then enhance it to also transport IPv6. The goal is to show what needs to be done to enable IPv6 over PPP.
I'll assume that the network used by the provider for serving customers is 2001:db8::/32. In reality, especially for providers that have more than a few hundred customers/connections or more than one access concentrator, the setup will be a bit more complex due to more than a single /32 net being involved. This article is only supposed to show some of the possibilities and techniques involved.
For my tests I used several virtual machines running Debian on VirtualBox and used PPPoE for communication. Actually any PPP capable transport (eg. modem, direct serial connection, UMTS) will do, but PPPoE was most straightforward for me.
The packages "ppp" and "pppoe" are needed for all machines, additionally "radvd" is needed for the server.
While installing the network interface of each machine should be in NAT or bridge mode, once all packages are in, they should be switched to internal (eg. with the ID "ppp-test"). I recommend to give the server two interfaces, so that routing can be tested.
The documentation of RP-PPPoE warns that the included server is not meant for commercial production setups, there should be plenty packages that are more suitable for the job, I just used it for convenience. I am also not entirely sure whether my basic PPP server setup is safe enough for a provider - do your own checks if you plan to be a real provider.
The PPPoE server expects its PPP configuration in /etc/ppp/pppoe-server-options, it contains the usual server side configurations:
#172.16.1.1:172.16.1.2 debug noipdefault passive +pap -chap auth name myisp.local ms-dns 10.0.2.3 netmask 255.255.255.0 hide-password
and some that are specific to PPPoE (copied from the default dsl-provider file in Debian):
lcp-echo-interval 20 lcp-echo-failure 3 connect /bin/true mtu 1492 noaccomp default-asyncmap
The first option would give the server the IPv4 address 172.16.1.1 and the client 172.16.1.2 if it was not commented out. In my setup this option actually comes from the PPPoE-Server. The netmask option must match whatever is set up in this first option or in the PPPoE server. The second option "debug" writes a few more lines into the system log - this can help during testing. The noipdefault option enables IPCP negotiation for IPv4 addresses. The passive option makes pppd wait for actions from the other side if there is no immediate reaction - this is useful for a server. The +pap and -chap options pin the pppd on PAP authentication, while auth requires the client to authenticate itself. The name option makes pppd tell the other side that it is a myisp.local machine (replace this with any nicer name). The ms-dns option makes the server tell the client what the DNS server is.
Authentication is configured in /etc/ppp/pap-secrets:
#user server password IPv4 fred myisp.local flintstone * barney myisp.local rubble *
The first parameter here is the user name, the second one must match whatever name was configured above, the third parameter is the password of the user, the last one should always be "*" to match any configured IP address. In this example there are two users "fred" and "barney".
IP-Forwarding should be enabled both for IPv4 and IPv6, best to put this into one of the startup scripts:
echo 1 >/proc/sys/net/ipv6/conf/all/forwarding echo 1 >/proc/sys/net/ipv6/conf/default/forwarding echo 1 >/proc/sys/net/ipv4/ip_forward
Now all we need to do is setup PPPoE, for this we set the interface on which the server runs (I'll assume eth1) up and start the server:
ifconfig eth1 up pppoe-server -F -I eth1 -L 172.16.1.1 -R 172.16.1.17
The -F option forces the server to stay in the foreground - this makes it easier to kill it again with Ctrl-C. Leave the -F option out if you start pppoe-server from an init-script. The -I option gives it the interface to run on, -L configures the local IPv4 address, and -R the first remote address (the server counts up from there for each parallel connection, per default it used up 64 connections and addresses).
The client is fairly easy. Debian (like most Linux distributions) brings a default configuration called dsl-provider. All that needs to be done is check that it uses the correct network device (the -I parameter on the "pty" line), configure the user name (eg. "user fred" - our setup does not require a @provider) and enter the credentials in /etc/ppp/pap-secrets:
fred * flintstone
If you now enter "pon dsl-provider" on your command line you should see a functioning IPv4 connection.
The first thing that needs to happen is for the PPP devices on both sides to realize that they are IPv6-capable. On the server side add a line like this to /etc/ppp/pppoe-server-options:
ipv6 ::1,::2
This gives the server the interface with host ID ::1 and the client host ID ::2. This option actually configures the last 64 bit of the IPv6 addresses or more specifically the EUI-64 of each side. I recommend that you always use ::1 for the server and ::2 for the client - a simple setup like this makes debugging a lot easier and would also be compatible with other services (eg. Sixxs tunnels use the same IDs).
On the client side you now need to add the line "+ipv6" to enable IPv6CP negotiation. Depending on your pppd version and your distributions settings this may already be the default, but it does not hurt to set it explicitly.
As soon as you restart the clients PPP you will have IPv6-capable PPP interfaces on both sides. Meaning they both have a link local fe80::/64 address. Everything else is out of scope for PPPv6. Period. Sorry, thanks for your consideration, move along...
But don't fret, IPv6 gives us plenty of possibilities to go on.
In the following examples I will use the 2001:db8:1::/48 block to assign addresses for the PPP interfaces - this of course assumes that there are no more than about 65000 client users and no double logins of the same user (so real providers need to think a bit more about network assignments than I did).
I put a simple configuration in /etc/ppp/ipv6-addr:
fred:1234 barney:5678
This file assigns each user a static number between 2 and hex ffff (it should be in hexadecimal notation!). The subnets 0 and 1 are reserved for server side networking (2001:db8:0::/48) and PPP interfaces (2001:db8:1::/48). I use the same 16bit ID for both the /64 PPP interface network ID and the /48 client side network - for convenience, again a real world provider will probably use a more complex setup.
The general recommendation from IPv6 experts is to assign static addresses to clients - and that's what this file is doing for us.
The PPP daemon starts specific scripts when it is done with each phase of the connection. For IPv6 this is /etc/ppp/ipv6-up after IPv6CP has completed and IPv6 can be used, and /etc/ppp/ipv6-down when the interface shut down or IPv6 was somehow disabled. On Debian both scripts execute sub scripts in /etc/ppp/ipv6-up.d or /etc/ppp/ipv6-down.d. All scripts receive various variables with status information from pppd, we are interested in $IFNAME (the network device) and $PEERNAME (the PAP-users name).
So we add a start script that configures the local side of the interface and tells the remote side via router advertisements what it looks like:
ADDR=$(grep ^$PEERNAME: /etc/ppp/ipv6-addr |cut -f 2 -d :) if test x$ADDR == x ; then echo "No IPv6 address found for user $PEERNAME" exit 0 fi #configure locally ifconfig $IFNAME add 2001:db8:1:$ADDR::1/64 #generate radvd config RAP=/etc/ppp/ipv6-radvd/$IFNAME RA=$RAP.conf echo interface $IFNAME >$RA echo '{ AdvSendAdvert on; MinRtrAdvInterval 5; MaxRtrAdvInterval 100;' >>$RA echo ' prefix' 2001:db8:1:$ADDR::/64 '{};' >>$RA echo ' RDNSS 2001:db8::1 {}; };' >>$RA #start radvd /usr/sbin/radvd -C $RA -p $RAP.pid exit 0
The first line of that script extracts the users ID from the address file and the if statement below makes sure something was found - otherwise the script simply denies setup. The ifconfig line sets the local IPv6 address, since this will not happen automatically. Then a specific router advertisement configuration is generated for this interface and finally the radvd is started with this config.
The radvd configuration assigns the correct PPP device in the first line, then activates automatic advertisements in the second line (to make sure the client receives at least some of them), configures the correct prefix in the third line and finally tells the client about a DNS server that is reachable on IPv6 (2001:db8::1).
This setup uses a /64 prefix for each PPP connection - effectively wasting 63 bits, since only two ends exist to each PPP connection - but we have plenty of bits and this actually reflects the RFC for PPPv6.
From this point on the clients will automatically configure themselves to the correct addresses and also use the correct router.
Unfortunately this setup risks leaving a lot of radvds running. This can be solved with a simple script for ipv6-down.d that cleans radvd up:
RAP=/etc/ppp/ipv6-radvd/$IFNAME kill `cat $RAP.pid` || true rm -f $RAP.*
Most clients will serve as routers to a complete network. With IPv6 this means they need to get a network ID assigned. IPv6 experts recommend a /48 for end users - that is what I will show here.
The server side configuration only needs one more line for this (insert it below the ifconfig line):
route -6 add 2001:db8:$ADDR::/48 gw 2001:db8:1:$ADDR::2
There is no need to clean it up, since the operating system will automatically delete the route when the PPP device is deleted after the connection ended.
The client does not need additional configuration - it just uses the assigned network to advertise on its local network interfaces (as shown in other articles). That means the provider (or server admin) somehow needs to tell the client user that he can use this sub-net - I leave this exercise in letter writing up to the reader...
DHCPv6 creates very ambivalent feelings among IPv6 experts. On the one hand the main reason for existance for DHCP was removed with IPv6: automatic address assignment. On the other hand DHCPv6 provides a few features that make it useful under some circumstances, but chances are it will become more and more unknown to the admins and owners of SOHO networks. One of those obscure features is prefix delegation (short: PD). Prefix delegation can be used by a DHCP server closer to the root of the network to tell its sub-servers what network prefix they own. One of the main applications of this feature may very well be the delegation of network prefixes from ISPs to their clients.
There are three pretty complete implementations of DHCPv6 (Dibbler, Wide, and ISC) - all of them lack some vital features - eg. ISC does not work on PPP, others are very peculiar about the DUID of the client, etc.pp.
I am working on an implementation that works on PPP and tunnels. As of the time of this writing (october 2009) it is not exactly production quality, but works very well for demo environments.
For the setup above the server would need to start the DHCP-server. The above ip6-up script could be enhanced like this:
DHCP=/etc/ppp/ipv6-dhcp/$IFNAME tdhcpd \ --prefix6=2001:db8:$ADDR::/48 \ --address=2001:db8:1:$ADDR::2 \ --dns-server=2001:db8::1 --dns-name=myisp.local \ --pid-file=$DHCP.pid \ --local-id=myisp.local \ $IFNAME
The parameters are:
The ipv6-down script would of course need to be extended with a kill command for the DHCP daemon.
The client side would need to be extended as well. The ipv6-up side would need to run the dhclient:
tdhcpc --prefix --dns --local-id=barney@myisp.local $IFNAME /etc/ppp/dhcpconfig.sh
The parameters are:
Let's assume our client router serves two networks (eg. a wired LAN and a wireless LAN). We could have a template for the local radvd (say in /etc/radvd.template):
interface eth0 { AdvSendAdvert on; prefix @ADDR@:11::/64 { }; }; interface wlan0 { AdvSendAdvert on; prefix @ADDR@:22::/64 { }; };
To actually let radvd run we would need to replace the "@ADDR@" tag with the real /48 prefix, which could be done in the dhcpconfig.sh script:
#prefix fixing (assuming a single /48) if test x"$PREFIX" != x ; then PFX=$(echo $new_ip6_prefix | sed 's,::/48$,,') sed s,@ADDR@,$PFX, </etc/radvd.template >/etc/radvd.conf /etc/init.d/radvd restart fi #add DNS servers for i in $DNSSRV ; do echo nameserver $i >>/etc/resolv.conf done
Unfortunately Windows XP does not support PPPv6 natively, the only solution here is to either use tunnelling or wait for some third-party vendor to provide a PPPv6 capable driver.
However, Windows Vista does support it - broadband/PPP connections will automatically do IPv6CP and autoconfiguration. It also automatically performs prefix delegation in "Internet Connection Sharing" setups - simply activating ICS in the "sharing" tab of the PPP will make windows look for prefix delegation and advertise the received prefix on the primary network interface of the computer (I have not yet found out how to advertise sub-nets on all interfaces).
I have no idea, whether Windows can be used as a PPP-server though. I somehow doubt it.
So far the server setup consisted mainly in integrating radvd and dhcpd and the client setup was negligible for simple client setups (at least on systems where PPPv6 works at all). On the server side instead of two (pppd, pppoe) now there are four (plus dhcpd and radvd) processes running and the third and fourth one will only consume very small amounts of memory and CPU time (except if grossly misconfigured).
The only complication that might arise on the client side is integrating the /48 sub-net into the local routing table, but this should not be any more difficult than setting up a local DHCP server in IPv4 and can be fairly automatic with pre-loaded routers like they are handed out to customers by most providers.
The setup that I described above is very unsophisticated - among other things it assumes that the clients do not try to subvert the system. A real world system needs to consider for example what to do if a client logs in twice at the same time.
Currently with IPv4 many providers use dynamic address allocation or even NAT to give addresses to clients. What was born out of convenience and necessity has evolved into a business model, selling static addresses for a premium. Providers will have to get used to the idea that this business model is at an end - IPv6 addresses are so cheap and so plentyful that it does not make sense to use either method anymore - in fact it just adds unnecessary costs to an otherwise simple setup.
The allocation of non-static interface addresses might make sense in one specific scenario though: if a client is logged in twice at the same time the second interface could get a random address instead of the preconfigured one - if the provider allows multiple logins at the same time.
The allocation of non-static /48 network prefixes on the other hand does not make any sense whatsoever: there is no good and proven way to communicate a new prefix to the client and the client would have to renumber his entire network just because the PPP connection was re-established. It may however make sense to hand out network prefixes only on request - in quite a few setups many (if not most) clients will not need a network prefix because they connect only one device directly to the network (eg. a laptop connecting via UMTS modem or a single PC connecting directly through DSL).
So far I did only very few experiments with DHCPv6 and address assignments.
The only result so far is that Linux based DHCPv6 clients can receive their address from a DHCPv6 server, while Windows (Vista) is not able to do so (at least not with on-board utilities).
Since DHCP needs extra setup for this it does not make sense to replace radvd with DHCP for address assignement in a PPPv6 environment. However, as we have seen above, prefix delegation (PD) is a viable way to tell the client about its subnet.