]>
Commit | Line | Data |
---|---|---|
98bd7ca0 DH |
1 | #! /usr/bin/perl -w |
2 | ||
a47d6336 | 3 | # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC") |
706792c9 DH |
4 | # |
5 | # Permission to use, copy, modify, and distribute this software for any | |
6 | # purpose with or without fee is hereby granted, provided that the above | |
7 | # copyright notice and this permission notice appear in all copies. | |
8 | # | |
9 | # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES | |
10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | # | |
17 | # Internet Systems Consortium, Inc. | |
429a56d7 TM |
18 | # PO Box 360 |
19 | # Newmarket, NH 03857 USA | |
706792c9 | 20 | # <info@isc.org> |
2c85ac9b | 21 | # https://www.isc.org/ |
706792c9 | 22 | |
98bd7ca0 DH |
23 | use strict; |
24 | use English; | |
25 | use Time::HiRes qw( sleep ); | |
26 | use Socket; | |
27 | use Socket6; | |
28 | use IO::Select; | |
29 | ||
30 | use dhcp_client; | |
31 | ||
32 | # XXX: for debugging | |
33 | use Data::Dumper; | |
34 | ||
35 | # not-yet-standard options | |
36 | my $OPT_TIME_SERVERS = 40; | |
37 | my $OPT_TIME_OFFSET = 41; | |
38 | ||
39 | # DOCSIS sub-options | |
40 | my $DOCSIS_OPT_ORO = 1; | |
41 | # 2 to 31 are reserved | |
42 | my $DOCSIS_OPT_TFTP_SERVERS = 32; | |
43 | my $DOCSIS_OPT_CONFIG_FILE_NAME = 33; | |
44 | my $DOCSIS_OPT_SYSLOG_SERVERS = 34; | |
45 | my $DOCSIS_OPT_TLV5 = 35; | |
46 | my $DOCSIS_OPT_DEVICE_ID = 36; | |
47 | my $DOCSIS_OPT_CCC = 37; | |
48 | my $DOCSIS_OPT_VERS = 38; | |
49 | ||
50 | # well-known addresses | |
51 | my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2"; | |
52 | my $All_DHCP_Servers = "ff05::1:3"; | |
53 | ||
54 | # ports | |
55 | my $client_port = 546; | |
56 | my $server_port = 547; | |
57 | ||
58 | # create a new Solicit message | |
59 | my $msg = dhcp_client::msg->new($MSG_DECLINE); | |
60 | ||
61 | # add the Client Identifier (required by DOCSIS and RFC 3315) | |
62 | my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6"; | |
63 | $msg->add_option($OPT_CLIENTID, $client_id); | |
64 | #$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3)); | |
65 | ||
66 | # add the Server identifier (required by RFC 3315) | |
67 | $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); | |
68 | ||
69 | # add Elapsed Time, set to 0 on first packet (required by RFC 3315) | |
70 | $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); | |
71 | ||
72 | # add IA_NA for each interface (required by DOCSIS and RFC 3315) | |
73 | # XXX: should this be a single interface only? | |
74 | my $iaid = 0; | |
75 | foreach my $iface (dhcp_client::iface()) { | |
76 | my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff"); | |
77 | $iaaddr_option .= pack("NN", 0, 0); | |
78 | my $option_data = pack("NNN", ++$iaid, 0, 0); | |
79 | $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); | |
80 | $option_data .= $iaaddr_option; | |
81 | $msg->add_option($OPT_IA_NA, $option_data); | |
82 | } | |
83 | ||
84 | # timeout parameters, from DOCSIS | |
85 | my $IRT = $SOL_TIMEOUT; | |
86 | my $MRT = $SOL_MAX_RT; | |
87 | my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 | |
88 | my $MRD = 0; | |
89 | ||
90 | # sleep a random amount of time between 0 and 1 second, required by RFC 3315 | |
91 | # XXX: this seems pretty stupid | |
92 | sleep(rand($SOL_MAX_DELAY)); | |
93 | ||
94 | my $RT; | |
95 | my $count = 0; | |
96 | my $mrd_end_time; | |
97 | if ($MRD != 0) { | |
98 | $mrd_end_time = time() + $MRD; | |
99 | } | |
100 | my $reply_msg; | |
101 | do { | |
102 | # create our socket, and send our Solicit | |
103 | socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; | |
104 | my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); | |
105 | my $packet = $msg->packet(); | |
106 | my $send_ret = send(SOCK, $packet, 0, | |
107 | pack_sockaddr_in6($server_port, $addr)); | |
108 | if (not defined($send_ret)) { | |
109 | printf STDERR | |
110 | "Error \%d sending DHCPv6 Solicit message;\n\%s\n", | |
111 | 0+$ERRNO, $ERRNO; | |
112 | exit(1); | |
113 | } elsif ($send_ret != length($packet)) { | |
114 | print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; | |
115 | exit(1); | |
116 | } | |
117 | $count++; | |
118 | ||
119 | my $RAND = rand(0.2) - 0.1; | |
120 | if (defined $RT) { | |
121 | $RT = 2*$RT + $RAND*$RT; | |
122 | if (($RT > $MRT) && ($MRT != 0)) { | |
123 | $RT = $MRT + $RAND*$RT; | |
124 | } | |
125 | } else { | |
126 | $RT = $IRT + $RAND*$IRT; | |
127 | } | |
128 | ||
129 | my $rt_end_time = time() + $RT; | |
130 | if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { | |
131 | $rt_end_time = $mrd_end_time; | |
132 | } | |
133 | ||
134 | for (;;) { | |
135 | my $timeout = $rt_end_time - time(); | |
136 | if ($timeout < 0) { | |
137 | # print STDERR "Timeout waiting for DHCPv6 Advertise ", | |
138 | # "or Reply message.\n"; | |
139 | last; | |
140 | } | |
141 | ||
142 | my @ready = IO::Select->new(\*SOCK)->can_read($timeout); | |
143 | ||
144 | if (@ready) { | |
145 | my $reply; | |
146 | my $recv_ret; | |
147 | ||
148 | $recv_ret = recv(SOCK, $reply, 1500, 0); | |
149 | if (not defined $recv_ret) { | |
150 | printf STDERR | |
151 | "Error \%d receiving DHCPv6 " . | |
152 | "message;\n\%s\n", | |
153 | 0+$ERRNO, $ERRNO; | |
154 | exit(1); | |
155 | } | |
156 | ||
157 | $reply_msg = dhcp_client::msg::decode($reply, 1); | |
158 | if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || | |
159 | ($reply_msg->{msg_type} == $MSG_REPLY)) { | |
160 | last; | |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | } until ($reply_msg || | |
166 | (($MRC != 0) && ($count > $MRC)) || | |
167 | (defined($mrd_end_time) && ($mrd_end_time > time()))); | |
168 | ||
169 | unless ($reply_msg) { | |
170 | if (($MRC != 0) && ($count >= $MRC)) { | |
171 | print STDERR | |
172 | "No reply after maximum retransmission count.\n"; | |
173 | } else { | |
174 | print STDERR | |
175 | "No reply after maximum retransmission duration.\n"; | |
176 | } | |
177 | } | |
178 | ||
179 | if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { | |
180 | print "Got DHCPv6 Reply message.\n"; | |
181 | exit(0); | |
182 | } | |
183 | ||
184 | #$Data::Dumper::Useqq = 1; | |
185 | #print Dumper($msg), "\n"; | |
186 | #print Dumper($msg->packet()), "\n"; | |
187 | # | |
188 | #print "packet length: ", length($msg->packet()), "\n"; | |
189 |