IPv6 access
My current ISP, Telia, doesn’t yet fully support IPv6 on their consumer network, but provides a 6rd tunnel for IPv6 networking. However, since I use a Linux box as my main router, I needed to set up the tunnel manually. After searching through forums, I’ve found some examples of that being done, but no direct copy & paste was possible. Most examples were on older distros, some were in languages I couldn’t run and had no knowledge how to edit to do required stuff, and settings were different than what I’ve seen appear on dhcp client log.
The script at the end of page, is what I ended up putting in /etc/dhcp/dhclient-exit-hooks.d/
folder, for DHCP Client to run after ip4 address change.
Script is based on multiple scripts from the past, found on various forums and sites, but none worked for me on Ubuntu 18.
DHCP Data
Depending on distro and ISP, you might need to edit dhclient.conf and explicitly request 6rd-option and provide formatting for it: option option-6rd code 212 = { integer 8, integer 8, array of ip-address };
where first integer is IPv4 mask length for ISP’s block, second integer is mask length for IPv6 6rd addresses, and then IPv6 network address and IPv4 6rd gateway. I did not need to do this, as my ISP sent this option regardless.
For IPv6 forwarding, set net.ipv6.conf.default.forwarding=1 and net.ipv6.conf.all.forwarding=1 or choose per-interface
My ISP actually provides larger prefix than /64 for IPv6. This is an example of content of my /var/lib/dhcp/dhclient.leases I replaced ip addresses with ###.###.###.###.
lease {
interface "enp9s0f1";
fixed-address ###.###.###.###;
option subnet-mask 255.255.240.0;
option routers ###.###.###.###;
option dhcp-lease-time 1200;
option dhcp-message-type 5;
option domain-name-servers ###.###.###.###,###.###.###.###;
option dhcp-server-identifier ###.###.###.###;
option option-6rd 14 38 ####:####:####:: ###.###.255.254;
option ntp-servers ###.###.###.###;
renew 1 2020/11/16 16:54:31;
rebind 1 2020/11/16 17:03:21;
expire 1 2020/11/16 17:05:51;
}
Here option-6rd line syntax is: IPv4 mask bits, IPv6 mask bits, IPv6 prefix, 6rd border router. At the time of scripting I didn’t find docs that would specify this, but luckily in my case I didn’t have to guess much, as there is no way 38 bits could be mask for IPv4 :)
RFC 5969 Does have a full specification if you are interested.
The Script
I utilized ipcalc and ipv6calc to automate calculations on ip subnets. Ubuntu 18 doesn’t allow bash to run this script due to security concerns, so default scripting shell is dash. Which is posix shell, but complicates some stuff, most notably splitting option-6rd to different variables.
Router ADVertisement Daemon is required for network computers to be able to communicate over IPv6, as DHCPv6 does not provide a way to assign the default route.
DHCPv6 is not a requirement, if you do not need to set specific addresses to network devices.
2001:4860:4860::8888 and 2001:4860:4860::8844 are Google’s IPv6 DNS servers. probably should have went with variables…
#!/bin/sh
# NOTE: /bin/sh is dash
# Requirements: ipcalc, ipv6calc
# Options: radvd, dhcpd6
# Should radvd or dhcpd6 be used
use_radvd="yes"
use_dhcpd6="yes"
#dhcpd configuration file (include in dhcpd6.conf) note, apparmor might block this if not located in /etc/dhcp/
dhcpdcfg="/etc/dhcp/dhcpd6-telia.conf"
#radvd configuration file
radvdcfg="/etc/radvd.conf"
# Interface names
LanInterface="br0"
TunInterface="i6rd"
#change to smaller if ::/64 is not available or want to use more smaller networks
LanIPSixSubnet="64"
sixrd_down() {
if [ "x$use_radvd" = "xyes" ]; then
service radvd stop
fi
if [ "x$use_dhcpd6" = "xyes" ]; then
service isc-dhcpd-server6 stop
fi
#destroy tunnel and flush global IPv6 addresses
ip tunnel del $TunInterface > /dev/null 2>&1 || true
ip -l 5 -6 addr flush scope global dev $LanInterface
}
sixrd_up() {
# bash would be nice, but must make do with dash (sh)
IFS=" "
set -- $new_option_6rd
sixrd_masklen=$1
sixrd_prefixlen=$2
sixrd_prefix=$3
sixrd_borderrtr=$4
relayprefix=$(ipcalc -bn $new_ip_address/$sixrd_masklen|sed -En 's/^Network:[[:blank:]]+([0-9./]+).*/\1/p')
# Log params
logger -t dhcp-option-6rd "6rd parameters: 6rd-prefix ${sixrd_prefix}/${sixrd_prefixlen} 6rd-relay_prefix ${relayprefix} br ${sixrd_borderrtr}"
#calculating IPv6 prefix that we can use
delegated_prefix=`ipv6calc -q --action 6rd_local_prefix --6rd_prefix ${sixrd_prefix}/${sixrd_prefixlen} --6rd_relay_prefix ${relayprefix} $new_ip_address`
#ipv6 address and mask for tunnel
ipsix_masklen="$(echo "$delegated_prefix" | awk '{split($0,a,"/"); print a[2]}')"
ifname_ip6addr="$(echo "$delegated_prefix" | awk '{split($0,a,"/"); print a[1]}')1/${ipsix_masklen}"
#ipv6 network and address lan interface
lan_ip6addr="$(echo "$delegated_prefix" | awk '{split($0,a,"/"); print a[1]}')2"
lan_ip6net="$(echo "$delegated_prefix" | awk '{split($0,a,"/"); print a[1]}')"
#Build tunnel and set addresses
ip tunnel add $TunInterface mode sit local $new_ip_address
ip tunnel 6rd dev $TunInterface 6rd-prefix ${sixrd_prefix}/${sixrd_prefixlen} 6rd-relay_prefix ${relayprefix}
ip link set dev $TunInterface up
ip -6 addr add "$ifname_ip6addr" dev $TunInterface
ip -6 route replace default via ::${sixrd_borderrtr} dev $TunInterface metric 1
#Add IPv6 address to lan interface
ip -6 addr add $lan_ip6addr/${LanIPSixSubnet} dev $LanInterface
# Enable routing back to LAN interface
ip -6 route replace "$delegated_prefix" dev $LanInterface metric 1
#Build radvd config file
if [ "x$use_radvd" = "xyes" ]; then
echo "
interface $LanInterface {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
AdvLinkMTU 1280;
prefix ${lan_ip6net}/${LanIPSixSubnet} {
AdvOnLink on;
" > $radvdcfg
fi
#autonomous address allocation on if only radvd is used
if [ "x$use_dhcpd6" != "xyes" -a "x$use_radvd" = "xyes" ]; then
echo "
AdvAutonomous on;
" >> $radvdcfg
else
echo "
AdvAutonomous off;
" >> $radvdcfg
fi
if [ "x$use_radvd" = "xyes" ]; then
echo "
AdvRouterAddr on;
AdvValidLifetime 86400;
AdvPreferredLifetime 86400;
};
RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
};
" >> $radvdcfg
service radvd start
fi
#Build dhcpd6 config file
if [ "x$use_dhcpd6" = "xyes" ]; then
echo "
subnet6 ${lan_ip6net}/${LanIPSixSubnet} {
range6 ${lan_ip6net}1000 ${lan_ip6net}1fff;
option dhcp6.name-servers $lan_ip6addr;
option dhcp6.domain-search \"example.org\";
}
" > $dhcpdcfg
service isc-dhcpd-server6 start
fi
}
case $reason in
BOUND | REBOOT)
if [ -z "$new_option_6rd" ]; then
logger -t dhcp-option-6rd "No 6rd option in response"
sixrd_down
return
else
sixrd_down
sixrd_up
fi
;;
RENEW | REBIND)
if [ "$new_ip_address" != "$old_ip_address" ]; then
if [ -z "$new_option_6rd" ]; then
logger -t dhcp-option-6rd "No 6rd option in response"
sixrd_down
return
else
sixrd_down
sixrd_up
fi
fi
;;
STOP | EXPIRE | FAIL | RELEASE)
sixrd_down
;;
esac