diff options
Diffstat (limited to 'tests')
37 files changed, 7209 insertions, 0 deletions
diff --git a/tests/DHCPv6/000-badmsgtype.pl b/tests/DHCPv6/000-badmsgtype.pl new file mode 100644 index 0000000..d0c35ee --- /dev/null +++ b/tests/DHCPv6/000-badmsgtype.pl @@ -0,0 +1,164 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..6bb5d6e --- /dev/null +++ b/tests/DHCPv6/010-solicit-noclientid.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..cee4ddb --- /dev/null +++ b/tests/DHCPv6/011-solicit-serverid.pl @@ -0,0 +1,215 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..c5c4ade --- /dev/null +++ b/tests/DHCPv6/020-advertise-mcast.pl @@ -0,0 +1,164 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..d5af077 --- /dev/null +++ b/tests/DHCPv6/030-request-noclientid.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..1f73038 --- /dev/null +++ b/tests/DHCPv6/031-request-noserverid.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..30e5040 --- /dev/null +++ b/tests/DHCPv6/032-request-badduid.pl @@ -0,0 +1,216 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..5dc6889 --- /dev/null +++ b/tests/DHCPv6/110-information-request-ia_na.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..dc3cf1c --- /dev/null +++ b/tests/DHCPv6/111-information-request-ia_ta.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..3be9261 --- /dev/null +++ b/tests/DHCPv6/112-badduid.pl @@ -0,0 +1,208 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..be9538c --- /dev/null +++ b/tests/DHCPv6/210-solicit-nohost.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..33f6951 --- /dev/null +++ b/tests/DHCPv6/211-solicit-opt-in-na.pl @@ -0,0 +1,218 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..a8d8149 --- /dev/null +++ b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl @@ -0,0 +1,218 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..9758cd8 --- /dev/null +++ b/tests/DHCPv6/280-release-nohost.pl @@ -0,0 +1,175 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..d130812 --- /dev/null +++ b/tests/DHCPv6/281-release-bad-address.pl @@ -0,0 +1,187 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..b3d0af5 --- /dev/null +++ b/tests/DHCPv6/282-release-no-address.pl @@ -0,0 +1,183 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..a1baae4 --- /dev/null +++ b/tests/DHCPv6/283-release.pl @@ -0,0 +1,189 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..02a25e3 --- /dev/null +++ b/tests/DHCPv6/290-decline-nohost.pl @@ -0,0 +1,175 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..d9f0e1e --- /dev/null +++ b/tests/DHCPv6/291-decline-bad-address.pl @@ -0,0 +1,187 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..84c970f --- /dev/null +++ b/tests/DHCPv6/292-decline-no-address.pl @@ -0,0 +1,183 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..960195a --- /dev/null +++ b/tests/DHCPv6/293-decline.pl @@ -0,0 +1,189 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..c559de2 --- /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 0000000..5caa377 --- /dev/null +++ b/tests/DHCPv6/dhcp_client.pm @@ -0,0 +1,454 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..d68ba09 --- /dev/null +++ b/tests/DHCPv6/stubcli-opt-in-na.pl @@ -0,0 +1,217 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..be9538c --- /dev/null +++ b/tests/DHCPv6/stubcli.pl @@ -0,0 +1,212 @@ +#! /usr/bin/perl -w + +# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# <info@isc.org> +# https://www.isc.org/ + +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 0000000..9fe4236 --- /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 0000000..4b67380 --- /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; +} + + diff --git a/tests/HOWTO-unit-test b/tests/HOWTO-unit-test new file mode 100644 index 0000000..e00b7d5 --- /dev/null +++ b/tests/HOWTO-unit-test @@ -0,0 +1,89 @@ +Introduction +------------ + +That is only a brief overview of tests in ISC DHCP. For more thorough +description, see ISC DHCP Developer's Guide. You can generate it, by +having Doxygen installed and doing: + + cd doc + make devel + +and then opening doc/html/index.html + +Tests Overview +-------------- + +In DHCP, a unit test exercises a particular piece of code in +isolation. There is a separate unit test per module or API. Each unit +test lives in a directory beneath the code it is designed to exercise. +So, we (will eventually) have: + + server/tests/ + client/tests/ + common/tests/ + dhcpctl/tests/ + +And so on. + +We are using ATF (Automated Test Framework) as a framework to run our +unit tests. See ISC DHCP Developer's Guide for much more thorough +description of unit-test and ATF framework in general. + +Installing ATF +-------------- +ATF sources can be downloaded from https://github.com/jmmv/kyua. ATF +must be configured, compiled and then installed to be available during +the DHCP configure procedure. Please follow INSTALL file supplied with +ATF sources (it's essentially the typical ./configure && make && +make install procedure). + +Beginning with ATF version 0.16, it is necessary to include the following +options --enable-tools and --disable-shared when configuring ATF: + + configure --prefix=<prefix> --enable-tools --disable-shared + +ISC DHCP unittests will run with ATF releases upto 0.19. Beginning with +ATF 0.20, the tools, atf-run and atf-report required by ISC DHCP, were +deprecated and are no longer included with ATF. + +Running Unit Tests +------------------ + +In order to run the unit tests for DHCP, enable ATF support during configure: + +$ ./configure --with-atf{=<atf-path>} + + where <atf-path> is the path into which ATF was installed. This would + be same value used for --prefix when ATF was configured (default is + /usr/local). + +And then build ISC_DHCP with: + +$ make + +Finally build and run unit tests with: + +$ make check + +This will traverse the source tree running the unit tests in each unit test +subdirectory. Note that if one or more tests in a unit test subdirectory fail +the make process will stop. To run all of the tests regardless of outcome, +use: + +$ make -k check + +You can run a single test by going to the appropriate test directory +and invoking the test directly: + +$ cd server/tests +$ make check + +Adding a New Unit Test +---------------------- + +See ISC DHCP Developer's Guide. + +Adding a New Unit Test Program +------------------------------ + +See ISC DHCP Developer's Guide. diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..9c6c650 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,33 @@ +EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \ + DHCPv6/000-badmsgtype.pl \ + DHCPv6/010-solicit-noclientid.pl \ + DHCPv6/011-solicit-serverid.pl \ + DHCPv6/020-advertise-mcast.pl \ + DHCPv6/030-request-noclientid.pl \ + DHCPv6/031-request-noserverid.pl \ + DHCPv6/032-request-badduid.pl \ + DHCPv6/110-information-request-ia_na.pl \ + DHCPv6/111-information-request-ia_ta.pl \ + DHCPv6/112-badduid.pl \ + DHCPv6/210-solicit-nohost.pl \ + DHCPv6/211-solicit-opt-in-na.pl \ + DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \ + DHCPv6/280-release-nohost.pl \ + DHCPv6/281-release-bad-address.pl \ + DHCPv6/282-release-no-address.pl \ + DHCPv6/283-release.pl \ + DHCPv6/290-decline-nohost.pl \ + DHCPv6/291-decline-bad-address.pl \ + DHCPv6/292-decline-no-address.pl \ + DHCPv6/293-decline.pl \ + DHCPv6/README DHCPv6/dhcp_client.pm \ + DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \ + DHCPv6/test-a.conf DHCPv6/test-b.conf \ + HOWTO-unit-test \ + unit_test_sample.c + +AM_CPPFLAGS = -I.. + +check_LIBRARIES = libt_api.a +libt_api_a_SOURCES = t_api.c t_api_dhcp.c + diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..629339d --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,562 @@ +# Makefile.in generated by automake 1.14 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = tests +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/unittest.sh.in $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/includes/config.h +CONFIG_CLEAN_FILES = unittest.sh +CONFIG_CLEAN_VPATH_FILES = +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libt_api_a_AR = $(AR) $(ARFLAGS) +libt_api_a_LIBADD = +am_libt_api_a_OBJECTS = t_api.$(OBJEXT) t_api_dhcp.$(OBJEXT) +libt_api_a_OBJECTS = $(am_libt_api_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libt_api_a_SOURCES) +DIST_SOURCES = $(libt_api_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +ATF_BIN = @ATF_BIN@ +ATF_CFLAGS = @ATF_CFLAGS@ +ATF_LDFLAGS = @ATF_LDFLAGS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDAP_CFLAGS = @LDAP_CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_prefix_program = @ac_prefix_program@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +byte_order = @byte_order@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \ + DHCPv6/000-badmsgtype.pl \ + DHCPv6/010-solicit-noclientid.pl \ + DHCPv6/011-solicit-serverid.pl \ + DHCPv6/020-advertise-mcast.pl \ + DHCPv6/030-request-noclientid.pl \ + DHCPv6/031-request-noserverid.pl \ + DHCPv6/032-request-badduid.pl \ + DHCPv6/110-information-request-ia_na.pl \ + DHCPv6/111-information-request-ia_ta.pl \ + DHCPv6/112-badduid.pl \ + DHCPv6/210-solicit-nohost.pl \ + DHCPv6/211-solicit-opt-in-na.pl \ + DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \ + DHCPv6/280-release-nohost.pl \ + DHCPv6/281-release-bad-address.pl \ + DHCPv6/282-release-no-address.pl \ + DHCPv6/283-release.pl \ + DHCPv6/290-decline-nohost.pl \ + DHCPv6/291-decline-bad-address.pl \ + DHCPv6/292-decline-no-address.pl \ + DHCPv6/293-decline.pl \ + DHCPv6/README DHCPv6/dhcp_client.pm \ + DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \ + DHCPv6/test-a.conf DHCPv6/test-b.conf \ + HOWTO-unit-test \ + unit_test_sample.c + +AM_CPPFLAGS = -I.. +check_LIBRARIES = libt_api.a +libt_api_a_SOURCES = t_api.c t_api_dhcp.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +unittest.sh: $(top_builddir)/config.status $(srcdir)/unittest.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-checkLIBRARIES: + -test -z "$(check_LIBRARIES)" || rm -f $(check_LIBRARIES) + +libt_api.a: $(libt_api_a_OBJECTS) $(libt_api_a_DEPENDENCIES) $(EXTRA_libt_api_a_DEPENDENCIES) + $(AM_V_at)-rm -f libt_api.a + $(AM_V_AR)$(libt_api_a_AR) libt_api.a $(libt_api_a_OBJECTS) $(libt_api_a_LIBADD) + $(AM_V_at)$(RANLIB) libt_api.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkLIBRARIES clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-checkLIBRARIES clean-generic cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/failover/dhcp-1.cf b/tests/failover/dhcp-1.cf new file mode 100644 index 0000000..07842ab --- /dev/null +++ b/tests/failover/dhcp-1.cf @@ -0,0 +1,154 @@ +authoritative; + +class "even" { + match if ((extract-int (suffix + (pick-first-value (option dhcp-client-identifier, + hardware), 1), 8) % 2) = 0); +} +class "odd" { + match if ((extract-int (suffix + (pick-first-value (option dhcp-client-identifier, + hardware), 1), 8) % 2) = 1); +} + +lease-file-name "dhcp-1.leases"; +pid-file-name "dhcp-1.pid"; +ddns-update-style none; +local-port 50002; +remote-port 50003; +omapi-port 50004; +omapi-key FOO; + +default-lease-time 600; +max-lease-time 600; + +failover peer "foo" { + primary; + address 10.0.0.1; + port 51000; + peer address 10.0.0.1; + peer port 51001; + max-response-delay 60; + max-unacked-updates 10; + mclt 100; + hba ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: + 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00; + load balance max seconds 2; +} + +option space SUNW; +option SUNW.root-mount-options code 1 = text; +option SUNW.root-server-ip-address code 2 = ip-address; +option SUNW.root-server-hostname code 3 = text; +option SUNW.root-path-name code 4 = text; +option SUNW.swap-server-ip-address code 5 = ip-address; +option SUNW.swap-file-path code 6 = text; +option SUNW.boot-file-path code 7 = text; +option SUNW.posix-timezone-string code 8 = text; +option SUNW.boot-read-size code 9 = unsigned integer 16; +option SUNW.install-server-ip-address code 10 = ip-address; +option SUNW.install-server-hostname code 11 = text; +option SUNW.install-path code 12 = text; +option SUNW.sysid-config-file-server code 13 = text; +option SUNW.JumpStart-server code 14 = text; +option SUNW.terminal-name code 15 = text; + +class "solaris-i86pc" { + match if option vendor-class-identifier = "SUNW.i86pc"; + vendor-option-space SUNW; + option SUNW.boot-file-path "/platform/i86pc/kernel/unix"; + option SUNW.root-path-name "/export/root/i86pc"; +} + +class "solaris-sun4u" { + match if option vendor-class-identifier = "SUNW.Ultra-5_10"; + vendor-option-space SUNW; + option SUNW.install-path "/export/2/s581_sparc"; + option SUNW.root-path-name "/export/2/s581_sparc/Solaris_8/Tools/Boot"; +} + +option domain-name "connectathon.org."; +option SUNW.root-server-ip-address 172.16.113.1; +option SUNW.root-server-hostname "sundhcp-server17-1"; + +class "sniffer" { + match if option host-name = "sniffer"; +} + +key FOO { + algorithm HMAC-MD5.SIG-ALG.REG.INT; + secret ABCD; +} + +zone BISBEE.FUGUE.COM. { + primary 127.0.0.1; + key FOO; +} + +zone 17.127.10.in-addr.arpa. { + primary 127.0.0.1; + key FOO; +} + +zone 0.0.10.in-addr.arpa. { + primary 127.0.0.1; + key FOO; +} + +subnet 204.152.186.128 netmask 255.255.255.192 { + not authoritative; +} + +shared-network LOCAL { + subnet 127.0.0.0 netmask 255.255.255.0 { + } + subnet 10.0.2.0 netmask 255.255.255.0 { + pool { + deny dynamic bootp clients; + failover peer "foo"; + range 10.0.2.100 10.0.2.200; + } + } +} + +shared-network NET-187 { + subnet 204.152.187.0 netmask 255.255.255.0 { + } + subnet 205.140.116.224 netmask 255.255.255.248 { + } + subnet 10.0.1.0 netmask 255.255.255.0 { + pool { + deny dynamic bootp clients; + failover peer "foo"; + range 10.0.1.10 10.0.1.200; + } + } +} + +subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + deny dynamic bootp clients; + allow members of "even"; + option impress-servers 10.0.0.0; + failover peer "foo"; + range 10.0.0.10 10.0.0.54; + range 10.0.0.100 10.0.0.149; + } + pool { + deny dynamic bootp clients; + allow members of "odd"; + failover peer "foo"; + option impress-servers 10.0.0.1; + range 10.0.0.55 10.0.0.99; + range 10.0.0.150 10.0.0.200; + } + pool { + deny dynamic bootp clients; + allow members of "sniffer"; + failover peer "foo"; + range 10.0.0.9 10.0.0.9; + } + option routers 10.0.0.1; + option domain-name "bisbee.fugue.com"; + option domain-name-servers 10.0.0.1; +} diff --git a/tests/failover/dhcp-2.cf b/tests/failover/dhcp-2.cf new file mode 100644 index 0000000..c9dfabe --- /dev/null +++ b/tests/failover/dhcp-2.cf @@ -0,0 +1,152 @@ +authoritative; + +class "even" { + match if ((extract-int (suffix + (pick-first-value (option dhcp-client-identifier, + hardware), 1), 8) % 2) = 0); +} +class "odd" { + match if ((extract-int (suffix + (pick-first-value (option dhcp-client-identifier, + hardware), 1), 8) % 2) = 1); +} + +lease-file-name "dhcp-2.leases"; +pid-file-name "dhcp-2.pid"; +local-port 50000; +remote-port 50001; +omapi-port 50005; + +ddns-update-style none; + +default-lease-time 600; +max-lease-time 600; + +failover peer "foo" { + secondary; + address 10.0.0.1; + port 51001; + peer address 10.0.0.1; + peer port 51000; + max-response-delay 60; + max-unacked-updates 10; + mclt 100; + load balance max seconds 2; +} + +option space SUNW; +option SUNW.root-mount-options code 1 = text; +option SUNW.root-server-ip-address code 2 = ip-address; +option SUNW.root-server-hostname code 3 = text; +option SUNW.root-path-name code 4 = text; +option SUNW.swap-server-ip-address code 5 = ip-address; +option SUNW.swap-file-path code 6 = text; +option SUNW.boot-file-path code 7 = text; +option SUNW.posix-timezone-string code 8 = text; +option SUNW.boot-read-size code 9 = unsigned integer 16; +option SUNW.install-server-ip-address code 10 = ip-address; +option SUNW.install-server-hostname code 11 = text; +option SUNW.install-path code 12 = text; +option SUNW.sysid-config-file-server code 13 = text; +option SUNW.JumpStart-server code 14 = text; +option SUNW.terminal-name code 15 = text; + +class "solaris-i86pc" { + match if option vendor-class-identifier = "SUNW.i86pc"; + vendor-option-space SUNW; + option SUNW.boot-file-path "/platform/i86pc/kernel/unix"; + option SUNW.root-path-name "/export/root/i86pc"; +} + +class "solaris-sun4u" { + match if option vendor-class-identifier = "SUNW.Ultra-5_10"; + vendor-option-space SUNW; + option SUNW.install-path "/export/2/s581_sparc"; + option SUNW.root-path-name "/export/2/s581_sparc/Solaris_8/Tools/Boot"; +} + +option domain-name "connectathon.org."; +option SUNW.root-server-ip-address 172.16.113.1; +option SUNW.root-server-hostname "sundhcp-server17-1"; + +class "sniffer" { + match if option host-name = "sniffer"; +} + +key FOO { + algorithm HMAC-MD5.SIG-ALG.REG.INT; + secret ABCD; +} + +zone BISBEE.FUGUE.COM. { + primary 127.0.0.1; + key FOO; +} + +zone 17.127.10.in-addr.arpa. { + primary 127.0.0.1; + key FOO; +} + +zone 0.0.10.in-addr.arpa. { + primary 127.0.0.1; + key FOO; +} + +subnet 204.152.186.128 netmask 255.255.255.192 { + not authoritative; +} + +shared-network LOCAL { + subnet 127.0.0.0 netmask 255.255.255.0 { + } + subnet 10.0.2.0 netmask 255.255.255.0 { + pool { + deny dynamic bootp clients; + failover peer "foo"; + range 10.0.2.100 10.0.2.200; + } + } +} + +shared-network 187-NET { + subnet 204.152.187.0 netmask 255.255.255.0 { + } + subnet 205.140.116.224 netmask 255.255.255.248 { + } + subnet 10.0.1.0 netmask 255.255.255.0 { + pool { + deny dynamic bootp clients; + failover peer "foo"; + range 10.0.1.10 10.0.1.200; + } + } +} + +subnet 10.0.0.0 netmask 255.255.255.0 { + pool { + deny dynamic bootp clients; + allow members of "even"; + option impress-servers 10.0.0.0; + failover peer "foo"; + range 10.0.0.10 10.0.0.54; + range 10.0.0.100 10.0.0.149; + } + pool { + deny dynamic bootp clients; + allow members of "odd"; + failover peer "foo"; + option impress-servers 10.0.0.1; + range 10.0.0.55 10.0.0.99; + range 10.0.0.150 10.0.0.200; + } + pool { + deny dynamic bootp clients; + allow members of "sniffer"; + failover peer "foo"; + range 10.0.0.9 10.0.0.9; + } + option routers 10.0.0.1; + option domain-name "bisbee.fugue.com"; + option domain-name-servers 10.0.0.1; +} diff --git a/tests/failover/new-failover b/tests/failover/new-failover new file mode 100755 index 0000000..65d82e0 --- /dev/null +++ b/tests/failover/new-failover @@ -0,0 +1,28 @@ +#!/bin/sh + +foo=10 +while [ $foo -lt 100 ]; do + cat >>dhcp-1.leases <<~ +lease 10.0.0.$foo { + starts 4 2001/05/01 02:19:16; + ends 5 2021/05/03 02:29:16; + binding state active; + next binding state free; + hardware ethernet 08:00:46:06:6c:23; + uid "test-$foo"; +} +~ + foo=`expr $foo + 1` + cat >>dhcp-2.leases <<~ +lease 10.0.0.$foo { + starts 4 2001/04/19 02:19:16; + ends 5 2021/04/21 02:29:16; + binding state active; + next binding state free; + hardware ethernet 08:00:46:06:6c:23; + uid "test-$foo"; +} +~ + foo=`expr $foo + 1` +done + diff --git a/tests/t_api.c b/tests/t_api.c new file mode 100644 index 0000000..e616226 --- /dev/null +++ b/tests/t_api.c @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: t_api.c,v 1.4 2009/10/28 04:12:30 sar Exp $ */ + +/*! \file */ + +/* + * This test API framework is taken from the BIND 9 code. It has been + * modified to remove the DNS-specific parts, and the BIND-specific + * parts. + * + * The DNS-specific parts are now wrapped with the DNS_SUPPORT macro, + * and the BIND-specific parts are now wrapped with the BIND_SUPPORT + * macro. + */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include <sys/wait.h> + +#include <isc/boolean.h> +#include <isc/commandline.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/mem.h> + +#ifdef DNS_SUPPORT +#include <dns/compress.h> +#include <omapip/result.h> +#endif /* DNS_SUPPORT */ + +#ifndef BIND_SUPPORT +#define isc_commandline_parse getopt +#define isc_commandline_argument optarg +#define isc_commandline_option optopt +#endif /* BIND_SUPPORT */ + +#include "t_api.h" +#include "cdefs.h" + +static const char *Usage = + "\t-a : run all tests\n" + "\t-b <dir> : chdir to dir before running tests" + "\t-c <config_file> : use specified config file\n" + "\t-d <debug_level> : set debug level to debug_level\n" + "\t-h : print test info\n" + "\t-u : print usage info\n" + "\t-n <test_name> : run specified test name\n" + "\t-t <test_number> : run specified test number\n" + "\t-x : don't execute tests in a subproc\n" + "\t-q <timeout> : use 'timeout' as the timeout value\n"; +/*!< + * -a --> run all tests + * -b dir --> chdir to dir before running tests + * -c config --> use config file 'config' + * -d --> turn on api debugging + * -h --> print out available test names + * -u --> print usage info + * -n name --> run test named name + * -tn --> run test n + * -x --> don't execute testcases in a subproc + * -q timeout --> use 'timeout' as the timeout value + */ + +#define T_MAXTESTS 256 /*% must be 0 mod 8 */ +#define T_MAXENV 256 +#define T_DEFAULT_CONFIG "t_config" +#define T_BUFSIZ 256 +#define T_BIGBUF 4096 + +#define T_TCTOUT 60 + +int T_debug; +int T_timeout; +pid_t T_pid; +static const char * T_config; +static char T_tvec[T_MAXTESTS / 8]; +static char * T_env[T_MAXENV + 1]; +static char T_buf[T_BIGBUF]; +static char * T_dir; + +static int +t_initconf(const char *path); + +static int +t_dumpconf(const char *path); + +static int +t_putinfo(const char *key, const char *info); + +static char * +t_getdate(char *buf, size_t buflen); + +static void +printhelp(void); + +static void +printusage(void); + +static int T_int; + +static void +t_sighandler(int sig) { + T_int = sig; +} + +int +main(int argc, char **argv) { + int c; + int tnum; + int subprocs; + pid_t deadpid; + int status; + int len; + isc_boolean_t first; + testspec_t *pts; + struct sigaction sa; + +#ifdef BIND_SUPPORT + isc_mem_debugging = ISC_MEM_DEBUGRECORD; +#endif /* BIND_SUPPORT */ + first = ISC_TRUE; + subprocs = 1; + T_timeout = T_TCTOUT; + + /* + * -a option is now default. + */ + memset(T_tvec, 0xffff, sizeof(T_tvec)); + + /* + * Parse args. + */ + while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:")) + != -1) { + if (c == 'a') { + /* + * Flag all tests to be run. + */ + memset(T_tvec, 0xffff, sizeof(T_tvec)); + } + else if (c == 'b') { + T_dir = isc_commandline_argument; + } + else if (c == 't') { + tnum = atoi(isc_commandline_argument); + if ((tnum > 0) && (tnum < T_MAXTESTS)) { + if (first) { + /* + * Turn off effect of -a default + * and allow multiple -t and -n + * options. + */ + memset(T_tvec, 0, sizeof(T_tvec)); + first = ISC_FALSE; + } + /* + * Flag test tnum to be run. + */ + tnum -= 1; + T_tvec[tnum / 8] |= (0x01 << (tnum % 8)); + } + } + else if (c == 'c') { + T_config = isc_commandline_argument; + } + else if (c == 'd') { + T_debug = atoi(isc_commandline_argument); + } + else if (c == 'n') { + pts = &T_testlist[0]; + tnum = 0; + while (pts->pfv != NULL) { + if (! strcmp(pts->func_name, + isc_commandline_argument)) { + if (first) { + memset(T_tvec, 0, + sizeof(T_tvec)); + first = ISC_FALSE; + } + T_tvec[tnum/8] |= (0x01 << (tnum%8)); + break; + } + ++pts; + ++tnum; + } + if (pts->pfv == NULL) { + fprintf(stderr, "no such test %s\n", + isc_commandline_argument); + exit(1); + } + } + else if (c == 'h') { + printhelp(); + exit(0); + } + else if (c == 'u') { + printusage(); + exit(0); + } + else if (c == 'x') { + subprocs = 0; + } + else if (c == 'q') { + T_timeout = atoi(isc_commandline_argument); + } + else if (c == ':') { + fprintf(stderr, "Option -%c requires an argument\n", + isc_commandline_option); + exit(1); + } + else if (c == '?') { + fprintf(stderr, "Unrecognized option -%c\n", + isc_commandline_option); + exit(1); + } + } + + /* + * Set cwd. + */ + + if (T_dir != NULL) + IGNORE_RET (chdir(T_dir)); + + /* + * We don't want buffered output. + */ + + (void)setbuf(stdout, NULL); + (void)setbuf(stderr, NULL); + + /* + * Setup signals. + */ + + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + +#ifdef SIGCHLD + /* + * This is mostly here for NetBSD's pthread implementation, until + * people catch up to the latest unproven-pthread package. + */ + sa.sa_handler = SIG_DFL; + (void)sigaction(SIGCHLD, &sa, NULL); +#endif + + sa.sa_handler = t_sighandler; + (void)sigaction(SIGINT, &sa, NULL); + (void)sigaction(SIGALRM, &sa, NULL); + + /* + * Output start stanza to journal. + */ + + snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); + len = strlen(T_buf); + (void) t_getdate(T_buf + len, T_BIGBUF - len); + t_putinfo("S", T_buf); + + /* + * Setup the test environment using the config file. + */ + + if (T_config == NULL) + T_config = T_DEFAULT_CONFIG; + + t_initconf(T_config); + if (T_debug) + t_dumpconf(T_config); + + /* + * Now invoke all the test cases. + */ + + tnum = 0; + pts = &T_testlist[0]; + while (*pts->pfv != NULL) { + if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) { + if (subprocs) { + T_pid = fork(); + if (T_pid == 0) { + (*pts->pfv)(); + exit(0); + } else if (T_pid > 0) { + + T_int = 0; + sa.sa_handler = t_sighandler; + (void)sigaction(SIGALRM, &sa, NULL); + alarm(T_timeout); + + deadpid = (pid_t) -1; + while (deadpid != T_pid) { + deadpid = + waitpid(T_pid, &status, 0); + if (deadpid == T_pid) { + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == + SIGTERM) + t_info( + "the test case timed out\n"); + else + t_info( + "the test case caused exception %d\n", + WTERMSIG(status)); + t_result(T_UNRESOLVED); + } + } else if ((deadpid == -1) && + (errno == EINTR) && + T_int) { + kill(T_pid, SIGTERM); + T_int = 0; + } + else if ((deadpid == -1) && + ((errno == ECHILD) || + (errno == ESRCH))) + break; + } + + alarm(0); + sa.sa_handler = SIG_IGN; + (void)sigaction(SIGALRM, &sa, NULL); + } else { + t_info("fork failed, errno == %d\n", + errno); + t_result(T_UNRESOLVED); + } + } + else { + (*pts->pfv)(); + } + } + ++pts; + ++tnum; + } + + snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); + len = strlen(T_buf); + (void) t_getdate(T_buf + len, T_BIGBUF - len); + t_putinfo("E", T_buf); + + return(0); +} + +void +t_assert(const char *component, int anum, int class, const char *what, ...) { + va_list args; + + (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ? + "A" : "C"); + + /* + * Format text to a buffer. + */ + va_start(args, what); + (void)vsnprintf(T_buf, sizeof(T_buf), what, args); + va_end(args); + + (void)t_putinfo("A", T_buf); + (void)printf("\n"); +} + +void +t_info(const char *format, ...) { + va_list args; + + va_start(args, format); + (void) vsnprintf(T_buf, sizeof(T_buf), format, args); + va_end(args); + (void) t_putinfo("I", T_buf); +} + +void +t_result(int result) { + const char *p; + + switch (result) { + case T_PASS: + p = "PASS"; + break; + case T_FAIL: + p = "FAIL"; + break; + case T_UNRESOLVED: + p = "UNRESOLVED"; + break; + case T_UNSUPPORTED: + p = "UNSUPPORTED"; + break; + case T_UNTESTED: + p = "UNTESTED"; + break; + case T_THREADONLY: + p = "THREADONLY"; + break; + default: + p = "UNKNOWN"; + break; + } + printf("R:%s\n", p); +} + +char * +t_getenv(const char *name) { + char *n; + char **p; + size_t len; + + n = NULL; + if (name && *name) { + + p = &T_env[0]; + len = strlen(name); + + while (*p != NULL) { + if (strncmp(*p, name, len) == 0) { + if ( *(*p + len) == '=') { + n = *p + len + 1; + break; + } + } + ++p; + } + } + return(n); +} + +/* + * + * Read in the config file at path, initializing T_env. + * + * note: no format checking for now ... + * + */ + +static int +t_initconf(const char *path) { + + int n; + int rval; + char **p; + FILE *fp; + + rval = -1; + + fp = fopen(path, "r"); + if (fp != NULL) { + n = 0; + p = &T_env[0]; + while (n < T_MAXENV) { + *p = t_fgetbs(fp); + if (*p == NULL) + break; + if ((**p == '#') || (strchr(*p, '=') == NULL)) { + /* + * Skip comments and other junk. + */ + (void)free(*p); + continue; + } + ++p; ++n; + } + (void)fclose(fp); + rval = 0; + } + + return (rval); +} + +/* + * + * Dump T_env to stdout. + * + */ + +static int +t_dumpconf(const char *path) { + int rval; + char **p; + FILE *fp; + + rval = -1; + fp = fopen(path, "r"); + if (fp != NULL) { + p = &T_env[0]; + while (*p != NULL) { + printf("C:%s\n", *p); + ++p; + } + (void) fclose(fp); + rval = 0; + } + return(rval); +} + +/* + * + * Read a newline or EOF terminated string from fp. + * On success: + * return a malloc'd buf containing the string with + * the newline converted to a '\0'. + * On error: + * return NULL. + * + * Caller is responsible for freeing buf. + * + */ + +char * +t_fgetbs(FILE *fp) { + int c; + size_t n; + size_t size; + char *buf, *old; + char *p; + + n = 0; + size = T_BUFSIZ; + old = buf = (char *) malloc(T_BUFSIZ * sizeof(char)); + + if (buf != NULL) { + p = buf; + while ((c = fgetc(fp)) != EOF) { + + if (c == '\n') + break; + + *p++ = c; + ++n; + if ( n >= size ) { + size += T_BUFSIZ; + buf = (char *)realloc(buf, + size * sizeof(char)); + if (buf == NULL) + goto err; + old = buf; + p = buf + n; + } + } + *p = '\0'; + if (c == EOF && n == 0U) { + free(buf); + return (NULL); + } + return (buf); + } else { + err: + if (old != NULL) + free(old); + fprintf(stderr, "malloc/realloc failed %d", errno); + return(NULL); + } +} + +/* + * + * Put info to log, using key. + * For now, just dump it out. + * Later format into pretty lines. + * + */ + +static int +t_putinfo(const char *key, const char *info) { + int rval; + + /* + * For now. + */ + rval = printf("%s:%s", key, info); + return(rval); +} + +static char * +t_getdate(char *buf, size_t buflen) { + size_t n; + time_t t; + struct tm *p; + + t = time(NULL); + p = localtime(&t); + n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p); + return(n != 0U ? buf : NULL); +} + +/* + * Some generally used utilities. + */ +#ifdef DNS_SUPPORT +struct dns_errormap { + isc_result_t result; + const char *text; +} dns_errormap[] = { + { ISC_R_SUCCESS, "ISC_R_SUCCESS" }, + { ISC_R_EXISTS, "ISC_R_EXISTS" }, + { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" }, + { ISC_R_NOSPACE, "ISC_R_NOSPACE" }, + { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" }, + { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" }, + { ISC_R_RANGE, "ISC_R_RANGE" }, + { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" }, + { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" }, + /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */ + /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */ + { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" }, + { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" }, + { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" }, + { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" }, + { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" }, + { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" }, + { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" }, + { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" }, + { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" }, + { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" }, + { DNS_R_SYNTAX, "DNS_R_SYNTAX" }, + { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" }, + { DNS_R_BADAAAA, "DNS_R_BADAAAA" }, + { DNS_R_NOOWNER, "DNS_R_NOOWNER" }, + { DNS_R_NOTTL, "DNS_R_NOTTL" }, + { DNS_R_BADCLASS, "DNS_R_BADCLASS" }, + { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" }, + { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" }, + { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" }, + { DNS_R_BADTTL, "DNS_R_BADTTL" }, + { DNS_R_NOREDATA, "DNS_R_NOREDATA" }, + { DNS_R_CONTINUE, "DNS_R_CONTINUE" }, + { DNS_R_DELEGATION, "DNS_R_DELEGATION" }, + { DNS_R_GLUE, "DNS_R_GLUE" }, + { DNS_R_DNAME, "DNS_R_DNAME" }, + { DNS_R_CNAME, "DNS_R_CNAME" }, + { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" }, + { DNS_R_NXRRSET, "DNS_R_NXRRSET" }, + { DNS_R_BADDB, "DNS_R_BADDB" }, + { DNS_R_ZONECUT, "DNS_R_ZONECUT" }, + { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" }, + { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" }, + { DNS_R_SINGLETON, "DNS_R_SINGLETON" }, + { (isc_result_t)0, NULL } +}; + +isc_result_t +t_dns_result_fromtext(char *name) { + + isc_result_t result; + struct dns_errormap *pmap; + + result = ISC_R_UNEXPECTED; + + pmap = dns_errormap; + while (pmap->text != NULL) { + if (strcmp(name, pmap->text) == 0) + break; + ++pmap; + } + + if (pmap->text != NULL) + result = pmap->result; + + return (result); +} + +struct dc_method_map { + unsigned int dc_method; + const char *text; +} dc_method_map[] = { + + { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" }, + { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" }, + { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" }, + { 0, NULL } +}; + +unsigned int +t_dc_method_fromtext(char *name) { + unsigned int dc_method; + struct dc_method_map *pmap; + + dc_method = DNS_COMPRESS_NONE; + + pmap = dc_method_map; + while (pmap->text != NULL) { + if (strcmp(name, pmap->text) == 0) + break; + ++pmap; + } + + if (pmap->text != NULL) + dc_method = pmap->dc_method; + + return(dc_method); +} +#endif /* DNS_SUPPORT */ + +int +t_bustline(char *line, char **toks) { + int cnt; + char *p; + + cnt = 0; + if (line && *line) { + while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) { + *toks++ = p; + line = NULL; + ++cnt; + } + } + return(cnt); +} + +static void +printhelp(void) { + int cnt; + testspec_t *pts; + + cnt = 1; + pts = &T_testlist[0]; + + printf("Available tests:\n"); + while (pts->func_name) { + printf("\t%d\t%s\n", cnt, pts->func_name); + ++pts; + ++cnt; + } +} + +static void +printusage(void) { + printf("Usage:\n%s\n", Usage); +} + +int +t_eval(const char *filename, int (*func)(char **), int nargs) { + FILE *fp; + char *p; + int line; + int cnt; + int result; + int nfails; + int nprobs; + int npass; + char *tokens[T_MAXTOKS + 1]; + + npass = 0; + nfails = 0; + nprobs = 0; + + fp = fopen(filename, "r"); + if (fp != NULL) { + line = 0; + while ((p = t_fgetbs(fp)) != NULL) { + + ++line; + + /* + * Skip comment lines. + */ + if ((isspace((unsigned char)*p)) || (*p == '#')) { + (void)free(p); + continue; + } + + cnt = t_bustline(p, tokens); + if (cnt == nargs) { + result = func(tokens); + switch (result) { + case T_PASS: + ++npass; + break; + case T_FAIL: + ++nfails; + break; + case T_UNTESTED: + break; + default: + ++nprobs; + break; + } + } else { + t_info("bad format in %s at line %d\n", + filename, line); + ++nprobs; + } + + (void)free(p); + } + (void)fclose(fp); + } else { + t_info("Missing datafile %s\n", filename); + ++nprobs; + } + + result = T_UNRESOLVED; + + if (nfails == 0 && nprobs == 0 && npass > 0) + result = T_PASS; + else if (nfails > 0) + result = T_FAIL; + else if (npass == 0) + result = T_UNTESTED; + + return (result); +} diff --git a/tests/t_api_dhcp.c b/tests/t_api_dhcp.c new file mode 100644 index 0000000..9ef221a --- /dev/null +++ b/tests/t_api_dhcp.c @@ -0,0 +1,44 @@ +/* + * We have to have a number of symbols defined in order to build a + * DHCP program. + */ + +#include <config.h> +#include "dhcpd.h" + +void +bootp(struct packet *packet) { +} + +void +dhcp(struct packet *packet) { +} + +void +dhcpv6(struct packet *packet) { +} + +isc_result_t +dhcp_set_control_state(control_object_state_t old, control_object_state_t new) { + return ISC_R_NOTIMPLEMENTED; +} + +int +check_collection(struct packet *p, struct lease *l, struct collection *c) { + return 0; +} + +void +classify (struct packet *p, struct class *c) { +} + +isc_result_t +find_class(struct class **class, const char *c1, const char *c2, int i) { + return ISC_R_NOTFOUND; +} + +int +parse_allow_deny(struct option_cache **oc, struct parse *p, int i) { + return 0; +} + diff --git a/tests/unit_test_sample.c b/tests/unit_test_sample.c new file mode 100644 index 0000000..86d6336 --- /dev/null +++ b/tests/unit_test_sample.c @@ -0,0 +1,25 @@ +#include "config.h" +#include "t_api.h" + +static void foo(void); + +/* + * T_testlist is a list of tests that are invoked. + */ +testspec_t T_testlist[] = { + { foo, "sample test" }, + { NULL, NULL } +}; + +static void +foo(void) { + static const char *test_desc = + "this is an example test, for no actual module"; + + t_assert("sample", 1, T_REQUIRED, test_desc); + + /* ... */ /* Test code would go here. */ + + t_result(T_PASS); +} + diff --git a/tests/unittest.sh.in b/tests/unittest.sh.in new file mode 100755 index 0000000..0ee7ab7 --- /dev/null +++ b/tests/unittest.sh.in @@ -0,0 +1,79 @@ +#!/bin/sh +# +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +# Script used to execute unit tests described by the Atffile in the current +# directory. It exits with return value of atf-run, which will be 0 if all +# tests passed, non-zero otherwise. +# + +# Add configured path to ATF tools, atf-run and atf-report +PATH="@ATF_BIN@:${PATH}" +export PATH + +# colors if not outputting to a dumb terminal and stdout is a tty +if test "$TERM" != dumb && { test -t 1; } 2>/dev/null; then \ + red='\033[0;31m' + green='\033[0;32m' + noclr='\033[0m' + + # if echo supports -e, we must use it to set colors + # (output will be "" if its supported) + if [ -z "`echo -e`" ] + then + dash_e="-e" + fi +fi; + +header="====================================================" + +status=0 +if [ -n "@ATF_BIN@" -a -f Atffile ] +then + # run the tests + echo "Running unit tests..." + atf-run > atf.out + status=$? + + # set color based on success/failure + if [ $status -eq 0 ] + then + color=$green + else + color=$red + fi + + # spit out the test report + # We print everything upto the summary in + # "no color". Print the summary in our + # result color. + cat atf.out | atf-report | while read line + do + cnt=`echo $line | grep -c "Summary"` + if [ $cnt -eq 1 ] + then + echo $dash_e $color$header + fi + echo $line; + done + echo $dash_e $header$noclr + + # clean up unless there were test failures + if [ $status -eq 0 ] + then + rm -f atf.out + fi +fi +exit $status |