Dynamically-Routed IPv6 With Multiple Subnets Using tinc and Quagga

Michael Adams, 2-22-2010, 11-27-2011 (current rev)

Introduction

With the advent of Windows 7, advanced IPv6 support in Linux and OSX, and the exhaustion of IPv4 addresses, it becomes a matter of urgency to transition to IPv6 networking. This document is a successor to one I wrote back in 2007 that demonstrated static networking of a single subnet.

Platforms and software for this reference

1. tinc: a mesh-capapble VPN software created in the 2000s. This example should work with any 1.0.x version or later.
2. Quagga: an open-source routing software. Version 0.98 or later is required: version 0.99.x highly suggested!
3. Low-capacity sites may successfully use routers reprogrammed with OpenWRT: 8.09.x & 10.03 won't work, but nightlies and latest stable builds will.
4. Higher-capacity or main node sites should use Debian or Ubuntu Linux: latest version of either preferred.
5. I have tried using ClearOS as a platform: it works, but you'll need to hunt down the latest RPM files for RHEL or CentOS for tinc & Quagga; Fedora RPMs don't work with these variants.

Miscellaneous tools

1. IPv6 workstation toolset : includes configuration batch files, copies of stone for 32-bit and x64 Windows, and a 3rd-party LPD for XP.
2. VNC viewer for IPv6: the original author's website went down in 2010; I have a copy of it.

Getting your networks

For this setup, you will use have two networks: one internal and one to access Internet with. Internal network: read up on unique local addressing, then generate a ULA range using SixXS or KAME.
External network: obtain a /48 or /56 from your ISP, else sign up for tunnel access with Hurricane Electric or SixXS.

Table of abbreviations


PASSWORD Any password suitable for the config file in question.
LOCALNET Interface name that will host local subnets: usually "br-lan" on OpenWRT or "eth0" on Debian/Ubuntu.
GLOBAL First 3 parts of a globally-accessible IPv6 range, i.e. "2001:db8:beef".
ULA First 3 parts of an internally-routed IPv6 range.
SITENAME Name of the site being configured. In tinc, its also used to associate with hostname files.

System configuration

OpenWRT

1. Program the router and configure its local IPv4 setup as you need it via the web interface.
2. Using the web interface, add an exception for port 655 TCP+UDP to the firewall.
3. Using telnet/SSH, prepare the router with the necessary software. Future steps, I assume you'll use nano to edit files.

opkg update
opkg install kmod-ipv6 kmod-ip6tables kmod-tun ip nano libreadline iptables-mod-extra kmod-sit alive6 6tunnel quagga-ospf6d tinc
opkg install luci
opkg install luci-sgi-uhttpd
/etc/init.d/uhttpd enable
4. Edit /etc/config/uhttpd to remove comments on the [::] lines.
5. Write the sysctl.conf, quagga files, and tinc files as needed.
6. Sign the local tinc host file with tincd -n link -K
7. Add tincd -n link & /etc/init.d/quagga restart to /etc/rc.local

Debian/Ubuntu

1. iptables firewall rules to add...

-A INPUT -p tcp -m tcp -m multiport -j ACCEPT --dports 655
-A INPUT -p udp -m udp -m multiport -j ACCEPT --dports 655

2. apt-get install tinc quagga
3. Write the sysctl.conf, quagga files, and tinc files as needed.
4. Sign the local tinc host file with tincd -n link -K
5. Add /etc/init.d/tinc restart & /etc/init.d/quagga restart to /etc/rc.local

/etc/sysctl.conf

OpenWRT Debian/Ubuntu
kernel.panic=3
net.ipv4.conf.default.arp_ignore=1
net.ipv4.conf.all.arp_ignore=1
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.icmp_ignore_bogus_error_responses=1
net.ipv4.tcp_fin_timeout=30 net.ipv4.tcp_keepalive_time=120
net.ipv4.tcp_syncookies=0
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_rfc1337=1
net.ipv4.route.flush=1
net.ipv4.tcp_window_scaling=1
net.ipv4.tcp_sack=1
net.ipv4.tcp_fack=1
net.ipv4.tcp_low_latency=1
net.ipv4.tcp_frto=2
net.ipv4.tcp_frto_response=2
net.ipv4.tcp_workaround_signed_windows=1
net.ipv4.tcp_mtu_probing=1
net.netfilter.nf_conntrack_checksum=0
net.netfilter.nf_conntrack_max=16384
net.netfilter.nf_conntrack_tcp_timeout_established=3600
net.netfilter.nf_conntrack_udp_timeout=60
net.netfilter.nf_conntrack_udp_timeout_stream=180
net.netfilter.nf_conntrack_checksum=0
net.netfilter.nf_conntrack_max=16384
net.bridge.bridge-nf-call-arptables=0
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0
vm.min_free_kbytes=1024
kernel.printk = 4 4 1 7
fs.inotify.max_user_watches = 524288
net.ipv6.conf.all.forwarding=1
net.ipv4.ip_forward=1
net.ipv4.icmp_echo_ignore_broadcasts = 0
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.tcp_syncookies=0
net.core.rmem_max=524288
net.core.wmem_max=524288
net.ipv4.tcp_rfc1337=1
net.ipv4.route.flush=1
net.ipv4.tcp_window_scaling=1
net.ipv4.tcp_sack=1
net.ipv4.tcp_fack=1
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_low_latency=1
net.ipv4.tcp_ecn=1
net.ipv4.tcp_fin_timeout=10
net.ipv4.ip_no_pmtu_disc=0
net.ipv4.tcp_congestion_control=yeah
net.ipv4.tcp_frto=2
net.ipv4.tcp_frto_response=2
net.ipv4.tcp_workaround_signed_windows=1
net.ipv4.tcp_mtu_probing=1
net.core.somaxconn=5000
net.core.netdev_max_backlog=5000
net.ipv4.conf.default.rp_filter=1
net.ipv4.tcp_mtu_probing=1
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1

Quagga configs

For this to work, we will be using OSPFv3. OSPF routers use decimal notation for its purposes, but IPv6 addresses are hexadecimal, so you can either choose to use the same number (delinated as "#") for both, or you can use the hexidecimal number of # for the IPv6 ranges (example: 0.0.0.11 would represent 2001:db8:beef:b::/64).

If you're using DHCPv6 for DNS (via Dibbler or ISC DHCP 4.x), you can add "ipv6 nd other-config-flag" as a command to zebra.conf (after the "suppress ra" command). There is a sample config for your DHCPv6 server at the bottom of this tutorial.

/etc/quagga/daemons (Debian/Ubuntu systems)

zebra=yes
bgpd=no
ospfd=no
ospf6d=yes
ripd=no
ripngd=no
isisd=no

/etc/quagga/ospf6d.conf

password PASSWORD
!
!adapter setup
!
interface LOCALNET
interface vpn6
!
!router setup
!
router ospf6
router-id 0.0.0.#
redistribute static
redistribute connected
area 0.0.0.# range GLOBAL:#::/64
area 0.0.0.# range ULA:#::/64
interface LOCALNET area 0.0.0.#
interface vpn6 area 0.0.0.0

/etc/quagga/zebra.conf

hostname SITENAME
password PASSWORD
interface LOCALNET
link-detect
no ipv6 nd suppress-ra
ipv6 nd ra-interval 10
ipv6 address GLOBAL:#::ffff/64
ipv6 address ULA:#::ffff/64
ipv6 nd prefix GLOBAL:#::/64
ipv6 nd prefix ULA:#::/64
interface vpn6
link-detect
ipv6 address ULA::#/64
ipv6 address GLOBAL::#/64
interface lo
link-detect
ipv6 forwarding
ipv6 route ULA:#::/64 LOCALNET
ipv6 route GLOBAL:#::/64 LOCALNET

tinc configs

On the "master" node, it must have a copy of the hostfile from each of the other nodes, plus its own. The nodes only require the master + their own. As for the hostfile, older versions of tinc support a "TCPOnly" flag, and all versions support a compression flag (0 none,1-9 gzip,10-11 zlib).

/etc/tinc/nets.boot

link

/etc/tinc/link/tinc.conf

Name=SITENAME
Device=/dev/net/tun
Mode=switch
Interface=vpn6
ConnectTo=master

/etc/tinc/link/tinc-up ("chmod a+x" this file)

#!/bin/sh
ip -6 link set vpn6 up mtu 1280
ip link set vpn6 qlen 4096

/etc/tinc/link/tinc-down ("chmod a+x" this file)

#!/bin/sh
ip -6 link set vpn6 down

/etc/tinc/link/hosts/master

Address=IP ADDRESS OF MASTER NODE
Port=655
Compression=9
TCPOnly=no

*tinc-generated SSL key*

/etc/tinc/link/hosts/SITENAME

Port=655

*tinc-generated SSL key*

Sample DHCPv6 config

option dhcp6.domain-search "DOMAIN";
option dhcp6.name-servers SERVER1, SERVER2;
default-lease-time 3600;
max-lease-time 7200;
authoritative;
log-facility local7;

#subnet6 ULA::/64{
#allow unknown-clients;
#}

subnet6 GLOBAL::/64{
allow unknown-clients;
}