#!/bin/vbash

# dhclient-script for Linux. Dan Halbert, March, 1997.
# Updated for Linux 2.[12] by Brian J. Murrell, January 1999.
# Modified for Debian.  Matt Zimmerman and Eloy Paris, December 2003
# Modified to remove useless tests for antiquated kernel versions that
# this doesn't even work with anyway, and introduces a dependency on /usr
# being mounted, which causes cosmetic errors on hosts that NFS mount /usr
# Andrew Pollock, February 2005
# Modified to work on point-to-point links. Andrew Pollock, June 2005
# Modified to support passing the parameters called with to the hooks. Andrew Pollock, November 2005

# 'ip' just looks too weird.  /sbin/ip looks less weird.
ip=/sbin/ip


# modified make_resolv_conf () for Vyatta system below
make_resolv_conf() {
    local new_resolv_conf="/etc/resolv.conf.dhclient-new-$interface"
    local old_resolv_conf="/etc/resolv.conf.dhclient-old-$interface"
    mv -f $new_resolv_conf $old_resolv_conf
    if [ -n "$new_domain_name" -o -n "$new_domain_name_servers" ]; then
        if [ -n "$new_domain_name" ]; then
            echo search $new_domain_name >>$new_resolv_conf
        fi
        if [ -n "$new_domain_name_servers" ]; then
            for nameserver in $new_domain_name_servers; do
                echo nameserver $nameserver >>$new_resolv_conf
            done
        fi
    elif [ "x${new_dhcp6_name_servers}" != x ] ; then
	if [ "x${new_dhcp6_domain_search}" != x ] ; then
	    echo search ${new_dhcp6_domain_search} >> $new_resolv_conf
	fi
	for nameserver in ${new_dhcp6_name_servers} ; do
	    echo nameserver ${nameserver} >> $new_resolv_conf
	done
    else
        echo " " > $new_resolv_conf
    fi

    /opt/vyatta/sbin/vyatta_update_resolv.pl --dhclient-script 1
    if ! diff -q $old_resolv_conf $new_resolv_conf >&/dev/null; then
       if [ -d /opt/vyatta/config/active/service/dns/forwarding ]; then
          /opt/vyatta/sbin/vyatta-dns-forwarding.pl --update-dnsforwarding --outside-cli >&/dev/null
       fi
    fi
    rm -f $old_resolv_conf
}

run_hook() {
    local script="$1"
    local exit_status
    shift	# discard the first argument, then the rest are the script's

    if [ -f $script ]; then
        . $script "$@"
    fi


    if [ -n "$exit_status" ] && [ "$exit_status" -ne 0 ]; then
        logger -p daemon.err "$script returned non-zero exit status $exit_status"
        save_exit_status=$exit_status
    fi

    return $exit_status
}

run_hookdir() {
    local dir="$1"
    local exit_status
    shift	# See run_hook

    if [ -d "$dir" ]; then
        for script in $(run-parts --list $dir); do
            run_hook $script "$@" || true
            exit_status=$?
        done
    fi

    return $exit_status
}

# Must be used on exit.   Invokes the local dhcp client exit hooks, if any.
exit_with_hooks() {
    exit_status=$1

    # Source the documented exit-hook script, if it exists
    if ! run_hook /etc/dhcp3/dhclient-exit-hooks "$@"; then
        exit_status=$?
    fi

    # Now run scripts in the Debian-specific directory.
    if ! run_hookdir /etc/dhcp3/dhclient-exit-hooks.d "$@"; then
        exit_status=$?
    fi

    exit $exit_status
}

get_prefix() {
    # use existing perl module to compute prefix
    # one line don't bother with script
    perl -MNetAddr::IP \
	-e '$ip = new NetAddr::IP(@ARGV); print $ip->masklen(), "\n"' $*
}

set_hostname() {
    local current_hostname=$(hostname)
    if [ -z "$current_hostname" -o "$current_hostname" = "(none)" ]; then
        hostname "$new_host_name"
    fi
}

set_address() {
    if [ -n "$old_ip_address" ] &&
	[ "$old_ip_address" != "$new_ip_address" -o "$old_prefix" != "$new_prefix" ] ; then
	# Clear out route cache and ARP tables and all addresses and routes
	ip -family inet addr flush dev $interface
    fi

    if [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ] ||
       [ -z "$old_ip_address" -o "$old_ip_address" != "$new_ip_address" ] ||
       [ -z "$old_prefix" -o  "$old_prefix" != "$new_prefix" ] ; then
        ip -family inet addr add $new_ip_address/$new_prefix \
	    broadcast $new_broadcast_address dev $interface
    fi

    if [ -n "$new_interface_mtu" ] &&
       [ ! "$old_interface_mtu" = "$new_interface_mtu" ]; then
        ip link set $interface mtu $new_interface_mtu
    fi
}

# Administrative for DHCP routes. Should be configurable
ZEBRA_ROUTE_DHCP=210

update_routers() {
    if [ -n "$old_routers" ]; then
	# No change just renewing
	if [ "$reason" = "RENEW" -a "$old_routers" = "$new_routers" ]; then
	    return 0
	fi

	for router in $old_routers; do
	    if [ "$old_subnet_mask" == "255.255.255.255" ]; then
		vtysh -c "conf t" -c "no ip route $router $interface $ZEBRA_ROUTE_DHCP"
	    fi

	    vtysh -c "conf t" -c "no ip route 0.0.0.0/0 $router $ZEBRA_ROUTE_DHCP"
	done
    fi

    for router in $new_routers; do
	# point to point
	if [ "$new_subnet_mask" == "255.255.255.255" ]; then
	    vtysh -c "conf t" -c "ip route $router $interface $ZEBRA_ROUTE_DHCP"
	fi

	# Note: this should really be $router and $interface but zebra does
	# not have way to specify this (yet).
	vtysh -c "conf t" -c "ip route 0.0.0.0/0 $router $ZEBRA_ROUTE_DHCP"
    done
}

if [ -n "$new_subnet_mask" ]; then
    new_prefix=$(get_prefix $new_ip_address $new_subnet_mask)
fi
if [ -n "$old_subnet_mask" ]; then
    old_prefix=$(get_prefix $old_ip_address $old_subnet_mask)
fi
if [ -n "$new_interface_mtu" ]; then
    # Vyatta configuration overrides response from server to allow user
    # to work around broken ISP's
    mtu_path=$(/opt/vyatta/sbin/vyatta-interfaces.pl --dev=$interface --path)
    if [ -r $mtu_path ]; then
	read new_interface_mtu < $mtu_path
    fi

    # The 576 MTU is only used for X.25 and dialup connections
    # where the admin wants low latency.  Such a low MTU can cause
    # problems with UDP traffic, among other things.  As such,
    # disallow MTUs from 576 and below by default, so that broken
    # MTUs are ignored, but higher stuff is allowed (1492, 1500, etc).
    if [ $new_interface_mtu -le 576 ]; then
	new_interface_mtu=
    fi
fi

# The action starts here

# Invoke the local dhcp client enter hooks, if they exist.
run_hook /etc/dhcp3/dhclient-enter-hooks
run_hookdir /etc/dhcp3/dhclient-enter-hooks.d

# Execute the operation
case "$reason" in
    MEDIUM)
        # Do nothing
        ;;

    PREINIT)
        ip -family inet addr flush dev $interface
        ip link set $interface up

        if [ -n "$DHCLIENT_DELAY" ] && [ $DHCLIENT_DELAY -gt 0 ]; then
            sleep $DHCLIENT_DELAY
        fi
        ;;

    ARPCHECK|ARPSEND)
        if [ -z "$new_ip_address" ] || [ -z "$interface" ] ||
           arping -q -f -c 2 -w 3 -D -I $interface $new_ip_address; then
            exit_with_hooks 0
        else
            exit_with_hooks 1
        fi
        ;;

    BOUND|RENEW|REBIND|REBOOT)
        set_hostname

	make_resolv_conf
	set_address
	update_routers

        exit_with_hooks 0
        ;;

    EXPIRE|FAIL|RELEASE|STOP)
	new_routers=""; update_routers

        if [ -n "$old_ip_address" ]; then
            ip -family inet addr flush dev ${interface}
        fi
	if [ "$reason" = "STOP" ]; then
            ip link set ${interface} down
	fi

	make_resolv_conf
        ;;

    TIMEOUT)
        if [ -n "$new_routers" ]; then
            ip -family inet addr add $new_ip_address/$new_prefix \
		broadcast $new_broadcast_address dev $interface

            set -- $new_routers
            first_router="$1"

            if ping -q -c 1 -I $interface $first_router ; then
		make_resolv_conf
		set_address
		update_routers

		exit_with_hooks 0
	    fi
            ip -family inet addr flush dev $interface
	    # Note: this exits with interface still up
	    # see Debian bug #144666
        fi
        exit_with_hooks 2 "$@"
        ;;

    PREINIT6)
        # Ensure interface is up.
        ${ip} link set ${interface} up

	exit_with_hooks 0
	;;

    BOUND6)
        if [ x${new_ip6_address} != x ] && [ x${new_ip6_prefixlen} != x ] ; then
	    ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \
		dev ${interface} scope global
	fi

        # Check for nameserver options.
	make_resolv_conf

	exit_with_hooks 0
	;;

    RENEW6|REBIND6)
	# Make sure nothing has moved around on us.

	# Nameservers/domains/etc.
	if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
	    [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
	    make_resolv_conf
	fi

	exit_with_hooks 0
	;;

    DEPREF6)
        if [ x${new_ip6_prefixlen} = x ] ; then
	    exit_with_hooks 2;
	fi

	${ip} -f inet6 addr change ${new_ip6_address}/${new_ip6_prefixlen} \
	    dev ${interface} scope global preferred_lft 0

	exit_with_hooks 0
	;;
    
    EXPIRE6|RELEASE6|STOP6)
        if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
	    exit_with_hooks 2;
	fi

	${ip} -f inet6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \
            dev ${interface}

	make_resolv_conf
	exit_with_hooks 0
	;;

esac

exit_with_hooks 0
