*** From dhcp-server -- To unsubscribe, see the end of this message. ***
Gary Mills writes:
> At times, it would be quite useful to view portions of the database
> that the DHCP server keeps in memory. For example, it would be very
> helpful sometimes to determine the ethernet address associated with
> a particular IP address. Has anyone done this with the ISC DHCP
> server? Parsing both the dhcpd.conf and dhcpd.leases file should
> yield the same result, but it would be quite a complicated process,
> and duplicates what the server already does.
Run this and you'll get a report of what IP's are leased to whom, and
also you'll get a breakdown by subnet. We run it hourly with the
output mailed to the assistant network guru.
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# showleases
# IP.pm
#
echo x - showleases
sed 's/^X//' >showleases << 'END-of-showleases'
X#!/usr/local/bin/perl
X
Xuse IP;
X
X#location of the lease file
X$CONF_FILENAME = shift @ARGV;
X$LEASE_FILENAME = shift @ARGV;
X
X$now = `date -u '+%Y/%m/%d %H:%M:%S'`;
Xchop $now;
X
X# print "[$now]\n";
X
Xsub print_text_header {
X print "DHCP leases at $now\n";
X print "All times are UTC\n";
X print "Start End ip_addr mac_addr\n";
X print
"----------------------------------------------------------------------------\n";
X}
X
Xopen(CONF_FILE, "<$CONF_FILENAME") || die "Can't read $CONF_FILENAME";
Xwhile(<CONF_FILE>) {
X chop;
X LINE:
X {
X /subnet\s*(\S+)\s*netmask\s*(\S+)/ && do {
X $subnet_addr = $1;
X $netmask = $2;
X zeroize(\$subnet_addr);
X zeroize(\$netmask);
X $subnets{$subnet_addr} = $netmask;
X $netmasks{$netmask} = 1;
X last LINE;
X }
X }
X}
Xclose CONF_FILE;
X
X# print join("\n", sort(keys %netmasks)), "\n";
X# exit 0;
X
X
Xopen(LEASEFILE, "<$LEASE_FILENAME") || die "can't read $LEASE_FILENAME";
Xwhile(<LEASEFILE>) {
X chop;
X LINE:
X {
X /^lease (.*) {/ && do {
X $ip_addr = $1;
X &zeroize(\$ip_addr);
X last LINE;
X };
X /starts (\d+) (.*) (.*);/ && do {
X ($n, $date, $time) = ($1, $2, $3);
X $start = "$date $time";
X $began = $start lt $now;
X # print "$began = $start < $now\n";
X last LINE;
X };
X /ends (\d+) (.*) (.*);/ && do {
X ($n, $date, $time) = ($1, $2, $3);
X $end = "$date $time";
X $over = $end lt $now;
X last LINE;
X };
X /hardware ethernet (.*);/ && do {
X $mac = $1;
X last LINE;
X };
X /}/ && do {
X next LINE if defined $leases{$ip_addr};
X MASK:
X foreach $mask ( sort keys %netmasks ) {
X $probe = &mask($ip_addr, $mask);
X # print "PROBE $ip_addr/$mask $probe\n";
X if($subnets{$probe} eq $mask) {
X # print "SUBNET $probe\n";
X $subnet = $probe;
X last MASK;
X }
X }
X $line = "$start $end $ip_addr $mac\n";
X # $line = "($subnet) $ip_addr $mac\n";
X # print "\nLINE $line";
X $leases{$ip_addr} = $line;
X if ($began && ! $over) {
X # print "ACTIVE\n";
X push(@list, $line);
X $active_leases{$ip_addr} = $line
X if !defined $active_leases{$ip_addr};
X $total_leases++;
X # print "Incrementing $subnet (was $found_leases{$subnet})\n";
X $found_leases{$subnet}++;
X }
X last LINE;
X };
X }
X}
X
X&print_text_header;
Xforeach $lease ( @leases{sort keys %active_leases} ) {
X $i++;
X # printf "%3d $lease", $i;
X print $lease;
X}
X# print join('', @leases{sort keys %active_leases});
X
Xprint "\n\n";
Xprint "Total leases: $total_leases\n";
Xprint "Subnet Number Active Leases\n";
Xprint "------------------------------------\n";
X
Xforeach $subnet ( sort keys %subnets ) {
X printf("%15s %8s\n", $subnet, $found_leases{$subnet});
X}
END-of-showleases
echo x - IP.pm
sed 's/^X//' >IP.pm << 'END-of-IP.pm'
X#!/usr/local/bin/perl
X
X=pod
X=head1 IP.pm - IP address arithmetic for Hostmaster
X
XAn IP address in canonical form is a string of the following form:
X
X NNN.NNN.NNN.NNN
X
XAn IP address in colloquial (common) form is a regular IP address with
Xno leading zeroes in the octets.
X
XThe functions in this module:
X
X=head2 mask($addr, $netmask)
X
XReturns the address masked by the mask.
X
X=head2 dezero(\$scalar_ref)
X
XExpects a reference to an address in canonical
Xform, and turns the address into colloquial form.
X
X=head2 zeroize(\$scalar_ref)
X
XExpects a reference to an address in common form, and turns the address into
Xcanonical form.
X
X=head2 complement($canonical_addr)
X
XExpects a canonical-form address, and returns its bitwise complement.
X
X=head2 incr_addr($addr)
X
XExpects a canonical address; returns the next address on that network
X(i.e. the address plus 1). Does no bounds checking to be sure you're
Xnot crossing over a subnet boundary.
X
X=head2 broadcastify($addr, $netmask)
X
XReturns the broadcast address for the subnet that $addr is a member
Xof, by complementing netmask and then bitwise-or'ing it in with the
Xaddress.
X
X=cut
X
X# while (<>) {
X# ($ip, $mask) = split(' ', $_);
X# print "MASKED ", mask($ip, $mask), "\n";
X# &dezero(\$ip);
X# print "DEZERO ", $ip, "\n";
X# &zeroize(\$ip);
X# print "REZERO ", $ip, "\n";
X# }
X
Xsub mask {
X my($ip, $mask) = @_;
X my @ip_bytes;
X my @mask_bytes;
X my @answer_bytes;
X
X use integer;
X
X &dezero(\$ip);
X &dezero(\$mask);
X @ip_bytes = split('\.', $ip);
X @mask_bytes = split('\.', $mask);
X # print "IP ", join('/', @ip_bytes), "\n";
X # print "MASK ", join('/', @mask_bytes), "\n";
X
X while(@ip_bytes) {
X use integer;
X $ans = int($ip_bytes[0]) & int($mask_bytes[0]);
X # print "$ip_bytes[0] & $mask_bytes[0] = $ans\n";
X push(@answer_bytes, $ans);
X shift @ip_bytes;
X shift @mask_bytes;
X }
X return sprintf("%03d.%03d.%03d.%03d",
X $answer_bytes[0],
X $answer_bytes[1],
X $answer_bytes[2],
X $answer_bytes[3]);
X}
X
Xsub dezero {
X my ($ip) = @_;
X $$ip =~ s/\.000/.0/g; # convert .000 to .0 in address
X $$ip =~ s/\.0+([1-9])/.$1/g; # convert .024 to .24 in address
X}
X
Xsub zeroize {
X my ($ip) = @_;
X my ($o1, $o2, $o3, $o4);
X
X ($o1, $o2, $o3, $o4) = split('\.', $$ip);
X $$ip = sprintf("%03d.%03d.%03d.%03d",
X $o1, $o2, $o3, $o4);
X}
X
Xsub complement {
X my($ip) = @_;
X my @ip_bytes;
X my @answer_bytes;
X
X use integer;
X
X &dezero(\$ip);
X @ip_bytes = split('\.', $ip);
X while(@ip_bytes) {
X use integer;
X $ans = abs(255 - $ip_bytes[0]);
X # print "$ip_bytes[0] & $mask_bytes[0] = $ans\n";
X push(@answer_bytes, $ans);
X shift @ip_bytes;
X }
X return sprintf("%03d.%03d.%03d.%03d",
X $answer_bytes[0],
X $answer_bytes[1],
X $answer_bytes[2],
X $answer_bytes[3]);
X}
X
X
X
Xsub incr_addr {
X my ($ip) = @_;
X my ($o1, $o2, $o3, $o4);
X
X &dezero(\$ip);
X ($o1, $o2, $o3, $o4) = split('\.', $ip);
X $o4++;
X if ($o4 > 255) {
X $o4=0;
X $o3++;
X if ($o3 > 255) {
X $o3=0;
X $o2++;
X if ($o2 > 255) {
X $o2=0;
X $o1++;
X }
X }
X }
X return sprintf("%03d.%03d.%03d.%03d",
X $o1, $o2, $o3, $o4);
X
X}
X
Xsub broadcastify {
X my($ip, $netmask) = @_;
X
X my @ip_bytes;
X my @mask_bytes;
X my @answer_bytes;
X
X use integer;
X
X &dezero(\$ip);
X &dezero(\$netmask);
X @ip_bytes = split('\.', $ip);
X @mask_bytes = split('\.', $netmask);
X # print "IP ", join('/', @ip_bytes), "\n";
X # print "MASK ", join('/', @mask_bytes), "\n";
X
X while(@ip_bytes) {
X use integer;
X $ans = int($ip_bytes[0]) | (256 + ~ int($mask_bytes[0]));
X # print "$ip_bytes[0] & $mask_bytes[0] = $ans\n";
X push(@answer_bytes, $ans);
X shift @ip_bytes;
X shift @mask_bytes;
X }
X return sprintf("%03d.%03d.%03d.%03d",
X $answer_bytes[0],
X $answer_bytes[1],
X $answer_bytes[2],
X $answer_bytes[3]);
X}
X
X
X1;
END-of-IP.pm
exit
------------------------------------------------------------------------------
To unsubscribe from this list, please visit http://www.fugue.com/dhcp/lists
If you are without web access, or if you are having trouble with the web page,
please send mail to [EMAIL PROTECTED] Please try to use the web
page first - it will take a long time for your request to be processed by hand.
------------------------------------------------------------------------------