#!/usr/bin/perl

# Module: dhcpv6-client-show-leases.pl
#
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# A copy of the GNU General Public License is available as
# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution
# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'.
# You can also obtain it by writing to the Free Software Foundation,
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# This code was originally developed by Vyatta, Inc.
# Portions created by Vyatta are Copyright (C) 2010 Vyatta, Inc.
# All Rights Reserved.
#
# Author: Bob Gilligan
# Date: April 2010
# Description: Script to display DHCPv6 client leases in a user-friendly form
#
# **** End License ****

use strict;
use lib "/opt/vyatta/share/perl5/";

use Getopt::Long;
use Vyatta::Config;


# Globals
my $debug_flag = 0;

GetOptions(
    "debug"     => \$debug_flag,
    );


sub log_msg {
    my $message = shift;

    print "DEBUG: $message" if $debug_flag;
}


#
# Main section.
#

opendir (my $dir, "/var/lib/dhcp3");
my @lease_files;
while (my $f = readdir $dir) {
    if ($f =~ /^dhclient_v6_(\w+).leases$/) {
	push (@lease_files, $f);
    }
}
closedir $dir;

if ($debug_flag) {
    print "lease files:", join(' ',@lease_files), "\n";
}

# Holds the most recent (last) entry for each interface
my %ghash = ();

foreach my $lease_filename (@lease_files) {
    my @lines=();

    if (!open(LEASE_FILE, "</var/lib/dhcp3/$lease_filename")) {
	printf("Can't open lease file for reading:  $lease_filename\n");
	exit 1;
    }

    @lines = <LEASE_FILE>;
    close(LEASE_FILE);
    chomp @lines;

    my $level = 0;
    my $s1;
    my $s2;
    my $ia_na;
    my $iaaddr;
    my $ends_day;
    my $ends_time;
    my $ifname;
    my $starts;
    my $pref_life;
    my $max_life;
    my $binding_state;

    # Parse the leases file into a hash keyed by IPv6 addr.
    foreach my $line (@lines) {
	log_msg("Line: $line\n");
	if ($line =~ /^lease6 \{/) {
	    if ($level != 0) {
		printf("Found lease6 at level $level\n");
		exit 1;
	    }
	    $level++;
	} elsif ($line =~ /^.*ia-na .*\{/) {
	    if ($level != 1) {
		printf("Found ia-na at level $level\n");
		exit 1;
	    }
	    log_msg("setting ia_na\n");
	    ($s1, $ia_na, $s2) = split(' ', $line);
	    $level++;
	} elsif ($line =~ /^.*interface /) {
	    if ($level != 1) {
		printf("Found interface at level $level\n");
		exit 1;
	    }
	    ($s1, $ifname) = split(' ', $line);
	    $ifname =~ s/;//;
	    $ifname =~ s/\"//g;
	    log_msg("Setting ifname to $ifname\n");
	} elsif ($line =~ /^.*iaaddr .*\{/) {
	    if ($level != 2) {
		printf("Found iaaddr at level $level\n");
		exit 1;
	    }
	    ($s1, $iaaddr, $s2) = split(' ', $line);
	    log_msg("Setting iaaddr to $iaaddr.\n");
	    log_msg("s1 $s1 s2 $s2\n");
	    $level++;
	} elsif ($line =~ /^.*starts /) {
	    ($s1, $starts) = split(' ', $line);
	    $starts =~ s/;//;
	} elsif ($line =~ /^.*preferred-life /) {
	    ($s1, $pref_life) = split(' ', $line);
	    $pref_life =~ s/;//;
	} elsif ($line =~ /^.*max-life /) {
	    ($s1, $max_life) = split(' ', $line);
	    $max_life =~ s/;//;
	} elsif ($line =~ /^.*ends /) {
	    if ($level != 2) {
		printf("Found ends at level $level\n");
		exit 1;
	    }
	    log_msg("Setting ends_day ends_time\n");
	    ($s1, $s2, $ends_day, $ends_time) = split(' ', $line);
	    $ends_time =~ s/;//;
	} elsif ($line =~ /^.*binding state /) {
	    if ($level != 2) {
		printf("Found binding state at level $level\n");
		exit 1;
	    }
	    log_msg("Setting binding state\n");
	    ($s1, $s2, $binding_state) = split(' ', $line);
	    $binding_state =~ s/;//;
	} elsif ($line =~ /^.*\{/) {
	    log_msg("Unknown clause: $line\n");
	    $level++;
	} elsif ($line =~ /\}$/) {
	    $level--;
	    if ($level == 0) {
		if (!defined($ia_na)) {
		    printf("ia_na not defined\n");
		    exit 1;
		}

		if (!defined($iaaddr)) {
		    printf("iaaddr not defined\n");
		    exit 1;
		}
	    }
	} else {
	    log_msg("Unknown parameter: $line\n");
	}
    }

    my @array = ($ia_na, $iaaddr, $starts, $max_life, $pref_life);
    $ghash{$ifname} = \@array;
}

# Display the leases...

my $num_entries = scalar(keys %ghash);
if ($num_entries == 0) {
    printf("There are no DHCPv6 leases.\n");
    exit 0;
} else {
    printf("DHCPv6 client leases:\n");
}

printf("\n");
printf("Interface IPv6 Address                            Expires\n");
printf("--------- --------------------------------------- ------------------------\n");
foreach my $key (keys %ghash) {
    my $entry = $ghash{$key};
    my ($ia_na, $iaaddr, $starts, $max_life, $pref_life) = @$entry;
    my $ts;
    if (defined ($starts) && defined ($max_life)) {
	my $exp_time = $starts + $max_life;
	$ts = localtime($exp_time);
    } else {
	$ts = "Unknown";
    }
    printf ("%-9s %-39s %s\n", $key, $iaaddr, $ts);
}