]>
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_INFORMATION_REQUEST); | |
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 DOCSIS and RFC 3315) | |
65 | # but use *our* DUID | |
66 | $msg->add_option($OPT_SERVERID, dhcp_client::duid()); | |
67 | ||
68 | # add Elapsed Time, set to 0 on first packet (required by RFC 3315) | |
69 | $msg->add_option($OPT_ELAPSED_TIME, "\x00\x00"); | |
70 | ||
71 | # add Reconfigure Accept (required by DOCSIS) | |
72 | $msg->add_option($OPT_RECONF_ACCEPT, ""); | |
73 | ||
74 | # add Options Request (required by DOCSIS, recommended by RFC 3315) | |
75 | my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET ); | |
76 | $msg->add_option($OPT_ORO, pack("n*", @oro)); | |
77 | ||
78 | ||
79 | # add Vendor Class option (required by DOCSIS) | |
80 | $msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0"); | |
81 | ||
82 | # add Vendor-specific Information Option option (required by DOCSIS) | |
83 | my $vsio = pack("N", 4491); | |
84 | ||
85 | # ORO (required by DOCSIS) | |
86 | my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS ); | |
87 | $vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro); | |
88 | ||
89 | # TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS) | |
f6b8f48d | 90 | my $tlv5_data = "\x01\x02\x03\x0"; |
98bd7ca0 DH |
91 | $vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data; |
92 | ||
93 | # DOCSIS Device (required by DOCSIS) | |
94 | my $docsis_device_id = dhcp_client::mac_addr_binary(); | |
95 | $vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id)); | |
96 | $vsio .= $docsis_device_id; | |
97 | ||
98 | $msg->add_option($OPT_VENDOR_OPTS, $vsio); | |
99 | ||
100 | # add Rapid Commit option (required by DOCSIS) | |
101 | $msg->add_option($OPT_RAPID_COMMIT, ""); | |
102 | ||
103 | # timeout parameters, from DOCSIS | |
104 | my $IRT = $SOL_TIMEOUT; | |
105 | my $MRT = $SOL_MAX_RT; | |
106 | my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0 | |
107 | my $MRD = 0; | |
108 | ||
109 | # sleep a random amount of time between 0 and 1 second, required by RFC 3315 | |
110 | # XXX: this seems pretty stupid | |
111 | sleep(rand($SOL_MAX_DELAY)); | |
112 | ||
113 | my $RT; | |
114 | my $count = 0; | |
115 | my $mrd_end_time; | |
116 | if ($MRD != 0) { | |
117 | $mrd_end_time = time() + $MRD; | |
118 | } | |
119 | my $reply_msg; | |
120 | do { | |
121 | # create our socket, and send our Solicit | |
122 | socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die; | |
123 | my $addr = inet_pton(AF_INET6, $All_DHCP_Servers); | |
124 | my $packet = $msg->packet(); | |
f6b8f48d | 125 | my $send_ret = send(SOCK, $packet, 0, |
98bd7ca0 DH |
126 | pack_sockaddr_in6($server_port, $addr)); |
127 | if (not defined($send_ret)) { | |
f6b8f48d | 128 | printf STDERR |
98bd7ca0 DH |
129 | "Error \%d sending DHCPv6 Solicit message;\n\%s\n", |
130 | 0+$ERRNO, $ERRNO; | |
131 | exit(1); | |
132 | } elsif ($send_ret != length($packet)) { | |
133 | print STDERR "Unable to send entire DHCPv6 Solicit message.\n"; | |
134 | exit(1); | |
135 | } | |
136 | $count++; | |
137 | ||
138 | my $RAND = rand(0.2) - 0.1; | |
139 | if (defined $RT) { | |
140 | $RT = 2*$RT + $RAND*$RT; | |
141 | if (($RT > $MRT) && ($MRT != 0)) { | |
142 | $RT = $MRT + $RAND*$RT; | |
143 | } | |
144 | } else { | |
145 | $RT = $IRT + $RAND*$IRT; | |
146 | } | |
147 | ||
148 | my $rt_end_time = time() + $RT; | |
f6b8f48d | 149 | if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) { |
98bd7ca0 DH |
150 | $rt_end_time = $mrd_end_time; |
151 | } | |
152 | ||
153 | for (;;) { | |
154 | my $timeout = $rt_end_time - time(); | |
155 | if ($timeout < 0) { | |
156 | # print STDERR "Timeout waiting for DHCPv6 Advertise ", | |
157 | # "or Reply message.\n"; | |
158 | last; | |
159 | } | |
160 | ||
161 | my @ready = IO::Select->new(\*SOCK)->can_read($timeout); | |
162 | ||
163 | if (@ready) { | |
164 | my $reply; | |
165 | my $recv_ret; | |
f6b8f48d | 166 | |
98bd7ca0 DH |
167 | $recv_ret = recv(SOCK, $reply, 1500, 0); |
168 | if (not defined $recv_ret) { | |
f6b8f48d TM |
169 | printf STDERR |
170 | "Error \%d receiving DHCPv6 " . | |
98bd7ca0 DH |
171 | "message;\n\%s\n", |
172 | 0+$ERRNO, $ERRNO; | |
173 | exit(1); | |
174 | } | |
175 | ||
176 | $reply_msg = dhcp_client::msg::decode($reply); | |
177 | if (($reply_msg->{msg_type} == $MSG_ADVERTISE) || | |
178 | ($reply_msg->{msg_type} == $MSG_REPLY)) { | |
179 | last; | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
f6b8f48d | 184 | } until ($reply_msg || |
98bd7ca0 DH |
185 | (($MRC != 0) && ($count > $MRC)) || |
186 | (defined($mrd_end_time) && ($mrd_end_time > time()))); | |
187 | ||
188 | unless ($reply_msg) { | |
189 | if (($MRC != 0) && ($count >= $MRC)) { | |
f6b8f48d | 190 | print STDERR |
98bd7ca0 DH |
191 | "No reply after maximum retransmission count.\n"; |
192 | } else { | |
f6b8f48d | 193 | print STDERR |
98bd7ca0 DH |
194 | "No reply after maximum retransmission duration.\n"; |
195 | } | |
196 | } | |
197 | ||
198 | if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) { | |
199 | print "Got DHCPv6 Reply message.\n"; | |
200 | exit(0); | |
201 | } | |
202 | ||
203 | #$Data::Dumper::Useqq = 1; | |
204 | #print Dumper($msg), "\n"; | |
205 | #print Dumper($msg->packet()), "\n"; | |
206 | # | |
207 | #print "packet length: ", length($msg->packet()), "\n"; |