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>