Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Not experiened with Ubuntu. Set up a dhcp server about a year ago but don't remember the command to see the active leases.

share|improve this question

Try checking the lease file /var/lib/dhcp/dhcpd.leases

share|improve this answer

Our organization uses a Python script, as posted below, to examine the /var/lib/dhcp/dhcpd.leases file:

import datetime, bisect

def parse_timestamp(raw_str):
        tokens = raw_str.split()

        if len(tokens) == 1:
                if tokens[0].lower() == 'never':
                        return 'never';

                        raise Exception('Parse error in timestamp')

        elif len(tokens) == 3:
                return datetime.datetime.strptime(' '.join(tokens[1:]),
                        '%Y/%m/%d %H:%M:%S')

                raise Exception('Parse error in timestamp')

def timestamp_is_ge(t1, t2):
        if t1 == 'never':
                return True

        elif t2 == 'never':
                return False

                return t1 >= t2

def timestamp_is_lt(t1, t2):
        if t1 == 'never':
                return False

        elif t2 == 'never':
                return t1 != 'never'

                return t1 < t2

def timestamp_is_between(t, tstart, tend):
        return timestamp_is_ge(t, tstart) and timestamp_is_lt(t, tend)

def parse_hardware(raw_str):
        tokens = raw_str.split()

        if len(tokens) == 2:
                return tokens[1]

                raise Exception('Parse error in hardware')

def strip_endquotes(raw_str):
        return raw_str.strip('"')

def identity(raw_str):
        return raw_str

def parse_binding_state(raw_str):
        tokens = raw_str.split()

        if len(tokens) == 2:
                return tokens[1]

                raise Exception('Parse error in binding state')

def parse_next_binding_state(raw_str):
        tokens = raw_str.split()

        if len(tokens) == 3:
                return tokens[2]

                raise Exception('Parse error in next binding state')

def parse_rewind_binding_state(raw_str):
        tokens = raw_str.split()

        if len(tokens) == 3:
                return tokens[2]

                raise Exception('Parse error in next binding state')

def parse_leases_file(leases_file):
        valid_keys = {
                'starts':               parse_timestamp,
                'ends':                 parse_timestamp,
                'tstp':                 parse_timestamp,
                'tsfp':                 parse_timestamp,
                'atsfp':                parse_timestamp,
                'cltt':                 parse_timestamp,
                'hardware':             parse_hardware,
                'binding':              parse_binding_state,
                'next':                 parse_next_binding_state,
                'rewind':               parse_rewind_binding_state,
                'uid':                  strip_endquotes,
                'client-hostname':      strip_endquotes,
                'option':               identity,
                'set':                  identity,
                'on':                   identity,
                'abandoned':            None,
                'bootp':                None,
                'reserved':             None,

        leases_db = {}

        lease_rec = {}
        in_lease = False
        in_failover = False

        for line in leases_file:
                if line.lstrip().startswith('#'):

                tokens = line.split()

                if len(tokens) == 0:

                key = tokens[0].lower()

                if key == 'lease':
                        if not in_lease:
                                ip_address = tokens[1]

                                lease_rec = {'ip_address' : ip_address}
                                in_lease = True

                                raise Exception('Parse error in leases file')

                elif key == 'failover':
                        in_failover = True
                elif key == '}':
                        if in_lease:
                                for k in valid_keys:
                                        if callable(valid_keys[k]):
                                                lease_rec[k] = lease_rec.get(k, '')
                                                lease_rec[k] = False

                                ip_address = lease_rec['ip_address']

                                if ip_address in leases_db:
                                        leases_db[ip_address].insert(0, lease_rec)

                                        leases_db[ip_address] = [lease_rec]

                                lease_rec = {}
                                in_lease = False

                        elif in_failover:
                                in_failover = False
                                raise Exception('Parse error in leases file')

                elif key in valid_keys:
                        if in_lease:
                                value = line[(line.index(key) + len(key)):]
                                value = value.strip().rstrip(';').rstrip()

                                if callable(valid_keys[key]):
                                        lease_rec[key] = valid_keys[key](value)
                                        lease_rec[key] = True

                                raise Exception('Parse error in leases file')

                        if in_lease:
                                raise Exception('Parse error in leases file')

        if in_lease:
                raise Exception('Parse error in leases file')

        return leases_db

def round_timedelta(tdelta):
        return datetime.timedelta(tdelta.days,
                tdelta.seconds + (0 if tdelta.microseconds < 500000 else 1))

def timestamp_now():
        n = datetime.datetime.utcnow()
        return datetime.datetime(n.year, n.month,, n.hour, n.minute,
                n.second + (0 if n.microsecond < 500000 else 1))

def lease_is_active(lease_rec, as_of_ts):
        return timestamp_is_between(as_of_ts, lease_rec['starts'],

def ipv4_to_int(ipv4_addr):
        parts = ipv4_addr.split('.')
        return (int(parts[0]) << 24) + (int(parts[1]) << 16) + \
                (int(parts[2]) << 8) + int(parts[3])

def select_active_leases(leases_db, as_of_ts):
        retarray = []
        sortedarray = []

        for ip_address in leases_db:
                lease_rec = leases_db[ip_address][0]

                if lease_is_active(lease_rec, as_of_ts):
                        ip_as_int = ipv4_to_int(ip_address)
                        insertpos = bisect.bisect(sortedarray, ip_as_int)
                        sortedarray.insert(insertpos, ip_as_int)
                        retarray.insert(insertpos, lease_rec)

        return retarray


myfile = open('/var/lib/dhcp/dhcpd.leases', 'r')
leases = parse_leases_file(myfile)

now = timestamp_now()
report_dataset = select_active_leases(leases, now)

print('| IP Address      | MAC Address       | Expires (days,H:M:S) | Client Hostname ')

for lease in report_dataset:
        print('| ' + format(lease['ip_address'], '<15') + ' | ' + \
                format(lease['hardware'], '<17') + ' | ' + \
                format(str((lease['ends'] - now) if lease['ends'] != 'never' else 'never'), '>20') + ' | ' + \

print('| Total Active Leases: ' + str(len(report_dataset)))
print('| Report generated (UTC): ' + str(now))
share|improve this answer
I like this, as it filters out previously expired leases (unlike viewing /var/lib/dhcp/dhcpd.leases). Thanks for sharing. – Wireblue Jul 13 '15 at 1:05
No problem, tell your friends – Canadian Luke Jul 13 '15 at 1:06
Could you make a github repository for that script? Here, it returns Parse error in leases file. (Ubuntu 12.04.3 LTS) – koppor Sep 12 '15 at 21:32
@koppor can you post to pastebin your /var/lib/dhcp/dhcpd.leases file? – Canadian Luke Sep 12 '15 at 21:34
@CanadianLuke Sure: – koppor Sep 12 '15 at 22:05

I use this script:


my $VERSION=0.03;

my $leases_file = "/var/lib/dhcp/dhcpd.leases";

use strict;
use Date::Parse;

my $now = time;
my %seen;       # leases file has dupes (because logging failover stuff?). This hash will get rid of them.

open(L, $leases_file) or die "Cant open $leases_file : $!\n";
undef $/;
my @records = split /^lease\s+([\d\.]+)\s*\{/m, <L>;
shift @records; # remove stuff before first "lease" block

## process 2 array elements at a time: ip and data
foreach my $i (0 .. $#records) {
    next if $i % 2;
    my ($ip, $_) = @records[$i, $i+1];

    s/^\n+//;     # && warn "leading spaces removed\n";
    s/[\s\}]+$//; # && warn "trailing junk removed\n";

    my ($s) = /^\s* starts \s+ \d+ \s+ (.*?);/xm;
    my ($e) = /^\s* ends   \s+ \d+ \s+ (.*?);/xm;

    my $start = str2time($s);
    my $end   = str2time($e);

    my %h; # to hold values we want

    foreach my $rx ('binding', 'hardware', 'client-hostname') {
        my ($val) = /^\s*$rx.*?(\S+);/sm;
        $h{$rx} = $val;

    my $formatted_output;

    if ($end && $end < $now) {
        $formatted_output =
            sprintf "%-15s : %-26s "              . "%19s "         . "%9s "     . "%24s    "              . "%24s\n",
                    $ip,     $h{'client-hostname'}, ""              , $h{binding}, "expired"               , scalar(localtime $end);
    else {
        $formatted_output =
            sprintf "%-15s : %-26s "              . "%19s "         . "%9s "     . "%24s -- "              . "%24s\n",
                    $ip,     $h{'client-hostname'}, "($h{hardware})", $h{binding}, scalar(localtime $start), scalar(localtime $end);

    next if $seen{$formatted_output};
    print $formatted_output;

You may want to adapt it to suit your needs.

There are also Perl modules which you may want to try if you have a vague notion of Perl: Net::ISC::DHCPd::Leases, POE::Filter::DHCPd::Lease or Text::DHCPLeases.

The last one can be installed with

sudo apt-get install libtext-dhcpleases-perl

The others with cpan -i.

Unfortunately, I haven't tried any of them, because I already had my script when I noticed them.

share|improve this answer

Here's a great command using CLI - You can go to the directory where dhcpd.log file is located and do:

tail -f dhcpd.log

That will show you leases as they are being issued by the server in real time.

You can also do:

cat /var/lib/dhcpd/dhcpd.leases to see leases that are in the lease file dhcpd.leases

share|improve this answer

If you are using NetworkManager (which is default in many distributions) the .lease files is located in /var/lib/NetworkManager

$ sudo ls -al /var/lib/NetworkManager/*.lease
-rw-r--r-- 1 root root 399 Jun 12 10:23 /var/lib/NetworkManager/
-rw-r--r-- 1 root root 856 Jun 12 10:30 /var/lib/NetworkManager/
-rw-r--r-- 1 root root 800 Jun 12 10:30 /var/lib/NetworkManager/
share|improve this answer

protected by Eric Carvalho Nov 1 '15 at 10:00

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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