diff options
Diffstat (limited to 'tests')
27 files changed, 4736 insertions, 0 deletions
diff --git a/tests/DHCPv6/000-badmsgtype.pl b/tests/DHCPv6/000-badmsgtype.pl new file mode 100644 index 00000000..c6dc202b --- /dev/null +++ b/tests/DHCPv6/000-badmsgtype.pl @@ -0,0 +1,144 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new(255); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + +# timeout parameters +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our bogus packet + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/010-solicit-noclientid.pl b/tests/DHCPv6/010-solicit-noclientid.pl new file mode 100644 index 00000000..89f4bc47 --- /dev/null +++ b/tests/DHCPv6/010-solicit-noclientid.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# do NOT add the Client Identifier (required by DOCSIS and RFC 3315) +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/011-solicit-serverid.pl b/tests/DHCPv6/011-solicit-serverid.pl new file mode 100644 index 00000000..f77fd42e --- /dev/null +++ b/tests/DHCPv6/011-solicit-serverid.pl @@ -0,0 +1,195 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server Identifier (NOT ALLOWED by DOCSIS and RFC 3315) +$msg->add_option($OPT_SERVERID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/020-advertise-mcast.pl b/tests/DHCPv6/020-advertise-mcast.pl new file mode 100644 index 00000000..b1ee8e6f --- /dev/null +++ b/tests/DHCPv6/020-advertise-mcast.pl @@ -0,0 +1,144 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_ADVERTISE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + +# timeout parameters +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our bogus packet + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/030-request-noclientid.pl b/tests/DHCPv6/030-request-noclientid.pl new file mode 100644 index 00000000..e7460426 --- /dev/null +++ b/tests/DHCPv6/030-request-noclientid.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_REQUEST); + +# NOT add the Client Identifier (required by DOCSIS and RFC 3315) +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/031-request-noserverid.pl b/tests/DHCPv6/031-request-noserverid.pl new file mode 100644 index 00000000..0a2a94b2 --- /dev/null +++ b/tests/DHCPv6/031-request-noserverid.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_REQUEST); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/032-request-badduid.pl b/tests/DHCPv6/032-request-badduid.pl new file mode 100644 index 00000000..bb61952e --- /dev/null +++ b/tests/DHCPv6/032-request-badduid.pl @@ -0,0 +1,196 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_REQUEST); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server Identifier (required by DOCSIS and RFC 3315) +# but use *our* DUID +$msg->add_option($OPT_SERVERID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/110-information-request-ia_na.pl b/tests/DHCPv6/110-information-request-ia_na.pl new file mode 100644 index 00000000..03a44ecd --- /dev/null +++ b/tests/DHCPv6/110-information-request-ia_na.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/111-information-request-ia_ta.pl b/tests/DHCPv6/111-information-request-ia_ta.pl new file mode 100644 index 00000000..aa9eba28 --- /dev/null +++ b/tests/DHCPv6/111-information-request-ia_ta.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_TA for each interface (should be IA_NA, by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_TA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/112-badduid.pl b/tests/DHCPv6/112-badduid.pl new file mode 100644 index 00000000..41ccdeb3 --- /dev/null +++ b/tests/DHCPv6/112-badduid.pl @@ -0,0 +1,188 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server Identifier (required by DOCSIS and RFC 3315) +# but use *our* DUID +$msg->add_option($OPT_SERVERID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/210-solicit-nohost.pl b/tests/DHCPv6/210-solicit-nohost.pl new file mode 100644 index 00000000..5ca80250 --- /dev/null +++ b/tests/DHCPv6/210-solicit-nohost.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/211-solicit-opt-in-na.pl b/tests/DHCPv6/211-solicit-opt-in-na.pl new file mode 100644 index 00000000..2dfbbcc9 --- /dev/null +++ b/tests/DHCPv6/211-solicit-opt-in-na.pl @@ -0,0 +1,198 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; +$Data::Dumper::Useqq = 1; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; +#my $client_id = dhcp_client::duid(3); +$msg->add_option($OPT_CLIENTID, $client_id); +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + my $enc_opt = dhcp_client::msg->new(0); + $enc_opt->add_option($OPT_CLIENTID, $client_id); + $option_data .= $enc_opt->packed_options(); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; +#print Dumper($reply_msg), "\n"; + exit(0); +} + +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl new file mode 100644 index 00000000..f7517eb5 --- /dev/null +++ b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl @@ -0,0 +1,198 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; +$Data::Dumper::Useqq = 1; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; +#my $client_id = dhcp_client::duid(3); +$msg->add_option($OPT_CLIENTID, $client_id); +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + my $enc_opt = dhcp_client::msg->new(0); + $enc_opt->add_option($OPT_CLIENTID, $client_id); + $option_data .= $enc_opt->packed_options(); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +#$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; +#print Dumper($reply_msg), "\n"; + exit(0); +} + +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/280-release-nohost.pl b/tests/DHCPv6/280-release-nohost.pl new file mode 100644 index 00000000..2b84481d --- /dev/null +++ b/tests/DHCPv6/280-release-nohost.pl @@ -0,0 +1,155 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new message +my $msg = dhcp_client::msg->new($MSG_RELEASE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our message + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/281-release-bad-address.pl b/tests/DHCPv6/281-release-bad-address.pl new file mode 100644 index 00000000..635e701e --- /dev/null +++ b/tests/DHCPv6/281-release-bad-address.pl @@ -0,0 +1,167 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_RELEASE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::"); + $iaaddr_option .= pack("NN", 0, 0); + my $option_data = pack("NNN", ++$iaid, 0, 0); + $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); + $option_data .= $iaaddr_option; + $msg->add_option($OPT_IA_NA, $option_data); +} + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/282-release-no-address.pl b/tests/DHCPv6/282-release-no-address.pl new file mode 100644 index 00000000..26f06b7d --- /dev/null +++ b/tests/DHCPv6/282-release-no-address.pl @@ -0,0 +1,163 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_RELEASE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/283-release.pl b/tests/DHCPv6/283-release.pl new file mode 100644 index 00000000..8d82a30c --- /dev/null +++ b/tests/DHCPv6/283-release.pl @@ -0,0 +1,169 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_RELEASE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; +$msg->add_option($OPT_CLIENTID, $client_id); +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff"); + $iaaddr_option .= pack("NN", 0, 0); + my $option_data = pack("NNN", ++$iaid, 0, 0); + $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); + $option_data .= $iaaddr_option; + $msg->add_option($OPT_IA_NA, $option_data); +} + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/290-decline-nohost.pl b/tests/DHCPv6/290-decline-nohost.pl new file mode 100644 index 00000000..4d8e8893 --- /dev/null +++ b/tests/DHCPv6/290-decline-nohost.pl @@ -0,0 +1,155 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_DECLINE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/291-decline-bad-address.pl b/tests/DHCPv6/291-decline-bad-address.pl new file mode 100644 index 00000000..1ebd2992 --- /dev/null +++ b/tests/DHCPv6/291-decline-bad-address.pl @@ -0,0 +1,167 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_DECLINE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::"); + $iaaddr_option .= pack("NN", 0, 0); + my $option_data = pack("NNN", ++$iaid, 0, 0); + $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); + $option_data .= $iaaddr_option; + $msg->add_option($OPT_IA_NA, $option_data); +} + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/292-decline-no-address.pl b/tests/DHCPv6/292-decline-no-address.pl new file mode 100644 index 00000000..f9ed7177 --- /dev/null +++ b/tests/DHCPv6/292-decline-no-address.pl @@ -0,0 +1,163 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_DECLINE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/293-decline.pl b/tests/DHCPv6/293-decline.pl new file mode 100644 index 00000000..bb7d06e4 --- /dev/null +++ b/tests/DHCPv6/293-decline.pl @@ -0,0 +1,169 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_DECLINE); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; +$msg->add_option($OPT_CLIENTID, $client_id); +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); + +# add the Server identifier (required by RFC 3315) +$msg->add_option($OPT_SERVERID, "InfiniteEntropy"); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff"); + $iaaddr_option .= pack("NN", 0, 0); + my $option_data = pack("NNN", ++$iaid, 0, 0); + $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); + $option_data .= $iaaddr_option; + $msg->add_option($OPT_IA_NA, $option_data); +} + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/README b/tests/DHCPv6/README new file mode 100644 index 00000000..c559de24 --- /dev/null +++ b/tests/DHCPv6/README @@ -0,0 +1,62 @@ +In order to test the DHCPv6 server, we have a configuration file with +known values, and some Perl scripts designed to send and receive +DHCPv6 packets to check various code paths. + +It is not complete test converage by any means, but it should be +fairly easy to add additional tests as needed. + +The scripts themselves are not very well written. There is a lot of +copied code, poor error handling, and so on. These should be rewritten +at some point. + +To use, the DHCPv6 server must be running in test mode to send back to +the originating port. (The scripts can be changed to bind to the +appropriate client port, but they don't now, and have to run as root +to do this). In server/dhcpv6.c, look for this comment: + +/* For testing, we reply to the sending port, so we don't need a root */ +/* client */ + to_addr.sin6_port = remote_port; +/* to_addr.sin6_port = packet->client_port;*/ + +And change the code to use the client_port value. + +You will need to modify one of the test configuration files to use one +of the physical subnets that your machine uses, in the subnet6 +statement. + +Then run the server as root, in debug mode: + +# touch /tmp/test.leases +# dhcpd -cf test-a.conf -lf /tmp/test.leases -d + +You can invoke the scripts then: + +$ perl 000-badmsgtype.pl + +The expected results vary per script, depending on the behavior that +is being tested. + + +Notes about scripts: + +In order to manipulate IPv6 addresses, we need the Socket6 library, +available from CPAN: + +http://search.cpan.org/~umemoto/Socket6-0.19/Socket6.pm + +The Perl that Sun issues for Solaris 10 is compiled with the Sun +compiler. If you have the Sun compiler, then this will work fine. +Otherwise you may need to install Perl from source. + +We need to get the hardware address in order to build DUID properly. +The IO::Interface module reports hardware address, but not on Solaris +10 it seems. Rather than do this the "right way", we do it the "Perl +way", and hack it. "ifconfig" does return the Ethernet address, but +only to the root user. However, we can look for files of the name +/etc/hostname.*, get the IP address from "ifconfig", and then check +for those addresses in the ARP table. + +Client DUID is supposed to be an opaque value to the server, but we go +ahead and make a "real" type 1 or type 3 DUID. + diff --git a/tests/DHCPv6/dhcp_client.pm b/tests/DHCPv6/dhcp_client.pm new file mode 100644 index 00000000..85ff0cc3 --- /dev/null +++ b/tests/DHCPv6/dhcp_client.pm @@ -0,0 +1,435 @@ +#! /usr/bin/perl -w + + +package dhcp_client; + +require Exporter; + +@ISA = qw(Exporter); + +# message types +$MSG_SOLICIT = 1; +$MSG_ADVERTISE = 2; +$MSG_REQUEST = 3; +$MSG_CONFIRM = 4; +$MSG_RENEW = 5; +$MSG_REBIND = 6; +$MSG_REPLY = 7; +$MSG_RELEASE = 8; +$MSG_DECLINE = 9; +$MSG_RECONFIGURE = 10; +$MSG_INFORMATION_REQUEST = 11; +$MSG_RELAY_FORW = 12; +$MSG_RELAY_REPL = 13; + +# option numbers +$OPT_CLIENTID = 1; +$OPT_SERVERID = 2; +$OPT_IA_NA = 3; +$OPT_IA_TA = 4; +$OPT_IAADDR = 5; +$OPT_ORO = 6; +$OPT_PREFERENCE = 7; +$OPT_ELAPSED_TIME = 8; +$OPT_RELAY_MSG = 9; +$OPT_AUTH = 11; +$OPT_UNICAST = 12; +$OPT_STATUS_CODE = 13; +$OPT_RAPID_COMMIT = 14; +$OPT_USER_CLASS = 15; +$OPT_VENDOR_CLASS = 16; +$OPT_VENDOR_OPTS = 17; +$OPT_INTERFACE_ID = 18; +$OPT_RECONF_MSG = 19; +$OPT_RECONF_ACCEPT = 20; + +# timeouts +$SOL_MAX_DELAY = 1; +$SOL_TIMEOUT = 1; +$SOL_MAX_RT = 120; +$REQ_TIMEOUT = 1; +$REQ_MAX_RT = 30; +$REQ_MAX_RC = 10; +$CNF_MAX_DELAY = 1; +$CNF_MAX_RT = 4; +$CNF_MAX_RD = 10; +$REN_TIMEOUT = 10; +$REN_MAX_RT = 600; +$REB_TIMEOUT = 10; +$REB_MAX_RT = 600; +$INF_MAX_DELAY = 1; +$INF_TIMEOUT = 1; +$INF_MAX_RT = 120; +$REL_TIMEOUT = 1; +$REL_MAX_RC = 5; +$DEC_TIMEOUT = 1; +$DEC_MAX_RC = 5; +$REC_TIMEOUT = 2; +$REC_MAX_RC = 8; +$HOP_COUNT_LIMIT = 32; + +@EXPORT = qw( $MSG_SOLICIT $MSG_ADVERTISE $MSG_REQUEST $MSG_CONFIRM + $MSG_RENEW $MSG_REBIND $MSG_REPLY $MSG_RELEASE $MSG_DECLINE + $MSG_RECONFIGURE $MSG_INFORMATION_REQUEST $MSG_RELAY_FORW + $MSG_RELAY_REPL + $OPT_CLIENTID $OPT_SERVERID $OPT_IA_NA $OPT_IA_TA $OPT_IAADDR + $OPT_ORO $OPT_PREFERENCE $OPT_ELAPSED_TIME $OPT_RELAY_MSG + $OPT_AUTH $OPT_UNICAST $OPT_STATUS_CODE $OPT_RAPID_COMMIT + $OPT_USER_CLASS $OPT_VENDOR_CLASS $OPT_VENDOR_OPTS + $OPT_INTERFACE_ID $OPT_RECONF_MSG $OPT_RECONF_ACCEPT + $SOL_MAX_DELAY $SOL_TIMEOUT $SOL_MAX_RT $REQ_TIMEOUT + $REQ_MAX_RT $REQ_MAX_RC $CNF_MAX_DELAY $CNF_MAX_RT + $CNF_MAX_RD $REN_TIMEOUT $REN_MAX_RT $REB_TIMEOUT $REB_MAX_RT + $INF_MAX_DELAY $INF_TIMEOUT $INF_MAX_RT $REL_TIMEOUT + $REL_MAX_RC $DEC_TIMEOUT $DEC_MAX_RC $REC_TIMEOUT $REC_MAX_RC + $HOP_COUNT_LIMIT ); + +my %msg_type_num = ( + MSG_SOLICIT => 1, + MSG_ADVERTISE => 2, + MSG_REQUEST => 3, + MSG_CONFIRM => 4, + MSG_RENEW => 5, + MSG_REBIND => 6, + MSG_REPLY => 7, + MSG_RELEASE => 8, + MSG_DECLINE => 9, + MSG_RECONFIGURE => 10, + MSG_INFORMATION_REQUEST => 11, + MSG_RELAY_FORW => 12, + MSG_RELAY_REPL => 13, +); +my %msg_num_type = reverse(%msg_type_num); + +my %opt_type_num = ( + OPT_CLIENTID => 1, + OPT_SERVERID => 2, + OPT_IA_NA => 3, + OPT_IA_TA => 4, + OPT_IAADDR => 5, + OPT_ORO => 6, + OPT_PREFERENCE => 7, + OPT_ELAPSED_TIME => 8, + OPT_RELAY_MSG => 9, + OPT_AUTH => 11, + OPT_UNICAST => 12, + OPT_STATUS_CODE => 13, + OPT_RAPID_COMMIT => 14, + OPT_USER_CLASS => 15, + OPT_VENDOR_CLASS => 16, + OPT_VENDOR_OPTS => 17, + OPT_INTERFACE_ID => 18, + OPT_RECONF_MSG => 19, + OPT_RECONF_ACCEPT => 20, +); +my %opt_num_type = reverse(%opt_type_num); + +my %status_code_num = ( + Success => 0, + UnspecFail => 1, + NoAddrsAvail => 2, + NoBinding => 3, + NotOnLink => 4, + UseMulticast => 5, +); +my %status_num_code = reverse(%status_code_num); + +my %docsis_type_num = ( + CL_OPTION_ORO => 1, + CL_OPTION_TFTP_SERVERS => 32, + CL_OPTION_CONFIG_FILE_NAME => 33, + CL_OPTION_SYSLOG_SERVERS => 34, + CL_OPTION_TLV5 => 35, + CL_OPTION_DEVICE_ID => 36, + CL_OPTION_CCC => 37, + CL_OPTION_DOCSIS_VERS => 38, +); +my %docsis_num_type = reverse(%docsis_type_num); + +use strict; +use English; +use POSIX; + +# XXX: very Solaris-specific +sub iface { + my @ifaces; + foreach my $fname (glob("/etc/hostname.*")) { + $fname =~ s[^/etc/hostname.][]; + push(@ifaces, $fname); + } + return wantarray() ? @ifaces : $ifaces[0]; +} + +# XXX: very Solaris-specific +sub mac_addr { + my @ip_addrs; + foreach my $iface (iface()) { + if (`ifconfig $iface 2>/dev/null` =~ /\sinet (\S+)\s/) { + push(@ip_addrs, $1); + } + } + my @mac_addrs; + foreach my $line (split(/\n/, `arp -an 2>/dev/null`)) { + my @parts = split(/\s+/, $line); + my $ip = $parts[1]; + my $mac = $parts[-1]; + if (grep { $ip eq $_ } @ip_addrs) { + $mac =~ s/://g; + push(@mac_addrs, $mac); + } + } + return wantarray() ? @mac_addrs : $mac_addrs[0]; +} + +sub mac_addr_binary { + my @mac_addr = split(//, mac_addr()); + my $mac_addr = join("", map { chr(hex($_)) } @mac_addr); + return $mac_addr; +} + +# DHCPv6 times start 2000-01-01 00:00:00 +my $dhcp_time_base = 946684800; +#{ +# local $ENV{TZ} = "UTC"; +# POSIX::tzset(); +# $dhcp_time_base = POSIX::mktime(0, 0, 0, 1, 0, 100); +#} + +sub dhcpv6_time { + return time() - $dhcp_time_base; +} + +sub duid { + my ($type) = @_; + + $type = 1 unless (defined $type); + + if (($type == 1) || ($type == 3)) { + my $mac_addr = mac_addr_binary(); + if ($type == 1) { + my $time = pack("N", dhcpv6_time()); + return "\x00\x01\x00\x01${time}${mac_addr}"; + } else { + return "\x00\x03\x00\x01${mac_addr}"; + } + } else { + die "Unknown DUID type $type requested"; + } +} + +package dhcp_client::msg; + +use Socket; +use Socket6; + +sub new { + my ($pkg, $msg_type, $trans_id) = @_; + + my $this = {}; + bless $this; + + $this->{msg_type} = $msg_type+0; + if (defined $trans_id) { + $this->{trans_id} = $trans_id; + } else { + $this->{trans_id} = chr(rand(256)) . + chr(rand(256)) . chr(rand(256)); + } + $this->{options} = [ ]; + + return $this; +} + + +sub add_option { + my ($this, $num, $data) = @_; + + push(@{$this->{options}}, [ $num, $data ]); +} + +sub get_option { + my ($this, $num) = @_; + my @options; + foreach my $option (@{$this->{options}}) { + if ($option->[0] == $num) { + push(@options, $option->[1]); + } + } + return wantarray() ? @options : $options[0]; +} + +sub packed_options { + my ($this) = @_; + + my $options = ""; + foreach my $option (@{$this->{options}}) { + $options .= pack("nn", $option->[0], length($option->[1])); + $options .= $option->[1]; + } + return $options; +} + +sub packet { + my ($this) = @_; + + my $packet = ""; + $packet .= chr($this->{msg_type}); + $packet .= $this->{trans_id}; + $packet .= $this->packed_options(); + return $packet; +} + +sub unpack_options { + my ($options) = @_; + + my @parsed_options; + my $p = 0; + while ($p < length($options)) { + my ($id, $len) = unpack("nn", substr($options, $p, 4)); + push(@parsed_options, [ $id, substr($options, $p + 4, $len) ]); + $p += 4 + $len; + } + return @parsed_options; +} + +sub print_docsis_option { + my ($num, $data, $indent) = @_; + + print "${indent}DOCSIS Option $num"; + if ($docsis_num_type{$num}) { + print " ($docsis_num_type{$num})"; + } + print ", length ", length($data), "\n"; + + return unless ($docsis_num_type{$num}); + + if ($docsis_num_type{$num} eq "CL_OPTION_ORO") { + my $num_oro = length($data) / 2; + for (my $i=0; $i<$num_oro; $i++) { + my $oro_num = unpack("n", substr($data, $i*2, 2)); + print "${indent} $oro_num"; + if ($docsis_num_type{$oro_num}) { + print " ($docsis_num_type{$oro_num})"; + } + print "\n"; + } + } elsif ($docsis_num_type{$num} eq "CL_OPTION_TFTP_SERVERS") { + my $num_servers = length($data) / 16; + for (my $i=0; $i<$num_servers; $i++) { + my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16)); + print "$indent TFTP server ", ($i+1), ": "; + print uc($srv), "\n"; + } + } elsif ($docsis_num_type{$num} eq "CL_OPTION_CONFIG_FILE_NAME") { + print "$indent Config file name: \"$data\"\n" + } elsif ($docsis_num_type{$num} eq "CL_OPTION_SYSLOG_SERVERS") { + my $num_servers = length($data) / 16; + for (my $i=0; $i<$num_servers; $i++) { + my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16)); + print "$indent syslog server ", ($i+1), ": "; + print uc($srv), "\n"; + } + } +} + +sub print_option { + my ($num, $data, $indent) = @_; + + print "${indent}Option $num"; + if ($opt_num_type{$num}) { + print " ($opt_num_type{$num})"; + } + print ", length ", length($data), "\n"; + if ($num == $dhcp_client::OPT_ORO) { + my $num_oro = length($data) / 2; + for (my $i=0; $i<$num_oro; $i++) { + my $oro_num = unpack("n", substr($data, $i*2, 2)); + print "${indent} $oro_num"; + if ($opt_num_type{$oro_num}) { + print " ($opt_num_type{$oro_num})"; + } + print "\n"; + } + } elsif (($num == $dhcp_client::OPT_CLIENTID) || + ($num == $dhcp_client::OPT_SERVERID)) { + print $indent, " "; + if (length($data) > 0) { + printf '%02X', ord(substr($data, 0, 1)); + for (my $i=1; $i<length($data); $i++) { + printf ':%02X', ord(substr($data, $i, 1)); + } + } + print "\n"; + } elsif ($num == $dhcp_client::OPT_IA_NA) { + printf "${indent} IAID: 0x\%08X\n", + unpack("N", substr($data, 0, 4)); + printf "${indent} T1: \%d\n", unpack("N", substr($data, 4, 4)); + printf "${indent} T2: \%d\n", unpack("N", substr($data, 8, 4)); + if (length($data) > 12) { + printf "${indent} IA_NA encapsulated options:\n"; + foreach my $option (unpack_options(substr($data, 12))) { + print_option(@{$option}, $indent . " "); + } + } + } elsif ($num == $dhcp_client::OPT_IAADDR) { + printf "${indent} IPv6 address: \%s\n", + uc(inet_ntop(AF_INET6, substr($data, 0, 16))); + printf "${indent} Preferred lifetime: \%d\n", + unpack("N", substr($data, 16, 4)); + printf "${indent} Valid lifetime: \%d\n", + unpack("N", substr($data, 20, 4)); + if (length($data) > 24) { + printf "${indent} IAADDR encapsulated options:\n"; + foreach my $option (unpack_options(substr($data, 24))) { + print_option(@{$option}, $indent . " "); + } + } + } elsif ($num == $dhcp_client::OPT_VENDOR_OPTS) { + my $enterprise_number = unpack("N", substr($data, 0, 4)); + print "${indent} Enterprise number: $enterprise_number\n"; + + # DOCSIS + if ($enterprise_number == 4491) { + foreach my $option (unpack_options(substr($data, 4))) { + print_docsis_option(@{$option}, $indent . " "); + } + } + } elsif ($num == $dhcp_client::OPT_STATUS_CODE) { + my $code = ord(substr($data, 0, 1)); + my $msg = substr($data, 1); + print "${indent} Code: $code"; + if ($status_num_code{$code}) { + print " ($status_num_code{$code})"; + } + print "\n"; + print "${indent} Message: \"$msg\"\n"; + } +} + +# XXX: we aren't careful about packet boundaries and values... +# DO NOT RUN ON PRODUCTION SYSTEMS!!! +sub decode { + my ($packet, $print) = @_; + + my $msg_type = ord(substr($packet, 0, 1)); + my $trans_id = substr($packet, 1, 3); + my $msg = dhcp_client::msg->new($msg_type, $trans_id); + + if ($print) { + print "DHCPv6 packet\n"; + print " Message type: $msg_num_type{$msg_type}\n"; + printf " Transaction id: 0x\%02X\%02X\%02X\n", + ord(substr($trans_id, 0, 1)), + ord(substr($trans_id, 1, 1)), + ord(substr($trans_id, 2, 1)); + print " Options:\n"; + } + + foreach my $option (unpack_options(substr($packet, 4))) { + print_option(@{$option}, " ") if ($print); + $msg->add_option(@{$option}); + } + + return $msg; +} + diff --git a/tests/DHCPv6/stubcli-opt-in-na.pl b/tests/DHCPv6/stubcli-opt-in-na.pl new file mode 100644 index 00000000..f400031b --- /dev/null +++ b/tests/DHCPv6/stubcli-opt-in-na.pl @@ -0,0 +1,197 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; +$Data::Dumper::Useqq = 1; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; +$msg->add_option($OPT_CLIENTID, $client_id); +#$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + my $enc_opt = dhcp_client::msg->new(0); + $enc_opt->add_option($OPT_CLIENTID, $client_id); + $option_data .= $enc_opt->packed_options(); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; +#print Dumper($reply_msg), "\n"; + exit(0); +} + +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/stubcli.pl b/tests/DHCPv6/stubcli.pl new file mode 100644 index 00000000..5ca80250 --- /dev/null +++ b/tests/DHCPv6/stubcli.pl @@ -0,0 +1,192 @@ +#! /usr/bin/perl -w + +use strict; +use English; +use Time::HiRes qw( sleep ); +use Socket; +use Socket6; +use IO::Select; + +use dhcp_client; + +# XXX: for debugging +use Data::Dumper; + +# not-yet-standard options +my $OPT_TIME_SERVERS = 40; +my $OPT_TIME_OFFSET = 41; + +# DOCSIS sub-options +my $DOCSIS_OPT_ORO = 1; +# 2 to 31 are reserved +my $DOCSIS_OPT_TFTP_SERVERS = 32; +my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; +my $DOCSIS_OPT_SYSLOG_SERVERS = 34; +my $DOCSIS_OPT_TLV5 = 35; +my $DOCSIS_OPT_DEVICE_ID = 36; +my $DOCSIS_OPT_CCC = 37; +my $DOCSIS_OPT_VERS = 38; + +# well-known addresses +my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; +my $All_DHCP_Servers = "ff05::1:3"; + +# ports +my $client_port = 546; +my $server_port = 547; + +# create a new Solicit message +my $msg = dhcp_client::msg->new($MSG_SOLICIT); + +# add the Client Identifier (required by DOCSIS and RFC 3315) +$msg->add_option($OPT_CLIENTID, dhcp_client::duid()); + +# add Elapsed Time, set to 0 on first packet (required by RFC 3315) +$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); + +# add IA_NA for each interface (required by DOCSIS and RFC 3315) +# XXX: should this be a single interface only? +my $iaid = 0; +foreach my $iface (dhcp_client::iface()) { + my $option_data = pack("NNN", ++$iaid, 0, 0); + $msg->add_option($OPT_IA_NA, $option_data); +} + +# add Reconfigure Accept (required by DOCSIS) +$msg->add_option($OPT_RECONF_ACCEPT, ""); + +# add Options Request (required by DOCSIS, recommended by RFC 3315) +my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); +$msg->add_option($OPT_ORO, pack("n*", @oro)); + + +# add Vendor Class option (required by DOCSIS) +$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); + +# add Vendor-specific Information Option option (required by DOCSIS) +my $vsio = pack("N", 4491); + +# ORO (required by DOCSIS) +my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); +$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); + +# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) +my $tlv5_data = "\x01\x02\x03\x0"; +$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; + +# DOCSIS Device (required by DOCSIS) +my $docsis_device_id = dhcp_client::mac_addr_binary(); +$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); +$vsio .= $docsis_device_id; + +$msg->add_option($OPT_VENDOR_OPTS, $vsio); + +# add Rapid Commit option (required by DOCSIS) +$msg->add_option($OPT_RAPID_COMMIT, ""); + +# timeout parameters, from DOCSIS +my $IRT = $SOL_TIMEOUT; +my $MRT = $SOL_MAX_RT; +my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 +my $MRD = 0; + +# sleep a random amount of time between 0 and 1 second, required by RFC 3315 +# XXX: this seems pretty stupid +sleep(rand($SOL_MAX_DELAY)); + +my $RT; +my $count = 0; +my $mrd_end_time; +if ($MRD != 0) { + $mrd_end_time = time() + $MRD; +} +my $reply_msg; +do { + # create our socket, and send our Solicit + socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; + my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); + my $packet = $msg->packet(); + my $send_ret = send(SOCK, $packet, 0, + pack_sockaddr_in6($server_port, $addr)); + if (not defined($send_ret)) { + printf STDERR + "Error \%d sending DHCPv6 Solicit message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } elsif ($send_ret != length($packet)) { + print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; + exit(1); + } + $count++; + + my $RAND = rand(0.2) - 0.1; + if (defined $RT) { + $RT = 2*$RT + $RAND*$RT; + if (($RT > $MRT) && ($MRT != 0)) { + $RT = $MRT + $RAND*$RT; + } + } else { + $RT = $IRT + $RAND*$IRT; + } + + my $rt_end_time = time() + $RT; + if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { + $rt_end_time = $mrd_end_time; + } + + for (;;) { + my $timeout = $rt_end_time - time(); + if ($timeout < 0) { +# print STDERR "Timeout waiting for DHCPv6 Advertise ", +# "or Reply message.\n"; + last; + } + + my @ready = IO::Select->new(\*SOCK)->can_read($timeout); + + if (@ready) { + my $reply; + my $recv_ret; + + $recv_ret = recv(SOCK, $reply, 1500, 0); + if (not defined $recv_ret) { + printf STDERR + "Error \%d receiving DHCPv6 " . + "message;\n\%s\n", + 0+$ERRNO, $ERRNO; + exit(1); + } + + $reply_msg = dhcp_client::msg::decode($reply, 1); + if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || + ($reply_msg->{msg_type} == $MSG_REPLY)) { + last; + } + } + } + +} until ($reply_msg || + (($MRC != 0) && ($count > $MRC)) || + (defined($mrd_end_time) && ($mrd_end_time > time()))); + +unless ($reply_msg) { + if (($MRC != 0) && ($count >= $MRC)) { + print STDERR + "No reply after maximum retransmission count.\n"; + } else { + print STDERR + "No reply after maximum retransmission duration.\n"; + } +} + +if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { + print "Got DHCPv6 Reply message.\n"; + exit(0); +} + +#$Data::Dumper::Useqq = 1; +#print Dumper($msg), "\n"; +#print Dumper($msg->packet()), "\n"; +# +#print "packet length: ", length($msg->packet()), "\n"; + diff --git a/tests/DHCPv6/test-a.conf b/tests/DHCPv6/test-a.conf new file mode 100644 index 00000000..9fe42364 --- /dev/null +++ b/tests/DHCPv6/test-a.conf @@ -0,0 +1,67 @@ +# +# Define the DHCPv6 option space. +# +# Option numbers are assigned by IANA: +# http://www.iana.org/assignments/dhcpv6-parameters +# +option dhcp6.time-servers code 40 = array of ip6-address; +option dhcp6.time-offset code 41 = signed integer 32; + +# +# Define the DOCSIS option space. +# TODO: DOCSIS oro definition +# +option space docsis code width 2 length width 2; +option vsio.docsis code 4491 = encapsulate docsis; +option docsis.tftp-servers code 32 = array of ip6-address; +option docsis.cablelabs-configuration-file code 33 = text; +option docsis.cablelabs-syslog-servers code 34 = array of ip6-address; +option docsis.device-id code 36 = string; + +# +# Declare some options. +# +option dhcp6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2; +option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2; + +# +# DNS server IP address to update dynamically +# +ddns-update-style interim; +ddns-domainname "foo.com"; + +# +# Per-host settings. +# +host cablemodem-1 { + host-identifier option + dhcp6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6; + fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff; + ddns-domainname "bar.com"; + option dhcp6.time-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; + option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; + option dhcp6.time-offset -14400; # -4 hours + option docsis.cablelabs-configuration-file "bootfile.cfg"; + option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; +} + +#host cablemodem-2 { +# host-identifier option docsis.device-id 00:06:5B:50:99:F6; +# option dhcp6.time-servers 3ffe:dddd:aaaa:aaaa::1, +# 3ffe:dddd:aaaa:aaaa::2; +# option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1, +# 3ffe:dddd:aaaa:aaaa::2; +# option dhcp6.time-offset -14400; # -4 hours +# option docsis.cablelabs-configuration-file "bootfile.cfg"; +# option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, +# 3ffe:aaaa:aaaa:aaaa::2; +#} + +# XXX: for testing +subnet6 fe80::20c:29ff:fe42:820/128 { +} + + diff --git a/tests/DHCPv6/test-b.conf b/tests/DHCPv6/test-b.conf new file mode 100644 index 00000000..4b673806 --- /dev/null +++ b/tests/DHCPv6/test-b.conf @@ -0,0 +1,60 @@ +# +# Define the DHCPv6 option space. +# +# Option numbers are assigned by IANA: +# http://www.iana.org/assignments/dhcpv6-parameters +# +option dhcpv6.time-servers code 40 = array of ip6-address; +option dhcpv6.time-offset code 41 = signed integer 32; + +# +# Define the DOCSIS option space. +# +option space docsis code width 2 length width 2; +option docsis.tftp-servers code 32 = array of ip6-address; +option docsis.cablelabs-configuration-file code 33 = text; +option docsis.cablelabs-syslog-servers code 34 = array of ip6-address; +option docsis.device-id code 36 = string; + +# +# Declare some options. +# +option dhcpv6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2; +option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2; + +# +# DNS server IP address to update dynamically +# +ddns-update-style interim; +ddns-domainname "foo.com"; + +# +# Per-host settings. +# +host cablemodem-1 { + option dhcpv6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6; + fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff; + ddns-domainname "bar.com"; + option dhcpv6.time-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; + option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; + option dhcpv6.time-offset -14400; # -4 hours + option docsis.cablelabs-configuration-file "bootfile.cfg"; + option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; +} + +host cablemodem-2 { + option docsis.device-id 00:06:5B:50:99:F6; + option dhcpv6.time-servers 3ffe:dddd:aaaa:aaaa::1, + 3ffe:dddd:aaaa:aaaa::2; + option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1, + 3ffe:dddd:aaaa:aaaa::2; + option dhcpv6.time-offset -14400; # -4 hours + option docsis.cablelabs-configuration-file "bootfile.cfg"; + option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1, + 3ffe:aaaa:aaaa:aaaa::2; +} + + |