]>
Commit | Line | Data |
---|---|---|
98bd7ca0 DH |
1 | #! /usr/bin/perl -w |
2 | ||
49a7fb58 | 3 | # Copyright (C) 2007-2022 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_RELEASE); | |
60 | ||
61 | # add the Client Identifier (required by DOCSIS and RFC 3315) | |
62 | $msg->add_option($OPT_CLIENTID, dhcp_client::duid()); | |
63 | ||
64 | # add the Server identifier (required by RFC 3315) | |
65 | $msg->add_option($OPT_SERVERID, "InfiniteEntropy"); | |
66 | ||
67 | # add Elapsed Time, set to 0 on first packet (required by RFC 3315) | |
68 | $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); | |
69 | ||
70 | # add IA_NA for each interface (required by DOCSIS and RFC 3315) | |
71 | # XXX: should this be a single interface only? | |
72 | my $iaid = 0; | |
73 | foreach my $iface (dhcp_client::iface()) { | |
74 | my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::"); | |
75 | $iaaddr_option .= pack("NN", 0, 0); | |
76 | my $option_data = pack("NNN", ++$iaid, 0, 0); | |
77 | $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8); | |
78 | $option_data .= $iaaddr_option; | |
79 | $msg->add_option($OPT_IA_NA, $option_data); | |
80 | } | |
81 | ||
82 | # timeout parameters, from DOCSIS | |
83 | my $IRT = $SOL_TIMEOUT; | |
84 | my $MRT = $SOL_MAX_RT; | |
85 | my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 | |
86 | my $MRD = 0; | |
87 | ||
88 | # sleep a random amount of time between 0 and 1 second, required by RFC 3315 | |
89 | # XXX: this seems pretty stupid | |
90 | sleep(rand($SOL_MAX_DELAY)); | |
91 | ||
92 | my $RT; | |
93 | my $count = 0; | |
94 | my $mrd_end_time; | |
95 | if ($MRD != 0) { | |
96 | $mrd_end_time = time() + $MRD; | |
97 | } | |
98 | my $reply_msg; | |
99 | do { | |
100 | # create our socket, and send our Solicit | |
101 | socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; | |
102 | my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); | |
103 | my $packet = $msg->packet(); | |
f6b8f48d | 104 | my $send_ret = send(SOCK, $packet, 0, |
98bd7ca0 DH |
105 | pack_sockaddr_in6($server_port, $addr)); |
106 | if (not defined($send_ret)) { | |
f6b8f48d | 107 | printf STDERR |
98bd7ca0 DH |
108 | "Error \%d sending DHCPv6 Solicit message;\n\%s\n", |
109 | 0+$ERRNO, $ERRNO; | |
110 | exit(1); | |
111 | } elsif ($send_ret != length($packet)) { | |
112 | print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; | |
113 | exit(1); | |
114 | } | |
115 | $count++; | |
116 | ||
117 | my $RAND = rand(0.2) - 0.1; | |
118 | if (defined $RT) { | |
119 | $RT = 2*$RT + $RAND*$RT; | |
120 | if (($RT > $MRT) && ($MRT != 0)) { | |
121 | $RT = $MRT + $RAND*$RT; | |
122 | } | |
123 | } else { | |
124 | $RT = $IRT + $RAND*$IRT; | |
125 | } | |
126 | ||
127 | my $rt_end_time = time() + $RT; | |
f6b8f48d | 128 | if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { |
98bd7ca0 DH |
129 | $rt_end_time = $mrd_end_time; |
130 | } | |
131 | ||
132 | for (;;) { | |
133 | my $timeout = $rt_end_time - time(); | |
134 | if ($timeout < 0) { | |
135 | # print STDERR "Timeout waiting for DHCPv6 Advertise ", | |
136 | # "or Reply message.\n"; | |
137 | last; | |
138 | } | |
139 | ||
140 | my @ready = IO::Select->new(\*SOCK)->can_read($timeout); | |
141 | ||
142 | if (@ready) { | |
143 | my $reply; | |
144 | my $recv_ret; | |
f6b8f48d | 145 | |
98bd7ca0 DH |
146 | $recv_ret = recv(SOCK, $reply, 1500, 0); |
147 | if (not defined $recv_ret) { | |
f6b8f48d TM |
148 | printf STDERR |
149 | "Error \%d receiving DHCPv6 " . | |
98bd7ca0 DH |
150 | "message;\n\%s\n", |
151 | 0+$ERRNO, $ERRNO; | |
152 | exit(1); | |
153 | } | |
154 | ||
155 | $reply_msg = dhcp_client::msg::decode($reply, 1); | |
156 | if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || | |
157 | ($reply_msg->{msg_type} == $MSG_REPLY)) { | |
158 | last; | |
159 | } | |
160 | } | |
161 | } | |
162 | ||
f6b8f48d | 163 | } until ($reply_msg || |
98bd7ca0 DH |
164 | (($MRC != 0) && ($count > $MRC)) || |
165 | (defined($mrd_end_time) && ($mrd_end_time > time()))); | |
166 | ||
167 | unless ($reply_msg) { | |
168 | if (($MRC != 0) && ($count >= $MRC)) { | |
f6b8f48d | 169 | print STDERR |
98bd7ca0 DH |
170 | "No reply after maximum retransmission count.\n"; |
171 | } else { | |
f6b8f48d | 172 | print STDERR |
98bd7ca0 DH |
173 | "No reply after maximum retransmission duration.\n"; |
174 | } | |
175 | } | |
176 | ||
177 | if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { | |
178 | print "Got DHCPv6 Reply message.\n"; | |
179 | exit(0); | |
180 | } | |
181 | ||
182 | #$Data::Dumper::Useqq = 1; | |
183 | #print Dumper($msg), "\n"; | |
184 | #print Dumper($msg->packet()), "\n"; | |
185 | # | |
186 | #print "packet length: ", length($msg->packet()), "\n"; |