I use FC as my Internet router through Comcast. For the last 5 years I had run a 6to4 tunnel, but recently I read that Comcast was deploying IPV6 natively so I decided to give it a try.
Hopefully my experience in transitioning to a dual stack using IPV4 and IPV6 from a 6to4 tunnel will help others.
There are plenty of articles and blogs stating how to do it but my situation once again was a bit unique for the following reasons:
- I am running kernel 2.6.26, I know it is a bit behind but my motto is “if isn’t broken do not fix it”. So as long as I patch software so I do not expose the server it is ok. Having an older kernel though will bite me.
- While I was running a 6to4 tunnel so IPV6 worked, DHCLIENT seem not to have IPV6 support, running DHCLIENT would not obtain a prefix for example. That was odd since it was supposed to have support.
- Some of the configurations I saw were for DEBIAN or (Ubuntu) but not FC although adapting them was easy enough.
So I decided to take the plunge!
The steps I show are not new, other blogs and forums have similar instructions for several devices. I decided to follow the ones in this blog, mainly because it was for Debian which could allow me to modify the scripts to run out of rc.local
Request an Address from Comcast
Get a Prefix
I use BIND internally so I did recompile it so it would support IPV6 (so I could add HOST records to it, the subject hopefully of another post), and of course recompiled DHCLIENT and DHCLIENT-SCRIPT from the latest ICS software to make sure it would do what was needed.
So next I did stop the 6to4 tunnel and rebooted (do not need to do that but wanted to make sure that no lingering routes remained and RADVD was not handling address).
So first we need to know what addresses we are getting.
You use DHCLIENT for that but you really want a prefix not jus and address and in addition you will be requesting a prefix on your WAN address but you will actually put that address on your LAN interface, confused? Do not be it will be explained shortly.
So issue:
dhclient -P -d -v -1 $EXTERNAL_INTERFACE
You will see something like this:
ia-pd 88:27:52:e7 { starts 1370574554; renew 157751; rebind 252402; iaprefix $PREFIX::/64 { starts 1370574554; preferred-life 315503; max-life 315503; } }
The important detail here is “iaprefix” follow by the actual network that Comcast is giving you.
Assign an Address to your Internal Interface
So configure the $INTERNAL_INTERFACE with an IP address like:
ip addr add $PREFIX::1/64 dev $INTERNAL_INTERFACE
So if the prefix is 2001:A:A:A::0/64 then use 2001:A:A:A::1/64.
Accept Router Advertisements
Now you need to accept Router Advertisements (RA) from Comcast so FC knows how to route to the internet. This is where having an old kernel bit me.
The RFC states that if you are a router you shouldn’t accept router advertisements (RA) the rationale being that you are a router or you are a host, but you are not both. Thus, even if you set net.ipv6.conf.all.accept_ra=1, if you also have ip_forward=1, Linux will silently drop all RAs.
In our case, however we want to accept router advertisements on our external interface and send our own router advertisements on our internal interface.
Fortunately, Linux added a new value to accept_ra which accepts RAs even if ip_forward is on and that value is 2.
Sadly, in kernels before that was added (anything before 2.6.37), a value of 2 is accepted, but it has no benefit (it’s treated the same as 1). In these older kernels you can set ip_forward=0 on the external interface and 1 on the internal interface and packets will still be forwarded and you will accept RAs. See this blog for more information.
So you need to disable forwarding:
sysctl -w net.ipv6.conf.$EXTERNAL_INTERFACE.forwarding=0
Then set RA to 2:
sysctl -w net.ipv6.conf.$YOUR_EXTERNAL_INTERFACE.accept_ra=2
You should now have connectivity. Try pinging ipv6.google.com.
How are we routing? Well via the link-local address on our external interface since it receives a Router Notification of where to send packets (this is how IPV6 routes, there is not need to set a default route).
Set Radvd for Internal Hosts
IPV6 will use auto-configuration when allowed. This is done using the Router Advertisement Daemon (RADVD). Install it if you do not have it.
Then open /etc/radvd.conf and set the following:
interface INTERNAL_INTERFACE {
AdvSendAdvert on;
RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
prefix PREFIX
{
AdvOnLink on;
AdvAutonomous on;
};
};
This configuration uses Google DNS servers. Now start radvd.
Automate Everything
Of course you would not like to this every time that you reboot so you need a few scripts at boot time.
Make the scripts
First a script to obtain the prefix, you need a second instance of DHCLIENT that requests a prefix:
#!/bin/bash IFACE="external interface" # If there's a stale dhclient, kill it pid=`ps -ef | awk '/[d]hclient -6/{print $2}'` /bin/kill $pid # Start our new one /sbin/dhclient -6 -P -1 -pf /var/run/dhclient6.$IFACE.pid \ -lf /var/lib/dhclient/dhclient6.$IFACE.leases $IFACE & exit 0
Save it as start-dhclient, make it executable and call it from /etc/rc.local for example so it will be run at boot.
Now save the following script as ipv6 and also run it after start-dhclient.
#!/bin/bash # # For radvd, it takes /etc/radvd.conf.tmpl, replaces "__PREFIX__" with your # prefix, and - if it's different from /etc/radvd.conf - replaces the config # file and restarts the daemon. # INT_IFACE="LAN Interface" EXT_IFACE="WAN Interface" LEASE="/var/lib/dhclient/dhclient6.$EXT_IFACE.leases" new_ip6_prefix=$(tail $LEASE | /usr/bin/awk '/iaprefix/ {print $2}') current_ip=$(/sbin/ip -6 addr show dev $INT_IFACE scope global |\ /usr/bin/awk '/inet6/ {print $2}') current_prefix=$(echo $current_ip | /bin/sed -e 's@::1/64@::/64@') if [ "$current_prefix" == "$new_ip6_prefix" ] ; then echo "Prefix is the same. Nothing to do" exit fi # Setup the new IP echo "Prefix is different or IPV6 address not setup. Setting up" new_ip=$(echo $new_ip6_prefix | /bin/sed -e 's@::/64@::1/64@g') if [ ! -z "$current_ip" ] ; then /sbin/ip -6 addr del $current_ip dev $INT_IFACE fi /sbin/ip -6 addr add $new_ip dev $INT_IFACE # Ensure we'll get router advertisements /sbin/sysctl -w "net.ipv6.conf.$EXT_IFACE.accept_ra=2" # Needed for kernels before 2.6.37, don't worry, # forwarding will still work as long as you have it set # on your other interface. /sbin/sysctl -w "net.ipv6.conf.$EXT_IFACE.forwarding=0" # Update radvd tmpfile=/tmp/radvd.conf.$$ sed -e "s@__PREFIX__@$new_ip6_prefix@g" /etc/radvd.conf.tmpl > $tmpfile diff $tmpfile /etc/radvd.conf >/dev/null if [ $? == 1 ]; then mv $tmpfile /etc/radvd.conf /etc/init.d/radvd restart else rm $tmpfile fi
Create a file called /etc/radvd.conf.tmpl as follows:
interface INTERNAL_INTERFACE {
AdvSendAdvert on;
RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
prefix __PREFIX__
{
AdvOnLink on;
AdvAutonomous on;
};
};
The script ipv6 does the following:
- Assigns an IP from the prefix to the internal interface
- Sets forwarding and ra accordingly
- Configures radvd
Finally check periodically that our prefix has not changed, drop ipv6 and start-dhclient under /etc/crond.daily so they run every night at 4 am. I have found that is often enough otherwise you need to check more often.
Final Thoughts
After doing above you should have a dual stack.
- You need to call DHCLIENT twice once for IPV4 and once for IPV6.
- You need to allow UDP traffic for DHCP requests, to 546, from 547 on the link-local address network fe80::/64.
- Unfortunately unless we run Dynamic DNS if the prefix changes your DNS entries will be wrong.
- Finally and I cannot stress this enough you need to have IP6TABLES running as soon as you have IPV6 working correctly. Otherwise your devices will be exposed to the Internet.
Happy dual stack.