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

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

4 Answers 4

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

share|improve this answer

Our organization uses a Python script, as posted below:

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

        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 == '}':
                        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

                                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 at 1:05
No problem, tell your friends – Canadian Luke Jul 13 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 at 21:32
@koppor can you post to pastebin your /var/lib/dhcp/dhcpd.leases file? – Canadian Luke Sep 12 at 21:34
@CanadianLuke Sure: – koppor Sep 12 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

protected by Eric Carvalho Nov 1 at 10:00

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

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.