DHCPView

DHCP is used to simplify network management. It is used to dynamically configure hosts on the network. A computer can be plugged in to the local ethernet and will get from the DHCP server all the information it needs to use the local network. The DHCP server hands out a 'lease' that is for a fixed length of time. It can be useful to keep an eye on how many leases have been handed out, how many are still valid, and how many have expired.

The following code works with the ISC DHCP server, version 2. These were written because the other lease reporting utilities I found where too complicated and didn't display the leases in order. I use two simple Perl snippets. One parses the lease file and returns the IP addresses, MAC addresses, expiry date/time, and hostname. The other sorts the IP addresses into numerical order to present the leases in an easy to scan format.

Parsing the leases file relies on the lease file format having leases recorded like:

lease 192.168.55.38 {
        starts 5 2002/02/15 20:37:19;
        ends 5 2002/02/15 22:37:19;
        hardware ethernet 00:e0:98:76:f9:c8;
        uid 01:00:e0:98:76:f9:c8;
        client-hostname "think";
}

I just scan through the file sequentially, plucking out the relevant data.

while (<LEASES>){
 my @in = split /\s+/, $_, 3;
 SWITCH: {
   if ($in[0] eq "lease"){ $ip = $in[1]; last SWITCH; }
   if ($in[1] eq "client-hostname"){
    $hostname{$ip} = substr($in[2],1,length($in[2])-4);
    last SWITCH;
   }
   if ($in[1] eq "ends"){
    $ends{$ip} = $in[2];
    last SWITCH;
   }
   if ($in[1] eq "hardware"){
    $mac{$ip} = substr($in[2],9,17);
    last SWITCH;
   }
 }
}

There are sometimes multiple entries in the leases file for an IP address. This snippet relies on the last mention of an IP address in the lease file being the current lease. Fortunately that's how the ISC dhcpd version 2 writes the lease file.

Sorting IP addresses into order is done by comparing each octet, starting with the first. If there is a difference, there is no need to do the next comparison.

sub byip {
 my @a = split(/\./,$a);
 my @b = split(/\./,$b);
 ($a[0] <=> $b[0]) or ($a[1] <=> $b[1]) or ($a[2] <=> $b[2]) or ($a[3] <=> $b[3]);
}

I've used these two snippets in a simple CGI that displays the state of the leases on the server. It needs Time::Local to do the lease expiry time comparison. The only configuration needed could be the path to Perl and the path to the lease file.

#! /usr/bin/perl -w
use Time::Local;

$leasefile = "/var/db/dhcpd.leases";

print q{Content-type: text/html

<html>
<head>
<title>DHCP Lease Viewer</title>
</head>
<body bgcolor=white>
<h1>DHCPView</h1>
Leases are colour coded, <span style="color: green">green</span> for active,
<span style="color: red">red</span> for expired.
<table>
<tr><th>IP Address</th><th>MAC Address</th><th>Hostname</th></tr>
};

open LEASES, $leasefile or die "Couldn't open $leasefile: $!\n";

while (<LEASES>){
 @in = split /\s+/, $_, 3;
 SWITCH: {
   if ($in[0] eq "lease"){ $ip = $in[1]; last SWITCH; }
   if ($in[1] eq "client-hostname"){
    $hostname{$ip} = substr($in[2],1,length($in[2])-4);
    last SWITCH;
   }
   if ($in[1] eq "ends"){
    $ends{$ip} = $in[2];
    last SWITCH;
   }
   if ($in[1] eq "hardware"){
    $mac{$ip} = substr($in[2],9,17);
    last SWITCH;
   }
 }
}
$now = time;
foreach $ip ( sort byip keys %mac ) {
 @exp = split(/[\/ :;]/,$ends{$ip});
 $exptime = timegm($exp[6],$exp[5],$exp[4],$exp[3],$exp[2]-1,$exp[1]);
 if ( $now > $exptime) {
  $status = "red";
 }
 else {
  $status = "green";
 }
 if ( not defined $hostname{$ip} ){
     $hostname{$ip} = " ";
 }
 printf(qq{<tr bgcolor=#f0f0f0><td><span style="color: %s">%s</span></td><td>%s</td><td>%s</td></tr>\n},$status,$ip,$mac{$ip},$hostname{$ip});
}
print "</table></body></html>\n";

exit;
sub byip {
 @a = split(/\./,$a);
 @b = split(/\./,$b);
 ($a[0] <=> $b[0]) or ($a[1] <=> $b[1]) or ($a[2] <=> $b[2]) or ($a[3] <=> $b[3]);
}

Philip Plane <philip@xinqu.net>