#!/bin/sh
# This script expect a system with resolvconf (openresolv) and iproute2

[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1

ACTION="$1"
IP_CACHE="/var/lib/misc/${interface}.cache"
RESOLV_CONF="/run/resolvconf/interfaces/${interface}.conf"
NTPFILE="/run/chrony/dhcp-sources.d/${interface}.sources"
NAME="/etc/net.d/${interface}-dhcp.conf"
NEXT="${NAME}+"

[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
[ -n "$subnet" ] || subnet=32
[ -n "$metric" ] || metric=5

# Handle stateful DHCPv6 like DHCPv4
[ -n "$ipv6" ] && ip="$ipv6/128"

if [ -z "${IF_WAIT_DELAY}" ]; then
	IF_WAIT_DELAY=10
fi

log()
{
    logger -I $$ -t udhcpc -p user.notice "${interface}: $*"
}

dbg()
{
    logger -I $$ -t udhcpc -p user.debug "${interface}: $*"
}

err()
{
    logger -I $$ -t udhcpc -p user.err "${interface}: $*"
}

wait_for_ipv6_default_route()
{
    dbg "waiting for IPv6 default route to be installed."
    while [ $IF_WAIT_DELAY -gt 0 ]; do
	if ip -6 route list proto dhcp dev $interface | grep -q default; then
	    return
	fi
	sleep 1
	printf "."
	: $((IF_WAIT_DELAY -= 1))
    done
    err "Timed out waiting for IPv6 default route!"
}

# Check if a DHCP option was requested in the parameter request list
# Returns: 0 if requested, 1 if not requested or config unavailable
was_option_requested()
{
    local opt_num="$1"
    local config="/etc/finit.d/available/dhcp-client-${interface}.conf"

    if [ ! -f "$config" ]; then
	dbg "config file not found: $config"
	return 1
    fi

    # Extract udhcpc command line and check for -O <option_num>
    if grep -q -- "-O ${opt_num}\b" "$config"; then
	return 0
    fi

    return 1
}

# RFC3442: If the DHCP server returns both a Classless
# Static Routes option and a Router option, the DHCP
# client MUST ignore the Router option.
set_dhcp_routes()
{
    echo "# Generated by udhcpc" > "$NEXT"
    echo "" >> "$NEXT"
    if [ -n "$staticroutes" ]; then
	if was_option_requested 121; then
	    # format: dest1/mask gw1 ... destn/mask gwn
	    set -- $staticroutes
	    while [ -n "$1" -a -n "$2" ]; do
		dbg "adding route $1 via $2 metric $metric tag 100"
		cat >> "$NEXT" <<-EOF
		route {
		    prefix = "$1"
		    nexthop = "$2"
		    distance = $metric
		    tag = 100
		}

		EOF
		shift 2
	    done
	else
	    log "ignoring unrequested staticroutes (option 121)"
	fi
    elif [ -n "$router" ] ; then
	if was_option_requested 3; then
	    for i in $router ; do
		cat >> "$NEXT" <<-EOF
		route {
		    prefix = "0.0.0.0/0"
		    nexthop = "$i"
		    distance = $metric
		    tag = 100
		}

		EOF
	    done
	else
	    log "ignoring unrequested router (option 3)"
	fi
    fi

    # Reduce changes needed by comparing with previous route(s)
    cmp -s "$NAME" "$NEXT" && return
    mv "$NEXT" "$NAME"
}

clr_dhcp_routes()
{
    log "deleting DHCP routes"
    [ -f "$NAME" ] || return
    rm "$NAME"
}

clr_dhcp_addresses()
{
    addrs=$(ip -j addr show dev $interface \
		| jq -c '.[0].addr_info[] | select(.family == "inet") | select(.protocol == "dhcp")')

    for addr in $addrs; do
	ip="$(echo "$addr" | jq -r '."local"')"
	prefix="$(echo "$addr" | jq -r '."prefixlen"')"
	log "removing $ip/$prefix"
	ip addr del "$ip/$prefix" dev "$interface"
    done
}

log "action $ACTION"
case "$ACTION" in
    deconfig)
	clr_dhcp_addresses
	clr_dhcp_routes

	# drop info from this interface
	rm -f "$RESOLV_CONF"
	rm -f "$NTPFILE"
	if [ -f "/etc/hostname.d/90-dhcp-${interface}" ]; then
	    log "removing /etc/hostname.d/90-dhcp-${interface}"
	     rm -f "/etc/hostname.d/90-dhcp-${interface}"
	     /usr/libexec/infix/hostname dhcp
	fi
	if [ -x /usr/sbin/avahi-autoipd ]; then
	    /usr/sbin/avahi-autoipd -c $interface && /usr/sbin/avahi-autoipd -k $interface
	fi
	;;

    leasefail|nak)
	if [ -x /usr/sbin/avahi-autoipd ]; then
	    /usr/sbin/avahi-autoipd -c $interface || /usr/sbin/avahi-autoipd -wD $interface --no-chroot
	fi
	;;

    renew|bound)
	if [ -x /usr/sbin/avahi-autoipd ]; then
	    /usr/sbin/avahi-autoipd -c $interface && /usr/sbin/avahi-autoipd -k $interface
	fi

	if /bin/ip addr add dev $interface $ip/$subnet $BROADCAST proto dhcp; then
	    echo "$ip" > "$IP_CACHE"
	fi
	if [ -n "$ipv6" ] ; then
	    wait_for_ipv6_default_route
	fi

	set_dhcp_routes

	# set hostname if given and requested
	if [ -n "$hostname" ]; then
	    if was_option_requested 12; then
		log "received DHCP hostname: $hostname"
		mkdir -p /etc/hostname.d
		echo "$hostname" > "/etc/hostname.d/90-dhcp-${interface}"
		/usr/libexec/infix/hostname dhcp
	    else
		log "ignoring unrequested hostname (option 12): $hostname"
	    fi
	fi

	# drop info from this interface
	truncate -s 0 "$RESOLV_CONF"

	# prefer rfc3397 domain search list (option 119) if available
	search_list=""
	if [ -n "$search" ]; then
	    if was_option_requested 119; then
		search_list=$search
	    else
		log "ignoring unrequested search (option 119): $search"
	    fi
	elif [ -n "$domain" ]; then
	    if was_option_requested 15; then
		search_list=$domain
	    else
		log "ignoring unrequested domain (option 15): $domain"
	    fi
	fi

	if [ -n "$search_list" ]; then
	    dbg "adding search $search_list"
	    echo "search $search_list # $interface" >> $RESOLV_CONF
	fi

	if [ -n "$dns" ]; then
	    if was_option_requested 6; then
		for i in $dns ; do
		    dbg "adding dns $i"
		    echo "nameserver $i # $interface" >> $RESOLV_CONF
		    resolvconf -u
		done
	    else
		log "ignoring unrequested dns (option 6): $dns"
	    fi
	fi

	if [ -n "$ntpsrv" ]; then
	    if was_option_requested 42; then
		truncate -s 0 "$NTPFILE"
		for srv in $ntpsrv; do
		    dbg "got NTP server $srv"
		    echo "server $srv iburst" >> "$NTPFILE"
		done
		chronyc reload sources >/dev/null
	    else
		log "ignoring unrequested ntpsrv (option 42): $ntpsrv"
	    fi
	fi
esac

HOOK_DIR="$0.d"
for hook in "${HOOK_DIR}/"*; do
    [ -f "${hook}" -a -x "${hook}" ] || continue
    "${hook}" "$ACTION"
done

exit 0
