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
Archive administrator: postmaster@marples.name