#!/usr/bin/perl
#
# Copyright (c) 2019 AT&T Intellectual Property.  All rights reserved.
# Copyright (c) 2015 by Brocade Communications Systems, Inc.
# All rights reserved.
#
# SPDX-License-Identifier: GPL-2.0-only
#

use strict;
use warnings;

use lib "/opt/vyatta/share/perl5/";
use Vyatta::Config;
use NetAddr::IP;

# The dhcpd daemon will exit in the event that no suitable
# addresses are configured for the dhcp subnets. This can
# occur during a transient configuration change on an
# interface. Start the dhcpd in the event that the dhcp server
# is configured and the dhcpd daemon is not currently running.

sub usage {
    print <<EOF;
Usage: <device> <inet|inet6> <address>
EOF
    exit 1;
}

my $op_mode_flag = 1;
my $listNodes    = ( $op_mode_flag ? "listOrigNodes" : "listNodes" );
my $exists       = ( $op_mode_flag ? "existsOrig" : "exists" );

my $dev;
my $proto;
my $addr;
my $vrf;
my $config_prefix;

sub start_dhcpd {
    my ( $vc, $dev, $addr ) = @_;
    my $found = 0;
    return unless ( -e "/opt/vyatta/etc/dhcpd/dhcpd_vrf_$vrf.conf" );

    if ( $vc->$exists("$config_prefix dhcp-server listento interface $dev") ) {
        system("/opt/vyatta/sbin/dhcpd.init restart $vrf opmode");
        return;
    }
    my $path = "$config_prefix dhcp-server shared-network-name";
    $vc->setLevel($path);
    my @subnets = $vc->$listNodes();
    foreach my $name (@subnets) {
        $vc->setLevel($path);
        my @subnets = $vc->$listNodes("$name subnet");
        foreach my $subnet (@subnets) {
            my $naipIP      = new NetAddr::IP($addr);
            my $naipNetwork = new NetAddr::IP($subnet);
            next unless ( $naipIP->within($naipNetwork) );
            $found = 1;
            last;
        }
        if ($found) {
            system("/opt/vyatta/sbin/dhcpd.init restart $vrf opmode");
            last;
        }
    }
}

sub start_dhcpdv6 {
    my ( $vc, $dev, $addr ) = @_;
    my $found = 0;
    return unless ( -e "/opt/vyatta/etc/dhcpdv6/dhcpdv6_vrf_$vrf.conf" );

    if ( $vc->$exists("$config_prefix dhcpv6-server listento interface $dev") ) {
        system("/opt/vyatta/sbin/dhcpdv6.init restart $vrf opmode");
        return;
    }
    my $path = "$config_prefix dhcpv6-server shared-network-name";
    $vc->setLevel($path);
    my @subnets = $vc->$listNodes();
    foreach my $name (@subnets) {
        $vc->setLevel($path);
        my @subnets = $vc->$listNodes("$name subnet");
        foreach my $subnet (@subnets) {
            my $naipIP      = new NetAddr::IP($addr);
            my $naipNetwork = new NetAddr::IP($subnet);
            next unless ( $naipIP->within($naipNetwork) );
            $found = 1;
            last;
        }
        if ($found) {
            system("/opt/vyatta/sbin/dhcpdv6.init restart $vrf opmode");
            last;
        }
    }
}

# find out which VRF the $dev belongs to
sub match_vrf {
    my $vrf_name = "default";
    my $config_path = "service";

    my $vcRTINST = new Vyatta::Config();
    my $path = "routing routing-instance";
    my $chvrf = "/usr/sbin/chvrf";

    foreach my $rtinst_name ( $vcRTINST->$listNodes($path) ) {
        if ( $vcRTINST->$exists("$path $rtinst_name interface $dev") ) {
            # When moving an interface from default VRF to non-default VRF,
            # netplug running in default VRF receives "deladdr" event, while
            # configd believes the interface belongs to the non-default VRF.
            # We make sure the "deladdr" event is really intended for the
            # non-default VRF by matching the interface's ip addresses with
            # ip address associated with "deladdr" events. If no match found,
            # the "deladdr" event is intended for the non-default VRF.
            if ( -x $chvrf ) {
                my $ip_active = `$chvrf $rtinst_name ip addr list $dev`;
                if ( ( $ip_active ) && !( $ip_active =~ m/$addr/ ) ) {
                    $vrf_name = $rtinst_name;
                    $config_path = "$path $rtinst_name service";
                    last;
                }
            }
        }
    }
    return ($vrf_name, $config_path);
}

$dev   = shift;
$proto = shift;
$addr  = shift;

usage() unless defined($dev);
usage() unless defined($proto);
usage() unless defined($addr);

($vrf, $config_prefix) = match_vrf();

my $vcDHCP = new Vyatta::Config();

if ( $proto eq 'inet' ) {
    if ( $vcDHCP->$exists("$config_prefix dhcp-server") ) {
        start_dhcpd( $vcDHCP, $dev, $addr );
    }
}

if ( $proto eq 'inet6' ) {
    if ( $vcDHCP->$exists("$config_prefix dhcpv6-server") ) {
        start_dhcpdv6( $vcDHCP, $dev, $addr );
    }
}

exit 0
