6

I have Ubuntu server 12.10 as DRBL and CloneZilla. How can I find out how many IP addresses have been assigned to the clients. How do I control/monitor the DHCP daemon service(stop/start/status)?

5 Answers 5

4

To monitor Dhcp leases just type in terminal :

gedit /var/lib/dhcp/dhcpd.leases 
3
  • 6
    Is gedit really the best way to peek into that file? First, it requires a desktop environment, which is usually not present on a server installation. Second: launching an editor suggests this file could be modified, but I'm pretty sure that editing it can cause nothing but trouble. Dec 10, 2014 at 18:50
  • 1
    This is sometimes on /var/lib/NetworkManager/*.lease Jul 1, 2016 at 13:27
  • 1
    Why Gedit? Just use cat /var/lib/dhcp/dhcpd.leases
    – wjandrea
    Jun 11, 2017 at 21:05
4

All the answers above are partial. And to be honest there is no simple solution. 1) You can parse the dhcpd.leases database file and get information on active leases, but

  • you will not get information on any FIXED addresses (assigned by a line like:

    host switch1      { hardware ethernet a1:b2:c3:d7:2f:bc ; fixed-address switch1.mydomain.com; }
    
  • and this also is not really giving any information on when was the last time a dhcp ack was sent to the machine.

2) on the other hand you can parse the dhcpd.log file to search for ack lines (they look like this):

2017-03-12T08:44:52.421174+01:00, Linuxx, info, dhcpd: DHCPACK on 10.0.0.63 to 68:ab:35:59:9c:a1 via 10.0.0.1

Which is giving you information on DHCPD requests and replies but there is no information about actual leases (time, status).

What you should really do is to do BOTH. First parse the log file, and then update the file with information obtained from dhcpd.leases file with database for missing information like lease start-end etc.

Now: I have played circa 2 full workdays till I have created a solution which creates a HTML table with ALL active leases, both FIXED and dynamic. Here is the code that you can place in your cgi-bin folder or wherever.

#!/usr/bin/perl
#####################################################################################
# list dhcpd active leases 
#   - both "fixed" addresses which are normally not placed into leases database
#   - and dynamically given leases which are present in leases DB
# working for isc-dhcpd-server service but should also work for other compatible
# dhcpd servers. 
# produces HTML or CSV list of leases
# in shell can pipe to lynx:
#   ./dhcp-leases.pl | lynx -stdin
#
# written by Marcin Gosiewski, BV Grupa s.c. Poland <marcin.gosiewski@bvsystemy.pl> http://www.bvsystemy.pl/ 
# based on portions of code by Jason Antman <jason@jasonantman.com> 
#
# to make it work change the $logfilename and $leasedbname below and modify
# the regexp in second part of code (see below) to match your log lines format
# also you can optionally turn off reverse dns lookup (see below) which speeds up the process 
# of table creation and is useless unless you have reverse dns populated for 
# your fixed or dynamic leases
#
# CHANGELOG:
#     2017-03-13: initial version
#     2019-08-15: extended for @logprog by Jim Klimov
use Socket;
use strict;
use warnings;
no warnings 'uninitialized';

# adjust this to match your files location: both log file and leases
# database. We use 2 last log files from logrotate, but you can add as many as you want
my @logfilenames = ( "/var/log/LOCALAPP.dhcpd.log.1", "/var/log/LOCALAPP.dhcpd.log" );
# Alternately, on systems without explicit log (e.g. with systemd journals), use empty array of files:
### my @logfilenames = ( ); # if empty, use output from logprog below
my @logprog = qw ( sudo journalctl --no-pager -lu dhcpd );
# Delegate rights for logprog as root, e.g.
#   echo 'www-data  ALL=(root)  NOPASSWD:/usr/bin/journalctl --no-pager -lu dhcpd' > /etc/sudoers.d/www-journalctl
my $leasedbname = "/var/lib/dhcp/dhcpd.leases";
my %data = ();
# optional, can be modified to produce local time
use Time::Local;
use POSIX 'strftime';
my $now = time();
# local variables, lease information stored here
my $ip=""; 
my $status=""; 
my $interface=""; 
my $sdate="";         # beginning of lease
my $stime=""; 
my $edate="";         # end of lease
my $etime=""; 
my $adate="";         # last update (ACK) sent to requesting server
my $atime="";
my $mac=""; 
my $hostname="";
my $dnsname="";       # reverse dns lookup for host

#######################################################################
# first gather data from logfile for all ACK actions
#######################################################################

# collect all lines from log files into memory...
my @lines = (); my @loglines=(); 
if (scalar @logfilenames > 0) {
 foreach my $logfilename (@logfilenames)
 {
  open LOGFILE, '<', $logfilename;
  chomp(@loglines = <LOGFILE>);
  #printf "LINES1: " . scalar @loglines . " in " .$logfilename . "\n";
  push(@lines, @loglines);
  close(LOGFILE);
 }
} else {
  open LOGPROG, '-|', join (' ', @logprog) or die "Could not pipe from logprog";
  chomp(@loglines = <LOGPROG>);
  #printf "LINES1: " . scalar @loglines . " in " .$logfilename . "\n";
  push(@lines, @loglines);
  close(LOGPROG);
}
@loglines=();
#printf "TOTAL LINES: " . scalar @lines . "\n";
foreach my $line (@lines)
{
  if ( $line !~ m/dhcpd[^:]*: DHCPACK/) { next;}
  #printf "LINE: $line\n";

  ###############################
  # Modify the following line to make regexp capture 6 groups from log line:
  # 1 - date
  # 2 - time
  # 3 - ip 
  # 4 - mac
  # 5 - hostname if available
  # 6 - interface
  #$line =~ m/(^.{10})T(.{8}).+,\ dhcpd: DHCPACK on (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) to ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}.*) via (.+)/;
  $line =~ m/(^.{10})T(.{8}).+,\ dhcpd: DHCPACK on (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) to ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}) (.*)via (.+)/;
  #$line =~ m/^(.{6}) (.{8})\ .+,?\ dhcpd[^:]*: DHCPACK on (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) to ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}) (.*)via (.+)/;
  # process the input
  $adate="$1";
  $atime="$2";
  $ip="$3";
  $mac="$4";
  $hostname="$5";
  $interface="$6";
  #add some 'known' facts:
  $status="ACK";
  $sdate="";    #"FOREVER";
  $stime="";
  $edate="";
  $etime="";

  #create/update record for this mac_addr
  #you can add extra check here if the IP address is not duplicated within
  #ack history and choose only the newer one. 

  $data{"$mac"}->{'ip'} = "$ip";
  $data{"$mac"}->{'status'} = "$status";
  $data{"$mac"}->{'interface'} = "$interface";
  $data{"$mac"}->{'adate'} = "$adate";
  $data{"$mac"}->{'atime'} = "$atime";
  $data{"$mac"}->{'sdate'} = "$sdate";
  $data{"$mac"}->{'stime'} = "$stime";
  $data{"$mac"}->{'edate'} = "$edate";
  $data{"$mac"}->{'etime'} = "$etime";
  $data{"$mac"}->{'mac'} = "$mac";
  if (length($hostname) > 0) {
    $hostname =~ s/^\ *\(*//;
    $hostname =~ s/\)*\ *$//;
  }
  $data{"$mac"}->{'hostname'} = "$hostname";
}
#close(LOGFILE);

#######################################################################
# gather data from lease database for dynamic addresses
# update the records (for existing) or add new records
#######################################################################

my $isdata = 0;
my $type = "";

#this information is not present in leases database so we just set
#it to default values
$interface="dhcpd";
$status="ACTIVE";
$adate="-";
$atime="";

open LEASEDB, $leasedbname or die $!;
foreach my $line (<LEASEDB>) 
{
  chomp($line);
  $isdata = 1 if $line =~ /^lease /;
  $isdata = 0 if $line =~ /^}/;

  if ($isdata) 
  {
    if ($line =~ /^lease/) 
    {
      $ip = (split(" ", $line))[1];
    } 
    elsif ($line =~ /^  starts/) 
    {
      ($sdate, $stime) = (split(" ", $line))[2,3];
      $sdate =~ s/\//-/g;
      $stime =~ s/;//;
    } 
    elsif ($line =~ /^  ends/) 
    {
      ($type, $edate, $etime) = (split(" ", $line))[1,2,3];
      if($type eq "never;")
      {
        $edate="forever";
        $etime=" ";
      }
      else
      {
        $edate =~ s/\//-/g;
        $etime =~ s/;//;
      }
    } 
    elsif ($line =~ /^  hardware ethernet/) 
    {
            $mac = (split(" ", $line))[2];
            $mac =~ s/;//;
    } 
    elsif ($line =~ /^  client-hostname/) 
    {
            $hostname = (split(/\"/, $line))[1];
    }
    elsif($mac ne "") 
    {
        #we have parsed the whole record, no more matching entries
        #data is collected to variables. now push the record.

        #now let's decide if we are updating the record or creating
        #new record

        # check against lease date, do not add expired leases
        # convert lease end time to local time/date and compare with $now
        my $y=0; my $m=0; my $d=0; my $H=0; my $M=0; my $S=0;
        my $edatetime = $now;
        ($y, $m, $d) = split("-", $edate);
        ($H, $M, $S) = split(":", $etime);
        $edatetime = timelocal($S,$M,$H,$d,$m-1,$y);
        if($edatetime >= $now)
        {
          # now check if record exists
          if(!defined($data{"$mac"}->{'mac'}))
          {
            #record does not exist, fill up default data
            $data{"$mac"}->{'mac'} = "$mac";
            $data{"$mac"}->{'interface'} = "$interface";
            $data{"$mac"}->{'ip'} = "$ip";
            $data{"$mac"}->{'hostname'} = "$hostname";
          }
          # record exists, let's check if we should update
          $data{"$mac"}->{'status'} = "$status";
          $data{"$mac"}->{'sdate'} = "$sdate";
          $data{"$mac"}->{'stime'} = "$stime";
          $data{"$mac"}->{'edate'} = "$edate";
          $data{"$mac"}->{'etime'} = "$etime";
          $data{"$mac"}->{'hostname'} = "$hostname";
          #we do NOT update ACK time because we do not have it
          #do NOT uncomment below
          #$data{"$mac"}->{'adate'} = "$adate";
          #$data{"$mac"}->{'atime'} = "$atime";

        }
    }
  }
}
close(LEASEDB);

#######################################################################
# sort data
#######################################################################

#we sort by IP but you can sort by anything.
my @sorted = sort { ($data{$a}{'ip'}) cmp ($data{$b}{'ip'}) } %data;

#######################################################################
# Print out everything to the HTML table
#######################################################################

my $hostnamelong="";

printf "Content-type: text/html\n\n";
printf "<html><head><title>Aktywne dzierzawy DHCP</title></head>\n";
printf "<style> table, th, td { border: 1px solid lightgray; border-collapse: collapse; padding: 3px; } ";
printf "tr:nth-child(even) { background-color: #dddddd; } ";
printf "</style>\n";
printf "<body>\n";
printf "<table border='1' cellpadding='6'>\n";
printf "<tr><th>IP</th><th>Status</th><th>Interface</th><th>Lease time</th><th>ACK time</th><th>Mac</th><th>Host</th></tr>\n";
foreach my $key (@sorted) {
    if($data{$key}{'mac'} eq "") { next ; }

    # BEGIN reverse dns lookup
    # can optionally turn off reverse dns lookup (comment out below lines) which speeds up the process 
    # of table creation and is useless unless you have reverse dns populated for 
    # your fixed or dynamic leases uncomment single line below instead:
    #
    # version without reverse dns lookup:
    # $hostnamelong = $data{$key}{'hostname'};
    #
    # version with reverse dns lookup: 
    # BEGIN
    $dnsname = gethostbyaddr(inet_aton($data{$key}{'ip'}), AF_INET);
    if($data{$key}{'hostname'} ne "")
    {
      $hostnamelong = $data{$key}{'hostname'} . " | " . $dnsname;
    }
    else
    {
      $hostnamelong = $dnsname;
    }
    $dnsname = "";
    # END

    printf "<tr>";
    printf "<td>" . $data{$key}{'ip'} ."</td>";
    printf "<td>" . $data{$key}{'status'} ."</td>";
    printf "<td>" . $data{$key}{'interface'} ."</td>";
    printf "<td>" . $data{$key}{'sdate'} . " " . $data{$key}{'stime'} ." - ";
    printf $data{$key}{'edate'} . " " . $data{$key}{'etime'} ."</td>";
    printf "<td>" . $data{$key}{'adate'} . " " . $data{$key}{'atime'} . "</td>";
    printf "<td>" . $data{$key}{'mac'} ."</td>";
    printf "<td>" . $hostnamelong ."</td>";
    printf "</tr>\n";
}

printf "</table>\n";
printf "</body></html>\n";

# END of programm

Please note, that:

1) the above script needs slight modification before running in YOUR environment, you have to modify the files locations, and one regex depending on your log file format. See comment in script.

2) the above script is not checking whether the IP is not repeated in ACK table, if 2 different machines got the same address within last days. This is by design (what I personally needed to see each mac address which was present in my network during last days) - you can easily modify it, there is a ready section for this in code, just add one condition.

Hope you like it.

4
  • I would like to use this script. However, I don't have a log file as the dhcpd.leases file came from another computer. Can I still use it? I tried using an empty array for the log file but running it just returned an empty table with headers.
    – frakman1
    Oct 27, 2019 at 4:47
  • to frakman1: without log file the script is rather useless. you will not have any usefull information without it. Oct 28, 2019 at 12:13
  • What are you calling "useful information"? What is the log file being used for? I don't need an ACK since the dhcpd.leases file tells me about every device that was granted a lease. I can just assume that they got it. Also, dhcpd.leases isn't just for active leases. It's for all leases, past and present. Most DHCP monitoring scripts only use dhcpd.leases and dhcpd.conf for their analysis.
    – frakman1
    Oct 28, 2019 at 16:40
  • to frakman1: please read the above post carefully. If you do not need the information in the log files: feel free to ignore it. I need information from both the leases file and the log as described at the top of the post above. over and out. Oct 29, 2019 at 19:37
1

The script used to control the DHCP daemon depends on which one you're using. I'm assuming isc-dhcp-server. Then it would be:

service isc-dhcp-server stop
service isc-dhcp-server start
service isc-dhcp-server status

If that doesn't work, look in /etc/init for a config file named after dhcp, and use that. Say your config file is called dhcpd3.conf, then you'd replace isc-dhcp-server in the above commands with dhcpd3.

To see "how many ip addresses have been assigned to clients", which was your original request, you can do this:

grep "^lease" /var/lib/isc-dhcp-server/dhcpd.leases |sort |uniq |wc -l

This will plainly give you the number of assigned addresses.

Again, if you get an error about /var/lib/isc-dhcp-server not being findable, look in /var/lib and replace it with something suggestind a dhcp server, usually dhcp or dhcp3

Note that as mentioned in another answer, this will miss the distinction between assigned and active (i.e. with a system using them at the moment) addresses. Also, if a client is forcefully shutdown and doesn't release the lease, you will also get unused addresses reported as assigned.

1
  • Found this to be helpful, new to running my own DHCP Server. I also found dhcp-lease-list --lease /var/lib/dhcp/dhcpd.leases to be helpful. Running hostapd and isc-dhcp-server on ubuntu, Pine64 Board.
    – Twisty
    Dec 23, 2016 at 19:04
0

If you are using isc-dhcp-server on UBUNTU 14.04 headless server

To control the isc-dhcp-server

service isc-dhcp-server stop
service isc-dhcp-server start
service isc-dhcp-server status

To monitor the isc-dhcp-server

This method will work on a headless server (non-GUI) for "assigned DHCP leases only", login, enter the following command from the terminal

cat /var/lib/dhcp/dhcpd.leases

If NMAP has been installed enter this command from the terminal

nmap -v -sP 192.168.0.0/24

This method will work on this subnet and scan 256 addressees, and output IP and MAC addresses. This will respond if the HOST is down with an IP Address that has no assignment and will respond with MAC and IP Address if the HOST is up.

Depending on if a switch or router is between the PC and the network, and depending what "network layer 2/3/4" router or switch operates at.

tail -f /var/log/syslog will output DHCP addresses being actively assigned.

tcpdump -i eth0 port 67 or port 68 -e -n will capture DHCP packets on port 67 and port 68 on network interface eth0

If DHCPDUMP has been installed enter this command from the terminal

dhcpdump -i eth0 will DUMP "as it implies" DHCP information from the server to the client.

0

To list all the active Clients IP's you can use nmap. It can list both DHCP as well as static clients.

you can install nmap in your PC with

sudo apt-get install nmap

Then to list all the active clients you can do like ,

 nmap -v -sP 192.168.0.0/255

here the IP's from 0 to 255 will be scanned and active IP's will be showup.

The better network monitor I can found my self is nethogs. you can get it by installing with

sudo apt-get install nethogs

Then simply

sudo nethogs <connection_name>

Can show the traffic monitor in your terminal.

hope this can help you.

1
  • 2
    Big difference between assigned and active though, this will miss a lot of the inactive ones
    – Meer Borg
    Mar 8, 2013 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.