DHCPv6 and DUID Confusion
In IPv6, DHCP is taking somewhat a back seat to router advertisements. Many smaller networks are unlikely to use DHCP. However, in particular for Enterprise/larger networks, DHCPv6 still offers a lot of advantages when it comes to managing hosts and accounting for IP addresses in use.
One of the big differences when it comes to DHCPv6 is that a host identifies itself with a DUID (DHCP Unique Identifier) which can be different from a MAC address. There are essentially three ways to come up with a DUID:
Link Layer + Time: In this case, the host will on first boot create a DUID using one interfaces link layer address (MAC address for Ethernet), as well as the timestamp (seconds since Epoch) to derive a DUID. This DUID will be saved to disk and remain constant even if the network card is swapped later.
Link Layer: Some hosts may not be able to retain a DUID between reboots in this case, the link layer address is used.
Vendor Assigned: You can also just assign an arbitrary DUID, maybe a host name, to identify the host.
Regardless which method you use, the sad part is that each operating system, and in some cases different software on the same operating system, chooses to display the DUID differently, making correlation hard.
Here are a few examples:
Linux seems to like a mix of octal and ASCII characters (if the value represents a printable character). For example:
\000\001\000\001\032\336\306\373\000\014)g\317\002
However, in Linux configuration files for DHCPv6 servers and clients, you may find a simpler hex format:
option dhcp6.client-id 0:1:0:1:1a:de:c6:fb:0:c:29:67:cf:2;
OS X on the other hand displays the time part in decimal, and the MAC address part in hexadecimal:
ipconfig getv6packet en0
CLIENTID (1) Length 14 DUID LLT HW 1 Time 389824106 Addr 40:6c:8f:11:d7:5c
Windows prefers to display the hexadecimal version as output for "ipconfig /all"
DHCPv6 Client DUID. . . : 00-01-00-01-13-0D-1E-A2-00-0C-29-A3-D3-30
To help myself a bit with this confusion, I started a little script that will convert DUIDs from different formats. It isn't quite done yet, but good enough to see if anybody finds it helpful and would like to test it. You can download the script from https://isc.sans.edu/diaryimages/duidconvert.pl
,
[To learn more about IPv6 Security, check out my class IPv6 Security Essentials]
------
Johannes B. Ullrich, Ph.D.
SANS Technology Institute
Twitter
Application Security: Securing Web Apps, APIs, and Microservices | Online | US Eastern | Jan 27th - Feb 1st 2025 |
Comments
I'd like to submit an improved version of duidconvert.pl.
Chris
#!/usr/bin/perl
use strict;
my $in = shift;
my $input;
#see iana.org for authoritative list.
my%Types=("00:01"=>"Link Layer+Time (DUID-LLT)",
"00:02"=>"Vendor Assigned (DUID-EN)",
"00:03"=>"Link Layer (DUID-LL)");
my%HWTypes=("00:00"=>"Reserved",
"00:01"=>"Ethernet",
"00:02"=>"Exp. Ethernet",
"00:03"=>"Amateur Radio",
"00:04"=>"Token Ring",
"00:05"=>"Chaos",
"00:06"=>"IEEE 802",
"00:07"=>"ARCNET",
"00:08"=>"Hyperchannel",
"00:09"=>"Lanstart",
"00:0a"=>"Autonet",
"00:0b"=>"Localtalk",
"00:0c"=>"LocalNet",
"00:0d"=>"Ultra link",
"00:0e"=>"SMDS",
"00:0f"=>"Frame Relay",
"00:10"=>"ATM",
"00:11"=>"HDLC",
"00:12"=>"Fiber Channel",
"00:13"=>"ATM",
"00:14"=>"Serial",
"00:15"=>"ATM",
"00:16"=>"MIL-STD-88-220",
"00:17"=>"Metricom",
"00:18"=>"IEEE 1394",
"00:19"=>"MAPOS",
"00:1a"=>"Twinaxial",
"00:1b"=>"EUI-64",
"00:1c"=>"HIPARP",
"00:1d"=>"ISO 7816-3",
"00:1e"=>"ARPSec",
"00:1f"=>"IPSec",
"00:20"=>"Infiniband",
"00:21"=>"TIA-102",
"00:22"=>"Wiegand",
"00:23"=>"Pure IP");
# windows uses - instead of :
$in =~ s/\-/:/;
if ( $in =~ /^:?([a-f0-9]{1,2}:)+([a-f0-9]{1,2})?$/i ) {
$input = $in;
print "Linux String: " . hextostring($in) . "\n";
}
elsif( $in =~ /^\\/ ) {
$input = stringtohex($in);
print "Hex: " . $input . "\n";
}
else {
print "The input string was not recognized.\n";
exit 1;
}
print hextorfc($input);
exit 0;
sub hextostring {
my $hex = shift;
$hex = lc($hex);
my $out = '';
my $num;
if ( $hex =~ /^:?([a-f0-9]{1,2}:)+([a-f0-9]{1,2})?$/ ) {
foreach ( split /:/,$hex ) {
$num = hex($_);
if ( $num < 127 and $num > 31 ) {
$out.=chr($num);
} else {
$out.=sprintf"\\%03o",$num;
}
}
return $out;
} else {
return 0;
}
}
sub stringtohex {
# sample: "\002\317g)\000\001\000\001\032\336\306\373\000\014)g\317\002"
my $string = shift;
my $out = '';
while ( $string ne '' ) {
if ( $string =~ s/^\\([0-9]{3})// ) {
$out.=sprintf('%02x',oct($1)).":";
} else {
$string =~ s/.// ;
$out.=sprintf('%02x',ord($&)).":";
}
}
$out =~ s/:$//;
$out = lc($out);
return $out;
}
sub hextorfc {
my $hex = shift;
my $type = 'unknown';
$hex =~ s/([0-9]{1,2}:){2}//;
$type = $&;
$type =~ s/:$//;
my $typestr = "DUID Type: " . $type . " -> " . $Types{$type};
if ( $type =~ m/^00:01$/ ) {
$hex =~ s/([0-9a-f]{1,2}:){2}//;
my $hwhex = $&;
$hwhex =~ s/:$//;
$hex =~ s/([0-9a-f]{1,2}:){4}//;
my $timehex = $&;
$timehex =~ s/://g;
my $time = hex($timehex);
return $typestr . "; Hardware: " . $hwhex . " -> " . $HWTypes{$hwhex} .
"; Time: " . $timehex . " (hex) -> " . $time . " (seconds since epoch) -> " .
localtime($time) . "; Link Layer: " . $hex . "\n";
}
if ( $type =~ m/^00:02$/ ) {
$hex =~ s/([0-9a-f]{2}:){4}//;
my $enthex = $&;
$enthex =~ s/:$//;
return $typestr . "; Enterprise ID: " . $enthex . "; Identifier: " . $hex . "\n";
}
if ( $type =~ m/^00:03$/ ) {
$hex =~ s/([0-9a-f]{1,2}:){2}//;
my $hwhex = $&;
$hwhex =~ s/:$//;
return $typestr . "; Hardware: " . $hwhex . " -> " .
$HWTypes{$hwhex} . "; Link Layer: ". $hex . "\n";
}
return "Unknown Format $type - $hex \n";
}
Anonymous
Jan 16th 2017
7 years ago