#!/usr/bin/perl
# Module: dhcpv6-client-show-leases.pl
#
# Copyright (c) 2019 AT&T Intellectual Property.  All rights reserved.
# Copyright (c) 2014 by Brocade Communications Systems, Inc.
# All rights reserved.
# Copyright (c) 2010-2013, Vyatta, Inc.
# All rights reserved.
#
# SPDX-License-Identifier: GPL-2.0-only
#

use strict;
use warnings;

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

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


# Globals
my $debug_flag = 0;
my $lease_dir = '/var/lib/dhcp';

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


sub log_msg {
    my $message = shift;

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


#
# Main section.
#

opendir (my $dir, $lease_dir);
my @lease_files;
while (my $f = readdir $dir) {
    if ($f =~ /^dhclient_v6_(\w+)(\.\d+)?\.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=();

    open(my $f, '<', "$lease_dir/$lease_filename")
	or die "Can't open lease file for reading:  $lease_filename\n";

    @lines = <$f>;
    close $f;
    chomp @lines;

    my $level = 0;
    my $s1;
    my $s2;
    my $ia_na;
    my $ia_ta;
    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");
		next;
	    }
	    $level++;
	} elsif ($line =~ /^.*ia-na .*\{/) {
	    if ($level != 1) {
		printf("Found ia-na at level $level\n");
		next;
	    }
	    log_msg("setting ia_na\n");
	    ($s1, $ia_na, $s2) = split(' ', $line);
	    $level++;
	} elsif ($line =~ /^.*ia-ta .*\{/) {
	    if ($level != 1) {
		printf("Found ia-ta at level $level\n");
		next;
	    }
	    log_msg("setting ia_ta\n");
	    ($s1, $ia_ta, $s2) = split(' ', $line);
	    $level++;
	} elsif ($line =~ /^.*interface /) {
	    if ($level != 1) {
		printf("Found interface at level $level\n");
		next;
	    }
	    ($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");
		next;
	    }
	    ($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");
		next;
	    }
	    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");
		next;
	    }
	    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) && !defined($ia_ta)) {
		    printf("ia_na and ia_ta not defined for $ifname \n");
		    next;
		}

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

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

# 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 (sort(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;
	my $epoch_time = time;
	# Don't display expired leases
	next if ($epoch_time > $exp_time);
	$ts = localtime($exp_time);
    } else {
	$ts = "Unknown";
    }
    printf ("%-9s %-39s %s\n", $key, $iaaddr, $ts);
}



