📄 dhcp_client.pm
字号:
#! /usr/bin/perl -w# Copyright (c) 2007 by Internet Systems Consortium, Inc. ("ISC")## Permission to use, copy, modify, and distribute this software for any# purpose with or without fee is hereby granted, provided that the above# copyright notice and this permission notice appear in all copies.## THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.## Internet Systems Consortium, Inc.# 950 Charter Street# Redwood City, CA 94063# <info@isc.org># http://www.isc.org/package dhcp_client;require Exporter;@ISA = qw(Exporter);# message types$MSG_SOLICIT = 1;$MSG_ADVERTISE = 2;$MSG_REQUEST = 3;$MSG_CONFIRM = 4;$MSG_RENEW = 5;$MSG_REBIND = 6;$MSG_REPLY = 7;$MSG_RELEASE = 8;$MSG_DECLINE = 9;$MSG_RECONFIGURE = 10;$MSG_INFORMATION_REQUEST = 11;$MSG_RELAY_FORW = 12;$MSG_RELAY_REPL = 13;# option numbers$OPT_CLIENTID = 1;$OPT_SERVERID = 2;$OPT_IA_NA = 3;$OPT_IA_TA = 4;$OPT_IAADDR = 5;$OPT_ORO = 6;$OPT_PREFERENCE = 7;$OPT_ELAPSED_TIME = 8;$OPT_RELAY_MSG = 9;$OPT_AUTH = 11;$OPT_UNICAST = 12;$OPT_STATUS_CODE = 13;$OPT_RAPID_COMMIT = 14;$OPT_USER_CLASS = 15;$OPT_VENDOR_CLASS = 16;$OPT_VENDOR_OPTS = 17;$OPT_INTERFACE_ID = 18;$OPT_RECONF_MSG = 19;$OPT_RECONF_ACCEPT = 20;# timeouts$SOL_MAX_DELAY = 1;$SOL_TIMEOUT = 1;$SOL_MAX_RT = 120;$REQ_TIMEOUT = 1;$REQ_MAX_RT = 30;$REQ_MAX_RC = 10;$CNF_MAX_DELAY = 1;$CNF_MAX_RT = 4;$CNF_MAX_RD = 10;$REN_TIMEOUT = 10;$REN_MAX_RT = 600;$REB_TIMEOUT = 10;$REB_MAX_RT = 600;$INF_MAX_DELAY = 1;$INF_TIMEOUT = 1;$INF_MAX_RT = 120;$REL_TIMEOUT = 1;$REL_MAX_RC = 5;$DEC_TIMEOUT = 1;$DEC_MAX_RC = 5;$REC_TIMEOUT = 2;$REC_MAX_RC = 8;$HOP_COUNT_LIMIT = 32;@EXPORT = qw( $MSG_SOLICIT $MSG_ADVERTISE $MSG_REQUEST $MSG_CONFIRM $MSG_RENEW $MSG_REBIND $MSG_REPLY $MSG_RELEASE $MSG_DECLINE $MSG_RECONFIGURE $MSG_INFORMATION_REQUEST $MSG_RELAY_FORW $MSG_RELAY_REPL $OPT_CLIENTID $OPT_SERVERID $OPT_IA_NA $OPT_IA_TA $OPT_IAADDR $OPT_ORO $OPT_PREFERENCE $OPT_ELAPSED_TIME $OPT_RELAY_MSG $OPT_AUTH $OPT_UNICAST $OPT_STATUS_CODE $OPT_RAPID_COMMIT $OPT_USER_CLASS $OPT_VENDOR_CLASS $OPT_VENDOR_OPTS $OPT_INTERFACE_ID $OPT_RECONF_MSG $OPT_RECONF_ACCEPT $SOL_MAX_DELAY $SOL_TIMEOUT $SOL_MAX_RT $REQ_TIMEOUT $REQ_MAX_RT $REQ_MAX_RC $CNF_MAX_DELAY $CNF_MAX_RT $CNF_MAX_RD $REN_TIMEOUT $REN_MAX_RT $REB_TIMEOUT $REB_MAX_RT $INF_MAX_DELAY $INF_TIMEOUT $INF_MAX_RT $REL_TIMEOUT $REL_MAX_RC $DEC_TIMEOUT $DEC_MAX_RC $REC_TIMEOUT $REC_MAX_RC $HOP_COUNT_LIMIT );my %msg_type_num = ( MSG_SOLICIT => 1, MSG_ADVERTISE => 2, MSG_REQUEST => 3, MSG_CONFIRM => 4, MSG_RENEW => 5, MSG_REBIND => 6, MSG_REPLY => 7, MSG_RELEASE => 8, MSG_DECLINE => 9, MSG_RECONFIGURE => 10, MSG_INFORMATION_REQUEST => 11, MSG_RELAY_FORW => 12, MSG_RELAY_REPL => 13,);my %msg_num_type = reverse(%msg_type_num);my %opt_type_num = ( OPT_CLIENTID => 1, OPT_SERVERID => 2, OPT_IA_NA => 3, OPT_IA_TA => 4, OPT_IAADDR => 5, OPT_ORO => 6, OPT_PREFERENCE => 7, OPT_ELAPSED_TIME => 8, OPT_RELAY_MSG => 9, OPT_AUTH => 11, OPT_UNICAST => 12, OPT_STATUS_CODE => 13, OPT_RAPID_COMMIT => 14, OPT_USER_CLASS => 15, OPT_VENDOR_CLASS => 16, OPT_VENDOR_OPTS => 17, OPT_INTERFACE_ID => 18, OPT_RECONF_MSG => 19, OPT_RECONF_ACCEPT => 20,);my %opt_num_type = reverse(%opt_type_num);my %status_code_num = ( Success => 0, UnspecFail => 1, NoAddrsAvail => 2, NoBinding => 3, NotOnLink => 4, UseMulticast => 5,);my %status_num_code = reverse(%status_code_num);my %docsis_type_num = ( CL_OPTION_ORO => 1, CL_OPTION_TFTP_SERVERS => 32, CL_OPTION_CONFIG_FILE_NAME => 33, CL_OPTION_SYSLOG_SERVERS => 34, CL_OPTION_TLV5 => 35, CL_OPTION_DEVICE_ID => 36, CL_OPTION_CCC => 37, CL_OPTION_DOCSIS_VERS => 38,);my %docsis_num_type = reverse(%docsis_type_num); use strict;use English;use POSIX;# XXX: very Solaris-specificsub iface { my @ifaces; foreach my $fname (glob("/etc/hostname.*")) { $fname =~ s[^/etc/hostname.][]; push(@ifaces, $fname); } return wantarray() ? @ifaces : $ifaces[0];}# XXX: very Solaris-specificsub mac_addr { my @ip_addrs; foreach my $iface (iface()) { if (`ifconfig $iface 2>/dev/null` =~ /\sinet (\S+)\s/) { push(@ip_addrs, $1); } } my @mac_addrs; foreach my $line (split(/\n/, `arp -an 2>/dev/null`)) { my @parts = split(/\s+/, $line); my $ip = $parts[1]; my $mac = $parts[-1]; if (grep { $ip eq $_ } @ip_addrs) { $mac =~ s/://g; push(@mac_addrs, $mac); } } return wantarray() ? @mac_addrs : $mac_addrs[0];}sub mac_addr_binary { my @mac_addr = split(//, mac_addr()); my $mac_addr = join("", map { chr(hex($_)) } @mac_addr); return $mac_addr;}# DHCPv6 times start 2000-01-01 00:00:00my $dhcp_time_base = 946684800;#{# local $ENV{TZ} = "UTC";# POSIX::tzset();# $dhcp_time_base = POSIX::mktime(0, 0, 0, 1, 0, 100);#}sub dhcpv6_time { return time() - $dhcp_time_base;}sub duid { my ($type) = @_; $type = 1 unless (defined $type); if (($type == 1) || ($type == 3)) { my $mac_addr = mac_addr_binary(); if ($type == 1) { my $time = pack("N", dhcpv6_time()); return "\x00\x01\x00\x01${time}${mac_addr}"; } else { return "\x00\x03\x00\x01${mac_addr}"; } } else { die "Unknown DUID type $type requested"; }}package dhcp_client::msg;use Socket;use Socket6;sub new { my ($pkg, $msg_type, $trans_id) = @_; my $this = {}; bless $this; $this->{msg_type} = $msg_type+0; if (defined $trans_id) { $this->{trans_id} = $trans_id; } else { $this->{trans_id} = chr(rand(256)) . chr(rand(256)) . chr(rand(256)); } $this->{options} = [ ]; return $this;}sub add_option { my ($this, $num, $data) = @_; push(@{$this->{options}}, [ $num, $data ]);}sub get_option { my ($this, $num) = @_; my @options; foreach my $option (@{$this->{options}}) { if ($option->[0] == $num) { push(@options, $option->[1]); } } return wantarray() ? @options : $options[0];}sub packed_options { my ($this) = @_; my $options = ""; foreach my $option (@{$this->{options}}) { $options .= pack("nn", $option->[0], length($option->[1])); $options .= $option->[1]; } return $options;}sub packet { my ($this) = @_; my $packet = ""; $packet .= chr($this->{msg_type}); $packet .= $this->{trans_id}; $packet .= $this->packed_options(); return $packet;}sub unpack_options { my ($options) = @_; my @parsed_options; my $p = 0; while ($p < length($options)) { my ($id, $len) = unpack("nn", substr($options, $p, 4)); push(@parsed_options, [ $id, substr($options, $p + 4, $len) ]); $p += 4 + $len; } return @parsed_options;}sub print_docsis_option { my ($num, $data, $indent) = @_; print "${indent}DOCSIS Option $num"; if ($docsis_num_type{$num}) { print " ($docsis_num_type{$num})"; } print ", length ", length($data), "\n"; return unless ($docsis_num_type{$num}); if ($docsis_num_type{$num} eq "CL_OPTION_ORO") { my $num_oro = length($data) / 2; for (my $i=0; $i<$num_oro; $i++) { my $oro_num = unpack("n", substr($data, $i*2, 2)); print "${indent} $oro_num"; if ($docsis_num_type{$oro_num}) { print " ($docsis_num_type{$oro_num})"; } print "\n"; } } elsif ($docsis_num_type{$num} eq "CL_OPTION_TFTP_SERVERS") { my $num_servers = length($data) / 16; for (my $i=0; $i<$num_servers; $i++) { my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16)); print "$indent TFTP server ", ($i+1), ": "; print uc($srv), "\n"; } } elsif ($docsis_num_type{$num} eq "CL_OPTION_CONFIG_FILE_NAME") { print "$indent Config file name: \"$data\"\n" } elsif ($docsis_num_type{$num} eq "CL_OPTION_SYSLOG_SERVERS") { my $num_servers = length($data) / 16; for (my $i=0; $i<$num_servers; $i++) { my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16)); print "$indent syslog server ", ($i+1), ": "; print uc($srv), "\n"; } }}sub print_option { my ($num, $data, $indent) = @_; print "${indent}Option $num"; if ($opt_num_type{$num}) { print " ($opt_num_type{$num})"; } print ", length ", length($data), "\n"; if ($num == $dhcp_client::OPT_ORO) { my $num_oro = length($data) / 2; for (my $i=0; $i<$num_oro; $i++) { my $oro_num = unpack("n", substr($data, $i*2, 2)); print "${indent} $oro_num"; if ($opt_num_type{$oro_num}) { print " ($opt_num_type{$oro_num})"; } print "\n"; } } elsif (($num == $dhcp_client::OPT_CLIENTID) || ($num == $dhcp_client::OPT_SERVERID)) { print $indent, " "; if (length($data) > 0) { printf '%02X', ord(substr($data, 0, 1)); for (my $i=1; $i<length($data); $i++) { printf ':%02X', ord(substr($data, $i, 1)); } } print "\n"; } elsif ($num == $dhcp_client::OPT_IA_NA) { printf "${indent} IAID: 0x\%08X\n", unpack("N", substr($data, 0, 4)); printf "${indent} T1: \%d\n", unpack("N", substr($data, 4, 4)); printf "${indent} T2: \%d\n", unpack("N", substr($data, 8, 4)); if (length($data) > 12) { printf "${indent} IA_NA encapsulated options:\n"; foreach my $option (unpack_options(substr($data, 12))) { print_option(@{$option}, $indent . " "); } } } elsif ($num == $dhcp_client::OPT_IAADDR) { printf "${indent} IPv6 address: \%s\n", uc(inet_ntop(AF_INET6, substr($data, 0, 16))); printf "${indent} Preferred lifetime: \%d\n", unpack("N", substr($data, 16, 4)); printf "${indent} Valid lifetime: \%d\n", unpack("N", substr($data, 20, 4)); if (length($data) > 24) { printf "${indent} IAADDR encapsulated options:\n"; foreach my $option (unpack_options(substr($data, 24))) { print_option(@{$option}, $indent . " "); } } } elsif ($num == $dhcp_client::OPT_VENDOR_OPTS) { my $enterprise_number = unpack("N", substr($data, 0, 4)); print "${indent} Enterprise number: $enterprise_number\n"; # DOCSIS if ($enterprise_number == 4491) { foreach my $option (unpack_options(substr($data, 4))) { print_docsis_option(@{$option}, $indent . " "); } } } elsif ($num == $dhcp_client::OPT_STATUS_CODE) { my $code = ord(substr($data, 0, 1)); my $msg = substr($data, 1); print "${indent} Code: $code"; if ($status_num_code{$code}) { print " ($status_num_code{$code})"; } print "\n"; print "${indent} Message: \"$msg\"\n"; } }# XXX: we aren't careful about packet boundaries and values... # DO NOT RUN ON PRODUCTION SYSTEMS!!!sub decode { my ($packet, $print) = @_; my $msg_type = ord(substr($packet, 0, 1)); my $trans_id = substr($packet, 1, 3); my $msg = dhcp_client::msg->new($msg_type, $trans_id); if ($print) { print "DHCPv6 packet\n"; print " Message type: $msg_num_type{$msg_type}\n"; printf " Transaction id: 0x\%02X\%02X\%02X\n", ord(substr($trans_id, 0, 1)), ord(substr($trans_id, 1, 1)), ord(substr($trans_id, 2, 1)); print " Options:\n"; } foreach my $option (unpack_options(substr($packet, 4))) { print_option(@{$option}, " ") if ($print); $msg->add_option(@{$option}); } return $msg;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -