Annotation of embedaddon/dhcp/tests/DHCPv6/dhcp_client.pm, revision 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>