From 7e35aa56c73128ce1abfe3acdcf7876a8106dc75 Mon Sep 17 00:00:00 2001 From: Tore Anderson Date: Sat, 22 Mar 2014 01:34:55 +0100 Subject: [PATCH] Improve CLAT IPv6 address auto-generation logic In the case of there being more than one EUI-64 based IPv6 address on the PLAT device, clatd will now pick the one which share the longest common prefix length with the PLAT prefix when deciding which one to base the auto-generated CLAT IPv6 address on. This should avoid accidentally ending up with a ULA-based CLAT IPv6 address when better alternatives exist. Resolves #1. --- clatd | 51 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/clatd b/clatd index 399ad22..3b6eeed 100755 --- a/clatd +++ b/clatd @@ -12,7 +12,7 @@ use strict; use Net::IP; -my $VERSION = "1.0"; +my $VERSION = "1.1"; # # Populate the global config hash with the default values @@ -436,6 +436,17 @@ sub get_clat_v6_addr { if(!$plat_dev) { err("get_clat_v6_addr(): No PLAT device to work with"); } + + # In case there are more than one EUI-64-based addresses on the plat device, + # we'll need the plat prefix as an bigint in order to find which of those + # addresses share the longest common prefix. We'll prefer to use that one. + my $plat_prefix_int = Net::IP->new(cfg("plat-prefix"), 6)->intip(); + if(!$plat_prefix_int) { + err("Failed to convert plat prefix to bigint"); + } + my $ip; # will contain the best candidate ip in bigint format + my $best_score; + p("Attempting to derive a CLAT IPv6 address from a EUI-64 address on ", "'$plat_dev'"); open(my $fd, '-|', cfg("cmd-ip"), qw(-6 address list scope global dev), @@ -446,25 +457,33 @@ sub get_clat_v6_addr { my $candidate = $1; next unless(is_modified_eui64($candidate)); d2("Saw EUI-64 based address: $candidate"); - my $ip = Net::IP->new($candidate, 6) or next; - $ip = $ip->intip(); - - # First clear the middle 0xfffe bits of the interface ID - my $mask = Net::IP->new("ffff:ffff:ffff:ffff:ffff:ff00:00ff:ffff"); - $mask = $mask->intip(); - $ip &= $mask; - - # Next set them to the value 0xc1a7 and return - $mask = Net::IP->new("::c1:a700:0", 6) or next; - $mask = $mask->intip(); - $ip |= $mask; - - $ip = Net::IP->new(Net::IP::ip_bintoip(Net::IP::ip_inttobin($ip, 6), 6)); - return $ip->short() if $ip; + my $candidate_int = Net::IP->new($candidate, 6)->intip(); + if(!$candidate_int) { + err("Failed to convert plat prefix to bigint"); + } + if(!$best_score or $best_score > ($plat_prefix_int ^ $candidate_int)) { + d2("$candidate has so far the longest common prefix with plat prefix"); + $best_score = $plat_prefix_int ^ $candidate_int; + $ip = $candidate_int; + } } } close($fd) or err("'ip -6 address list scope global dev $plat_dev' failed"); + + # First clear the middle 0xfffe bits of the interface ID + my $mask = Net::IP->new("ffff:ffff:ffff:ffff:ffff:ff00:00ff:ffff"); + $mask = $mask->intip(); + $ip &= $mask; + + # Next set them to the value 0xc1a7 and return + $mask = Net::IP->new("::c1:a700:0", 6) or err(Net::IP::Error()); + $mask = $mask->intip(); + $ip |= $mask; + + $ip = Net::IP->new(Net::IP::ip_bintoip(Net::IP::ip_inttobin($ip, 6), 6)); + return $ip->short() if $ip; + err("Failed to generate a CLAT IPv6 address (try setting 'clat-v6-addr')"); }