Annotation of embedaddon/dhcp/tests/DHCPv6/dhcp_client.pm, revision 1.1.1.1
1.1 misho 1: #! /usr/bin/perl -w
2:
3: # Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
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.
18: # 950 Charter Street
19: # Redwood City, CA 94063
20: # <info@isc.org>
21: # https://www.isc.org/
22:
23: package dhcp_client;
24:
25: require Exporter;
26:
27: @ISA = qw(Exporter);
28:
29: # message types
30: $MSG_SOLICIT = 1;
31: $MSG_ADVERTISE = 2;
32: $MSG_REQUEST = 3;
33: $MSG_CONFIRM = 4;
34: $MSG_RENEW = 5;
35: $MSG_REBIND = 6;
36: $MSG_REPLY = 7;
37: $MSG_RELEASE = 8;
38: $MSG_DECLINE = 9;
39: $MSG_RECONFIGURE = 10;
40: $MSG_INFORMATION_REQUEST = 11;
41: $MSG_RELAY_FORW = 12;
42: $MSG_RELAY_REPL = 13;
43:
44: # option numbers
45: $OPT_CLIENTID = 1;
46: $OPT_SERVERID = 2;
47: $OPT_IA_NA = 3;
48: $OPT_IA_TA = 4;
49: $OPT_IAADDR = 5;
50: $OPT_ORO = 6;
51: $OPT_PREFERENCE = 7;
52: $OPT_ELAPSED_TIME = 8;
53: $OPT_RELAY_MSG = 9;
54: $OPT_AUTH = 11;
55: $OPT_UNICAST = 12;
56: $OPT_STATUS_CODE = 13;
57: $OPT_RAPID_COMMIT = 14;
58: $OPT_USER_CLASS = 15;
59: $OPT_VENDOR_CLASS = 16;
60: $OPT_VENDOR_OPTS = 17;
61: $OPT_INTERFACE_ID = 18;
62: $OPT_RECONF_MSG = 19;
63: $OPT_RECONF_ACCEPT = 20;
64:
65: # timeouts
66: $SOL_MAX_DELAY = 1;
67: $SOL_TIMEOUT = 1;
68: $SOL_MAX_RT = 120;
69: $REQ_TIMEOUT = 1;
70: $REQ_MAX_RT = 30;
71: $REQ_MAX_RC = 10;
72: $CNF_MAX_DELAY = 1;
73: $CNF_MAX_RT = 4;
74: $CNF_MAX_RD = 10;
75: $REN_TIMEOUT = 10;
76: $REN_MAX_RT = 600;
77: $REB_TIMEOUT = 10;
78: $REB_MAX_RT = 600;
79: $INF_MAX_DELAY = 1;
80: $INF_TIMEOUT = 1;
81: $INF_MAX_RT = 120;
82: $REL_TIMEOUT = 1;
83: $REL_MAX_RC = 5;
84: $DEC_TIMEOUT = 1;
85: $DEC_MAX_RC = 5;
86: $REC_TIMEOUT = 2;
87: $REC_MAX_RC = 8;
88: $HOP_COUNT_LIMIT = 32;
89:
90: @EXPORT = qw( $MSG_SOLICIT $MSG_ADVERTISE $MSG_REQUEST $MSG_CONFIRM
91: $MSG_RENEW $MSG_REBIND $MSG_REPLY $MSG_RELEASE $MSG_DECLINE
92: $MSG_RECONFIGURE $MSG_INFORMATION_REQUEST $MSG_RELAY_FORW
93: $MSG_RELAY_REPL
94: $OPT_CLIENTID $OPT_SERVERID $OPT_IA_NA $OPT_IA_TA $OPT_IAADDR
95: $OPT_ORO $OPT_PREFERENCE $OPT_ELAPSED_TIME $OPT_RELAY_MSG
96: $OPT_AUTH $OPT_UNICAST $OPT_STATUS_CODE $OPT_RAPID_COMMIT
97: $OPT_USER_CLASS $OPT_VENDOR_CLASS $OPT_VENDOR_OPTS
98: $OPT_INTERFACE_ID $OPT_RECONF_MSG $OPT_RECONF_ACCEPT
99: $SOL_MAX_DELAY $SOL_TIMEOUT $SOL_MAX_RT $REQ_TIMEOUT
100: $REQ_MAX_RT $REQ_MAX_RC $CNF_MAX_DELAY $CNF_MAX_RT
101: $CNF_MAX_RD $REN_TIMEOUT $REN_MAX_RT $REB_TIMEOUT $REB_MAX_RT
102: $INF_MAX_DELAY $INF_TIMEOUT $INF_MAX_RT $REL_TIMEOUT
103: $REL_MAX_RC $DEC_TIMEOUT $DEC_MAX_RC $REC_TIMEOUT $REC_MAX_RC
104: $HOP_COUNT_LIMIT );
105:
106: my %msg_type_num = (
107: MSG_SOLICIT => 1,
108: MSG_ADVERTISE => 2,
109: MSG_REQUEST => 3,
110: MSG_CONFIRM => 4,
111: MSG_RENEW => 5,
112: MSG_REBIND => 6,
113: MSG_REPLY => 7,
114: MSG_RELEASE => 8,
115: MSG_DECLINE => 9,
116: MSG_RECONFIGURE => 10,
117: MSG_INFORMATION_REQUEST => 11,
118: MSG_RELAY_FORW => 12,
119: MSG_RELAY_REPL => 13,
120: );
121: my %msg_num_type = reverse(%msg_type_num);
122:
123: my %opt_type_num = (
124: OPT_CLIENTID => 1,
125: OPT_SERVERID => 2,
126: OPT_IA_NA => 3,
127: OPT_IA_TA => 4,
128: OPT_IAADDR => 5,
129: OPT_ORO => 6,
130: OPT_PREFERENCE => 7,
131: OPT_ELAPSED_TIME => 8,
132: OPT_RELAY_MSG => 9,
133: OPT_AUTH => 11,
134: OPT_UNICAST => 12,
135: OPT_STATUS_CODE => 13,
136: OPT_RAPID_COMMIT => 14,
137: OPT_USER_CLASS => 15,
138: OPT_VENDOR_CLASS => 16,
139: OPT_VENDOR_OPTS => 17,
140: OPT_INTERFACE_ID => 18,
141: OPT_RECONF_MSG => 19,
142: OPT_RECONF_ACCEPT => 20,
143: );
144: my %opt_num_type = reverse(%opt_type_num);
145:
146: my %status_code_num = (
147: Success => 0,
148: UnspecFail => 1,
149: NoAddrsAvail => 2,
150: NoBinding => 3,
151: NotOnLink => 4,
152: UseMulticast => 5,
153: );
154: my %status_num_code = reverse(%status_code_num);
155:
156: my %docsis_type_num = (
157: CL_OPTION_ORO => 1,
158: CL_OPTION_TFTP_SERVERS => 32,
159: CL_OPTION_CONFIG_FILE_NAME => 33,
160: CL_OPTION_SYSLOG_SERVERS => 34,
161: CL_OPTION_TLV5 => 35,
162: CL_OPTION_DEVICE_ID => 36,
163: CL_OPTION_CCC => 37,
164: CL_OPTION_DOCSIS_VERS => 38,
165: );
166: my %docsis_num_type = reverse(%docsis_type_num);
167:
168: use strict;
169: use English;
170: use POSIX;
171:
172: # XXX: very Solaris-specific
173: sub iface {
174: my @ifaces;
175: foreach my $fname (glob("/etc/hostname.*")) {
176: $fname =~ s[^/etc/hostname.][];
177: push(@ifaces, $fname);
178: }
179: return wantarray() ? @ifaces : $ifaces[0];
180: }
181:
182: # XXX: very Solaris-specific
183: sub mac_addr {
184: my @ip_addrs;
185: foreach my $iface (iface()) {
186: if (`ifconfig $iface 2>/dev/null` =~ /\sinet (\S+)\s/) {
187: push(@ip_addrs, $1);
188: }
189: }
190: my @mac_addrs;
191: foreach my $line (split(/\n/, `arp -an 2>/dev/null`)) {
192: my @parts = split(/\s+/, $line);
193: my $ip = $parts[1];
194: my $mac = $parts[-1];
195: if (grep { $ip eq $_ } @ip_addrs) {
196: $mac =~ s/://g;
197: push(@mac_addrs, $mac);
198: }
199: }
200: return wantarray() ? @mac_addrs : $mac_addrs[0];
201: }
202:
203: sub mac_addr_binary {
204: my @mac_addr = split(//, mac_addr());
205: my $mac_addr = join("", map { chr(hex($_)) } @mac_addr);
206: return $mac_addr;
207: }
208:
209: # DHCPv6 times start 2000-01-01 00:00:00
210: my $dhcp_time_base = 946684800;
211: #{
212: # local $ENV{TZ} = "UTC";
213: # POSIX::tzset();
214: # $dhcp_time_base = POSIX::mktime(0, 0, 0, 1, 0, 100);
215: #}
216:
217: sub dhcpv6_time {
218: return time() - $dhcp_time_base;
219: }
220:
221: sub duid {
222: my ($type) = @_;
223:
224: $type = 1 unless (defined $type);
225:
226: if (($type == 1) || ($type == 3)) {
227: my $mac_addr = mac_addr_binary();
228: if ($type == 1) {
229: my $time = pack("N", dhcpv6_time());
230: return "\x00\x01\x00\x01${time}${mac_addr}";
231: } else {
232: return "\x00\x03\x00\x01${mac_addr}";
233: }
234: } else {
235: die "Unknown DUID type $type requested";
236: }
237: }
238:
239: package dhcp_client::msg;
240:
241: use Socket;
242: use Socket6;
243:
244: sub new {
245: my ($pkg, $msg_type, $trans_id) = @_;
246:
247: my $this = {};
248: bless $this;
249:
250: $this->{msg_type} = $msg_type+0;
251: if (defined $trans_id) {
252: $this->{trans_id} = $trans_id;
253: } else {
254: $this->{trans_id} = chr(rand(256)) .
255: chr(rand(256)) . chr(rand(256));
256: }
257: $this->{options} = [ ];
258:
259: return $this;
260: }
261:
262:
263: sub add_option {
264: my ($this, $num, $data) = @_;
265:
266: push(@{$this->{options}}, [ $num, $data ]);
267: }
268:
269: sub get_option {
270: my ($this, $num) = @_;
271: my @options;
272: foreach my $option (@{$this->{options}}) {
273: if ($option->[0] == $num) {
274: push(@options, $option->[1]);
275: }
276: }
277: return wantarray() ? @options : $options[0];
278: }
279:
280: sub packed_options {
281: my ($this) = @_;
282:
283: my $options = "";
284: foreach my $option (@{$this->{options}}) {
285: $options .= pack("nn", $option->[0], length($option->[1]));
286: $options .= $option->[1];
287: }
288: return $options;
289: }
290:
291: sub packet {
292: my ($this) = @_;
293:
294: my $packet = "";
295: $packet .= chr($this->{msg_type});
296: $packet .= $this->{trans_id};
297: $packet .= $this->packed_options();
298: return $packet;
299: }
300:
301: sub unpack_options {
302: my ($options) = @_;
303:
304: my @parsed_options;
305: my $p = 0;
306: while ($p < length($options)) {
307: my ($id, $len) = unpack("nn", substr($options, $p, 4));
308: push(@parsed_options, [ $id, substr($options, $p + 4, $len) ]);
309: $p += 4 + $len;
310: }
311: return @parsed_options;
312: }
313:
314: sub print_docsis_option {
315: my ($num, $data, $indent) = @_;
316:
317: print "${indent}DOCSIS Option $num";
318: if ($docsis_num_type{$num}) {
319: print " ($docsis_num_type{$num})";
320: }
321: print ", length ", length($data), "\n";
322:
323: return unless ($docsis_num_type{$num});
324:
325: if ($docsis_num_type{$num} eq "CL_OPTION_ORO") {
326: my $num_oro = length($data) / 2;
327: for (my $i=0; $i<$num_oro; $i++) {
328: my $oro_num = unpack("n", substr($data, $i*2, 2));
329: print "${indent} $oro_num";
330: if ($docsis_num_type{$oro_num}) {
331: print " ($docsis_num_type{$oro_num})";
332: }
333: print "\n";
334: }
335: } elsif ($docsis_num_type{$num} eq "CL_OPTION_TFTP_SERVERS") {
336: my $num_servers = length($data) / 16;
337: for (my $i=0; $i<$num_servers; $i++) {
338: my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16));
339: print "$indent TFTP server ", ($i+1), ": ";
340: print uc($srv), "\n";
341: }
342: } elsif ($docsis_num_type{$num} eq "CL_OPTION_CONFIG_FILE_NAME") {
343: print "$indent Config file name: \"$data\"\n"
344: } elsif ($docsis_num_type{$num} eq "CL_OPTION_SYSLOG_SERVERS") {
345: my $num_servers = length($data) / 16;
346: for (my $i=0; $i<$num_servers; $i++) {
347: my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16));
348: print "$indent syslog server ", ($i+1), ": ";
349: print uc($srv), "\n";
350: }
351: }
352: }
353:
354: sub print_option {
355: my ($num, $data, $indent) = @_;
356:
357: print "${indent}Option $num";
358: if ($opt_num_type{$num}) {
359: print " ($opt_num_type{$num})";
360: }
361: print ", length ", length($data), "\n";
362: if ($num == $dhcp_client::OPT_ORO) {
363: my $num_oro = length($data) / 2;
364: for (my $i=0; $i<$num_oro; $i++) {
365: my $oro_num = unpack("n", substr($data, $i*2, 2));
366: print "${indent} $oro_num";
367: if ($opt_num_type{$oro_num}) {
368: print " ($opt_num_type{$oro_num})";
369: }
370: print "\n";
371: }
372: } elsif (($num == $dhcp_client::OPT_CLIENTID) ||
373: ($num == $dhcp_client::OPT_SERVERID)) {
374: print $indent, " ";
375: if (length($data) > 0) {
376: printf '%02X', ord(substr($data, 0, 1));
377: for (my $i=1; $i<length($data); $i++) {
378: printf ':%02X', ord(substr($data, $i, 1));
379: }
380: }
381: print "\n";
382: } elsif ($num == $dhcp_client::OPT_IA_NA) {
383: printf "${indent} IAID: 0x\%08X\n",
384: unpack("N", substr($data, 0, 4));
385: printf "${indent} T1: \%d\n", unpack("N", substr($data, 4, 4));
386: printf "${indent} T2: \%d\n", unpack("N", substr($data, 8, 4));
387: if (length($data) > 12) {
388: printf "${indent} IA_NA encapsulated options:\n";
389: foreach my $option (unpack_options(substr($data, 12))) {
390: print_option(@{$option}, $indent . " ");
391: }
392: }
393: } elsif ($num == $dhcp_client::OPT_IAADDR) {
394: printf "${indent} IPv6 address: \%s\n",
395: uc(inet_ntop(AF_INET6, substr($data, 0, 16)));
396: printf "${indent} Preferred lifetime: \%d\n",
397: unpack("N", substr($data, 16, 4));
398: printf "${indent} Valid lifetime: \%d\n",
399: unpack("N", substr($data, 20, 4));
400: if (length($data) > 24) {
401: printf "${indent} IAADDR encapsulated options:\n";
402: foreach my $option (unpack_options(substr($data, 24))) {
403: print_option(@{$option}, $indent . " ");
404: }
405: }
406: } elsif ($num == $dhcp_client::OPT_VENDOR_OPTS) {
407: my $enterprise_number = unpack("N", substr($data, 0, 4));
408: print "${indent} Enterprise number: $enterprise_number\n";
409:
410: # DOCSIS
411: if ($enterprise_number == 4491) {
412: foreach my $option (unpack_options(substr($data, 4))) {
413: print_docsis_option(@{$option}, $indent . " ");
414: }
415: }
416: } elsif ($num == $dhcp_client::OPT_STATUS_CODE) {
417: my $code = ord(substr($data, 0, 1));
418: my $msg = substr($data, 1);
419: print "${indent} Code: $code";
420: if ($status_num_code{$code}) {
421: print " ($status_num_code{$code})";
422: }
423: print "\n";
424: print "${indent} Message: \"$msg\"\n";
425: }
426: }
427:
428: # XXX: we aren't careful about packet boundaries and values...
429: # DO NOT RUN ON PRODUCTION SYSTEMS!!!
430: sub decode {
431: my ($packet, $print) = @_;
432:
433: my $msg_type = ord(substr($packet, 0, 1));
434: my $trans_id = substr($packet, 1, 3);
435: my $msg = dhcp_client::msg->new($msg_type, $trans_id);
436:
437: if ($print) {
438: print "DHCPv6 packet\n";
439: print " Message type: $msg_num_type{$msg_type}\n";
440: printf " Transaction id: 0x\%02X\%02X\%02X\n",
441: ord(substr($trans_id, 0, 1)),
442: ord(substr($trans_id, 1, 1)),
443: ord(substr($trans_id, 2, 1));
444: print " Options:\n";
445: }
446:
447: foreach my $option (unpack_options(substr($packet, 4))) {
448: print_option(@{$option}, " ") if ($print);
449: $msg->add_option(@{$option});
450: }
451:
452: return $msg;
453: }
454:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>