Introduction
One thing which isn't well understood is the big number of moving parts required to get IPv6 working with your ISP. This is a quick review of what happens under the hood of your ADSL router.
Four addresses, four addressing mechanisms
Firstly, PPP Over Ethernet link control protocol allocates the link-local addressing for the PPP link. It assigns a local link-local address to the customers interface and a remote link-local address to the ISP's interface. That gives us a medium we can use to exchange IPv6 packets between the ISP and the ADSL router.
Secondly, stateless address autoconfiguration (SLAAC) assigns a global address to the PPP link between the ISP and the ADSL router. That requires both ICMPv6 and link-layer multicast to be in working order, a point often forgotten when configuring IPv6 firewalls. When autoconfiguration installs this global address from the ISP it also installs the IPv6 default route from the ISP.
Thirdly, DHCPv6 runs over the PPP link to inform the ADSL router of the IPv6 address prefixes to use for the customer LANs on the interior-facing interfaces of the ADSL router. This DHCPv6 exchange also configures routing upon the upstream ISP equipment. The implication is that if DHCPv6 stops, then your internet stops when the DHCPv6 lease expires.
The implication is that you need to take some care with which addresses are used to advertise services. If they are advertised on the prefix delegated by the ISP and that delegation disappears then the service is unreachable. For example, you may not be able to print if your ADSL link goes down. Advertising services on the link-layer address is also a bit fraught, as then the service isn't available to downstream subnets within the site.
Fourthly, there is also IPv6 link-layer addressing on each of the interior interfaces of the ADSL router, configured in the usual way for ethernet interfaces
The scenario
So much for the theory, let's have a look at the practice. I use a RaspberryPi computer as the ADSL router. It contains these interfaces:
eth0 An interior LAN interface. A fast ethernet interface to a fast ethernet switch. The house's access point, computers and printers plug into this switch.
eth1 An exterior interface to the ISP. This is a USB interface to a D-Link DSL-526B ADSL modem, configured as a bridge. The USB port carries ethernet frames to the modem using the CDC protocol.
The Raspberry Pi's BCM2835 system-on-a-chip has one USB port. This USB bus connects across the printed circuit board to a LAN9512, which places a USB hub with one ethernet controller and two USB ports onto the bus. As a result there is no performance using the ADSL modem's ethernet port or its CDC USB port: either way the traffic enters the Raspberry Pi's BCM2835 SoC as ethernet-over-USB.
Raspbian, a Debian Wheezy Stable tuned to the ARM variant contained within the Raspberry Pi's BCM2835, is the obvious choice of operating system for this sort of fun. Just for the record it has ppp-2.4.5-5.1, ifupdown-0.7.8, and raspberrypi-bootloader-1.20130617-1 containing Linux kernel "3.6.11+ #474".
Forwarding
In Linux hosts don't forward out-of-the box. Let's turn that on by editing /etc/sysctl.conf
net.ipv6.conf.all.forwarding=2 net.ipv6.conf.default.forwarding=2
What's this special value "2"? Originally the value was "1", but this disabled autoconfiguration on all interfaces. That is, you couldn't appear to be a router on some interfaces and appear to be a host on other interfaces. But that's exactly the mental model of a ADSL router. (This is the trouble you make when you ignore the UNIX tenet that the system administrator knows what they are doing.)
There's a similar trap with enabling autoconfiguration, it disables routing. So another special value (sigh):
net.ipv6.conf.all.accept_ra = 2 net.ipv6.conf.default.accept_ra = 2
The keyword all
sets the value on all current interfaces. The keyword default
sets the value on all future interfaces. We need both since we don't know when sysctl -p is run to make the file's contents take effect.
Interior interface, eth0
Debian configures its interfaces in /etc/network/interfaces. It typically has private IPv4 addressing which iptables NATs to the outside, looking something like this:
auto eth0 iface eth0 inet static address 192.168.255.254 netmask 255.255.255.0 broadcast 192.168.255.255
My ISP, Example.Net, uses static prefix delegations. So I can statically address the interior interface. This is good as we can run services on the interior interface without its global addressing being removed.
If your ISP doesn't do this then you should allocate a random /64 subnet from fc00::/7, RFC4193 has the details. You'll then need to bind local services running on the ADSL gateway -- such as printer spooling -- to the RFC4193 subnet rather than to the prefix delegated globally-reachable address. You won't be able to access these services from the Internet, but that was your ISP's intention in allocating a dynamic address, but at least you'll be able to reach the local services when the ADSL link is down.
Let's say I was allocated 2001:0db8:1234:abcd::/56. (Not really, because this is within the documentation prefix. But hey, this is documentation.) Set /etc/network/interfaces to add:
auto eth0 iface eth0 inet6 static address 2001:0db8:1234:abcd::1 netmask 64 autoconf 0 accept_ra 0 privext 0
You see that we've used subnet 00. Use whatever subnet you want of the allocation you have been given. It's just that zero leads to less typing. Note IPv6's convention of placing the router on address ::1.
You'll see that we've turned off autconfiguration for this interface. That makes sense, as we've statically configured the interface. But as usual with Linux flipping autoconf bits then flips forwarding bits :-( This hack puts things back to rights:
post-up /sbin/sysctl -w net.ipv6.conf.eth0.forwarding=2 post-up /sbin/sysctl -w net.ipv6.conf.default.forwarding=2 post-up /sbin/sysctl -w net.ipv6.conf.default.accept_ra=2
Exterior interface, eth1
We don't need IP addressing on the ethernet interface to the ADSL modem. This ethernet link just carries PPP frames.
We then add an ISP to that configuration:allow-hotplug eth1 iface eth1 inet manual pre-up /sbin/ifconfig eth1 up post-down /sbin/ifconfig eth1 down
allow-hotplug eth1 iface eth1 inet manual pre-up /sbin/ifconfig eth1 up up ifup ppp0=example down ifdown ppp0=example post-down /sbin/ifconfig eth1 down iface example inet ppp provider example
PPPoE over exterior interface, ppp0
Configured in /etc/ppp/peers/example:
You'd have all of the regular features:noipdefault defaultroute replacedefaultroute hide-password noauth persist plugin rp-pppoe.so eth1 usepeerdns user "vk5tu@example.net" bsdcomp 15 deflate 15
together with authentication in /etc/ppp/chap-secrets:
"vk5tu@example.net" * "Zgz4T3CRCGhH6G7Zf8mb0vIO"
Now add these to /etc/ppp/peers/example for IPv6:
+ipv6 ipv6cp-use-ipaddr ipv6 ,
Note that comma carefully, it is needed.
Restart PPP with ifdown ppp0=example && ifup ppp0=example and look in the log messages for link-layer address assignments.
pppd[]: pppd 2.4.5 started by root, uid 0 pppd[]: PPP session is 3 pppd[]: Connected to 00:11:22:33:44:55 via interface eth1 pppd[]: Using interface ppp0 pppd[]: Connect: ppp0 <--> eth1 pppd[]: CHAP authentication succeeded pppd[]: peer from calling number 00:11:22:33:44:55 authorized pppd[]: local LL address fe80::255:44ff:fe33:2211 pppd[]: remote LL address fe80::211:22ff:fe33:4455 pppd[]: local IP address 192.0.2.44 pppd[]: remote IP address 198.51.100.199 pppd[]: primary DNS address 203.0.113.1 pppd[]: secondary DNS address 203.0.113.2
ISP link global addressing and default route, ppp0
ICMPv6 is used to implement IPv6 stateless automatic address configuration (SLAAC). The router advertises the subnet's prefix and the hosts add their own host addressing to that. To aid in boot time the host and prompt the router to immediately send an advertisement.
Note that these advertisements are link-layer multicasts. Some with global source addresses. It is very easy to shoot yourself in the foot with your firewall rules here. The symptom is a delayed loss of the default route, seemingly for no apparent reason.
The use of SLAAC is set using Linux's sysctl. You want these, which should already be set:
net.ipv6.conf.ppp0.disable_ipv6 = 0 net.ipv6.conf.ppp0.forwarding = 2 net.ipv6.conf.ppp0.autoconf = 1 net.ipv6.conf.ppp0.accept_ra = 2 net.ipv6.conf.ppp0.accept_ra_defrtr = 1 net.ipv6.conf.ppp0.accept_ra_pinfo = 1 net.ipv6.conf.ppp0.accept_redirects = 1 net.ipv6.conf.ppp0.accept_source_route = 0
To check operation use ip -f inet6 addr show to view the interface's addressing:
$ ip -f inet6 addr show dev ppp0 4: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qlen 3 inet6 2001:44b8:10:868:192b:b4b7:d760:9879/64 scope global dynamic valid_lft 5237sec preferred_lft 5237sec inet6 fe80::192b:b4b7:d760:9879/10 scope link valid_lft forever preferred_lft forever
Use ip -f inet6 route show to view the interface's routes.
$ ip -f inet6 route show dev ppp0 2001:44b8:10:868::/64 proto kernel metric 256 expires 4585sec fe80::/64 proto kernel metric 256 fe80::/10 metric 1 fe80::/10 proto kernel metric 256 default via fe80::224:14ff:fe9a:9810 proto ra metric 1024 expires 4525sec
If a default route isn't present then tcpdump -i ppp0 -n -v is the fastest diagnostic tool.
The SLAAC-assigned interface is dynamic. Furthermore the ISP might block access to it from the global internet. Unlike IPv4, do not use the address of the exterior interface for providing services on IPv6. Use one of the addresses from the prefix delegation. Subnet 0 is a good choice for this purpose.
ISP prefix delegation for the interior interfaces
Prefix delegation gives some addressing for you to use within your home network. You can allocate differing subnets for differing purposes. Some people like to run wired and wireless networks on different subnets to minimise the number of wireless broadcasts, which are transmitted at a low speed and thus rob wireless time from hosts doing real work.
You need to know in advance how many bits of subnetting (technically, "site local aggregator addressing") the ISP is giving you. 4, 8 and 16 are common values. For a simple network you can look at these as subnet numbers. If you use VLANs then it's a good plan to have the SLA address match the VLAN number. If you have a simple network then just use subnet 0 to make your typing simpler.
You also need to decide which IP address (technically, "interface identifier") you will use for your router's interior interfaces. ...::1 is a common and good choice and what we use in this example.
As a concrete example, we will use subnet 1 (to match the default VLAN ID) and address ::1 for the single interior interface eth0.
The ISC DHCPv6 client doesn't work over PPP links, due to a long-standing bug.
I used the WIDE DHCPv6 client. Debian packages this well, with all the necessary helper scripts out of the box.
Set /etc/wide-dhcpv6/dhcp6c.conf:
profile default { request domain-name-servers; request domain-name; script "/etc/wide-dhcpv6/dhcp6c-script"; }; interface ppp0 { # Request a prefix delegation send ia-pd 1; }; id-assoc pd 1 { prefix-interface eth0 { # 8 bits for subnetting sla-len 8; # Our subnet is VLAN 1 sla-id 1; # Our interface has address ...::1 ifid 1; }; };
WIDE DHCPv6 is very quiet. If you want to debug it then hack the /etc/init.d/wide-dhcpv6-client script to add the -D debug parameter.
If you run internal services then you can set up a eth:0 interface and use subnet 0 for it. It's common for services to bind to particular addresses: ...::100 might be a web server, ::101 might be the mail server, and so on.
Firewalls
We are not NATing, so no firewall is required for operation. Of course we do want to keep the greater internet out of our pictures of Aunt Mabel's wedding reception. So we do want to run a stateful firewall.</p>I find it attractive to run a stateful firewall for connections which are forwarded through the router, but not those connecting to it. I then configure those services running on the router to reject improper usage. In this case you want the expectation-making rule in the FORWARD chain, as well as the expectation-enforcement rule for outgoing and incoming traffic.
ip6tables -A FORWARD -i eth0 -o ppp0 -m state --state NEW -j ACCEPT ip6tables -A FORWARD -i eth0 -o ppp0 -m state --state RELATED,ESTABLISHED -j ACCEPT ip6tables -A FORWARD -i ppp0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
This simple rule checks for FORWARDed ICMPv6, discarding incoming ping6:
-A check-forward-icmp -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j accept-forward-icmp -A check-forward-icmp -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j accept-forward-icmp -A check-forward-icmp -p ipv6-icmp -m icmp6 --icmpv6-type 3 -j accept-forward-icmp -A check-forward-icmp -p ipv6-icmp -m icmp6 --icmpv6-type 4 -j accept-forward-icmp -A check-forward-icmp -i ppp0 -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j reject-forward-icmp -A check-forward-icmp -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j accept-forward-icmp -A check-forward-icmp -p ipv6-icmp -m icmp6 --icmpv6-type 129 -j accept-forward-icmp -A check-forward-icmp -j reject-forward-icmp
Some comments
This is obviously more complex than with IPv4. Which should not be the case considering that IPv4 is NATing and IPv6 is routing. The major complexity and fragility is the Prefix Delegation, and doing this through SLAAC would have been a better design.
The software design is poor. The Linux kernel does its best to defeat being simultaneously a host and a router, which is the typical design for a ADSL "router". The WIDE and Dibbler DHCPv6 clients both fail when ppp0 losses carrier, rather than opening a new connection on the interface and trying again.
Errata: Router advertisement to interior network
[Whoops, left this out]
How does a host on the internal network automatically configure? It listens for advertisements from routers. Install the package radvd. Configure it to advertise the subnet's prefix and prefix length, the router's address, the MTU, and the DNS server details. radvd is fussy about the format of it's configuration file /etc/radvd.conf
interface eth0 { AdvSendAdvert on; IgnoreIfMissing on; AdvLinkMTU 1500; AdvDefaultPreference high; prefix 2001:0db8:1234:abcd::/64 { AdvOnLink on; AdvAutonomous on; }; RDNSS 2001:0db8:1234:abcd::1 { }; DNSSL example.id.au { }; };
July 4 2013, 08:23:42 UTC 7 years ago
net.ipv6.conf.all.forwarding=2
net.ipv6.conf.default.forwarding=2
There is a slight race condition in between the two. "all" includes all existing interfaces, but not future interfaces. "default" includes all future interfaces, but no existing interfaces.
So if an interface (i.e. ppp0) is created in between the two statements, it will not have that applied. To do it race-free, just reverse the order:
net.ipv6.conf.default.forwarding=2
net.ipv6.conf.all.forwarding=2
SLAAC
October 28 2014, 09:07:40 UTC 6 years ago
When using SLAAC you may want to enable some privacy as this is made up of the prefix and based on hardware mac address which can make it easy to be tracked.
In this case you may want to add to you sysctl.conf file
net.ipv6.conf.eth1.use_tempaddr = 2
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
which will randomize the part that is based on your hardware mac address
RE: SLAAC
October 31 2014, 21:52:41 UTC 6 years ago
1) My machines don't move. Therefore the network prefix does not change, so the activity can still be related to my subscription. If my machines moved around more often then there would be a gain in privacy.
2) Given (1) I'll take the convenience of being able to translate MAC address (often known) to IPv6 address. This makes the initial login to newly imaged devices very simple (no need to check DHCP servers, etc).
3) Privacy addresses are a poor choice for servers. Including ssh servers. Losing the ability to ssh to machines is more of a security concern to me than losing some minor (see (1)) privacy.
The default method of determining a IPv6 address will change to, essentially, prefix:random, where the random host portion is permanently maintained by the operating system after initial generation. In my view this has been woefully undertested (there will be a whole class of computers lacking sufficient randomness upon first-ever boot) and we'll pay the costs of that in future network design (eg, it won't be possible to have a large Link Layer of CPE devices).
DHCP
Anonymous
June 1 2015, 02:04:49 UTC 5 years ago
My setup is basically the same: ppp connection on eth1, local network on eth0. The ISP assigned me two networks: a /64 for the outside (ppp0) and a /56 for the inside (eth0). The outside address is assigned during the ppp connection, and I assigned an address from the /56 pool manually to eth0. I enabled forwarding and had the firewall wide open. Now I thought that I should be able to ping the internal address from another machine somewhere else. Didn't work. In Wireshark I saw nothing arriving on ppp0. Turns out that my ISP enables a route from the /64 to the /56 only when I request a /56 address from them with dhcp6c. On the router machine, I have to use dhcp6c just for that reason. Other machines in the local network are fine with manual configuration.
In /etc/ppp/peers/example I am using:
# enable IPv6
+ipv6
# local part of the IP address, lower 64 bit; this should give me a fixed IP
ipv6 ::1
# force pppd to create ppp0. I have another connection with unit 1 for ppp1
# in order to configure the fire wall, I need to know which one is which
unit 0
# no IPv4
noip