dhcpcd-discuss

6rd option support for dhcpcd

Jeroen Vreeken

Fri Dec 02 15:36:10 2011

Hi,

Recently my ISP began IPv6 trials using 6rd.
They use the relatively new 6rd dhcp option for this.
I attached a patch against dhcpcd 5.2.12 to add support for this option.
For example in my case I now get the following 6rd variable for my hooks:

new_6rd='16 40 2a01:0670:6d00:0000:0000:0000:0000:0000 88.159.90.37'

I also created a hook called '40-6rd' to handle the option and bring up (or change) an 6rd tunnel. I didn't include it in the patch since it depends on a few tools of my own. (You can find them at http://video.vreeken.net/sixcalc/ )

On my system (running slackware) which acts as a router for my home network I now use the following setup:

    dhcpcd:
        Receives the option and decodes it
    40-6rd:
Uses the option fields and a tool called 'sixrdcalc' to bring up an 6rd interface
    /etc/rc.d/rc.inetsix1:
        Setup LAN interfaces with the delegated preffix.

Regards,
Jeroen

diff -ruN dhcpcd-5.2.12/dhcp.c dhcpcd-5.2.12-6rd/dhcp.c
--- dhcpcd-5.2.12/dhcp.c	2011-04-06 10:25:51.000000000 +0200
+++ dhcpcd-5.2.12-6rd/dhcp.c	2011-11-24 21:04:34.000000000 +0100
@@ -50,6 +50,7 @@
 #define RFC3361	(1 << 10)
 #define RFC3397	(1 << 11)
 #define RFC3442 (1 << 12)
+#define RFC5969 (1 << 13)
 
 #define IPV4R	IPV4 | REQUEST
 
@@ -159,6 +160,7 @@
 	{ 114,	STRING,		"default_url" },
 	{ 118,	IPV4,		"subnet_selection" },
 	{ 119,	STRING | RFC3397,	"domain_search" },
+	{ 212,  RFC5969,	"6rd" },
 	{ 0, 0, NULL }
 };
 
@@ -267,7 +269,8 @@
 
 		if (opt->type == 0 ||
 		    opt->type & STRING ||
-		    opt->type & RFC3442)
+		    opt->type & RFC3442 ||
+		    opt->type & RFC5969 )
 			return 0;
 
 		sz = 0;
@@ -636,6 +639,74 @@
 	return sip;
 }
 
+/* Decode an RFC5969 6rd order option into a space
+ * separated string. Returns length of string (including
+ * terminating zero) or zero on error. */
+static ssize_t
+decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+	uint8_t ipv4masklen, ipv6prefixlen;
+	uint8_t ipv6prefix[16];
+	uint8_t br[4];
+	int i;
+	ssize_t b, bytes = 0;
+
+	if (pl < 22) {
+		errno = EINVAL;
+		return 0;
+	}
+	
+	ipv4masklen = *p++;
+	pl--;
+	ipv6prefixlen = *p++;
+	pl--;
+	
+	for (i = 0; i < 16; i++) {
+		ipv6prefix[i] = *p++;
+		pl--;
+	}
+	if (out) {
+		b= snprintf(out, len,
+		    "%d %d "
+		    "%02x%02x:%02x%02x:"
+		    "%02x%02x:%02x%02x:"
+		    "%02x%02x:%02x%02x:"
+		    "%02x%02x:%02x%02x",
+		    ipv4masklen, ipv6prefixlen,
+		    ipv6prefix[0], ipv6prefix[1], ipv6prefix[2], ipv6prefix[3],
+		    ipv6prefix[4], ipv6prefix[5], ipv6prefix[6], ipv6prefix[7],
+		    ipv6prefix[8], ipv6prefix[9], ipv6prefix[10],ipv6prefix[11],
+		    ipv6prefix[12],ipv6prefix[13],ipv6prefix[14], ipv6prefix[15]
+		);
+		    
+		len -= b;
+		out += b;
+		bytes += b;
+	} else {
+		bytes += 16 * 2 + 8 + 2 + 1 + 2;
+	}
+
+	while (pl >= 4) {
+		br[0] = *p++;
+		br[1] = *p++;
+		br[2] = *p++;
+		br[3] = *p++;
+		pl -= 4;
+		
+		if (out) {
+			b= snprintf(out, len, " %d.%d.%d.%d",
+			    br[0], br[1], br[2], br[3]);
+			len -= b;
+			out += b;
+			bytes += b;
+		} else {
+			bytes += (4 * 4);
+		}
+	}
+	
+	return bytes;
+}
+
 char *
 get_option_string(const struct dhcp_message *dhcp, uint8_t option)
 {
@@ -1198,6 +1269,9 @@
 	if (type & RFC3442)
 		return decode_rfc3442(s, len, dl, data);
 
+	if (type & RFC5969)
+		return decode_rfc5969(s, len, dl, data);
+
 	if (type & STRING) {
 		/* Some DHCP servers return NULL strings */
 		if (*data == '\0')
diff -ruN dhcpcd-5.2.12/dhcp.h dhcpcd-5.2.12-6rd/dhcp.h
--- dhcpcd-5.2.12/dhcp.h	2011-04-06 10:25:51.000000000 +0200
+++ dhcpcd-5.2.12-6rd/dhcp.h	2011-11-23 11:11:51.000000000 +0100
@@ -109,6 +109,7 @@
 	DHO_FQDN                   = 81,
 	DHO_DNSSEARCH              = 119, /* RFC 3397 */
 	DHO_CSR                    = 121, /* RFC 3442 */
+	DHO_6RD                    = 212, /* RFC 5969 */
 	DHO_MSCSR                  = 249, /* MS code for RFC 3442 */
 	DHO_END                    = 255
 };
# Configure a ipv6 rapid deployment interface

sixrd_tunnel_dev="6rd"

if [ -n "$new_6rd" ] && $if_up; then
	sixrd_existing=false
	sixrd_equal=false
	
	# First check if tunnel already exists
	if ip link show $sixrd_tunnel_dev >/dev/null 2>/dev/null; then
		sixrd_existing=true
	fi
	
	if [ -n "$old_6rd" ] && 
	   [ "$old_6rd" == "$new_6rd" ] &&
	   [ -n "$old_ip_addres" ] $$ 
	   [ "$old_ip_address" == "$new_ip_address" ]; then
		sixrd_equal=true
	fi
	if [ -n "$old_6rd" ] ; then
		echo old6rd
	fi
	if [ "$old_6rd" == "$new_6rd" ]; then
		echo rdsame
	fi
	if [ -n "$old_ip_addres" ]; then
		echo oldip
	fi

	#echo existing: $sixrd_existing >>/tmp/dump
	#echo equal: $sixrd_equal >>/tmp/dump
	#echo $new_6rd
	
	read -r -a sixrd <<< $new_6rd
	sixrd_prefix=${sixrd[2]}/${sixrd[1]}
	sixrd_border_relay=${sixrd[3]}
	
	read -r -a sixrd <<< `sixrdcalc $new_ip_address $new_6rd`
	sixrd_relay_prefix=${sixrd[0]}
	sixrd_delegated_prefix=${sixrd[1]}
	sixrd_host=${sixrd[2]}
	echo sixrd $sixrd
	echo $sixrd_relay_prefix
	echo $sixrd_delegated_prefix
	echo $sixrd_host
	
	# reset an old one, or create a new
	if $sixrd_existing && (! $sixrd_equal); then
		ip link set $sixrd_tunnel_dev down
		ip tunnel del $sixrd_tunnel_dev
		ip tunnel add $sixrd_tunnel_dev mode sit local $new_ip_address
	else
		ip tunnel add $sixrd_tunnel_dev mode sit local $new_ip_address
	fi
	
	if ! $sixrd_existing || ! $sixrd_equal; then
		# Settup tunnel
		ip tunnel 6rd dev $sixrd_tunnel_dev 6rd-prefix $sixrd_prefix 6rd-relay_prefix $sixrd_relay_prefix
		ip addr add $sixrd_host dev $sixrd_tunnel_dev
		ip link set $sixrd_tunnel_dev up
		
		# Add a route to the world
		ip route add 2000::/3 via ::$sixrd_border_relay dev $sixrd_tunnel_dev

		# Restart derived ipv6 interfaces
		/etc/rc.d/rc.inetsix1 restart	
	fi
fi

Follow-Ups:
Re: 6rd option support for dhcpcdRoy Marples
Archive administrator: postmaster@marples.name