# General interface configuration
# Set addresses and routes.

pad_network()
{
    local addr="$1"
    local c

    c=$((3-$(echo "$addr" | grep -o '\.' | wc -l)))

    [ $c -ge 0 ] 2>/dev/null || return 1

    while [ "$c" -gt 0 ]; do
        addr="$addr.0"
        c=$((c-1))
    done

    echo "$addr"
}

set_classless_static_routes()
{
    local prefix dest via_arg net_address gateway

    set -- $new_classless_static_routes
    while [ $# -gt 0 ]; do
        dest="$1"
        gateway="$2"
        shift 2
        [ -n "$dest" -a -n "$gateway" ] || return 1
        prefix="${dest%%.*}"

        if [ $prefix -eq 0 ]; then
            # default route
            net_address="0.0.0.0"
        elif [ $prefix -gt 0 -a $prefix -le 32 ]; then
            net_address="$(pad_network ${dest#*.})"
        else
            return 1
        fi

        # take care of link-local routes
        if [ "${gateway}" != '0.0.0.0' ]; then
            via_arg="via ${gateway}"
        else
            via_arg=''
        fi

        # set route (ip detects host routes automatically)
        ip -4 route add "${net_address}/${prefix}" \
            ${via_arg} dev "${interface}" >/dev/null 2>&1
    done
}

case "$reason" in

    ### DHCPv4 Handlers

    MEDIUM|ARPCHECK|ARPSEND)
        # Do nothing
        ;;
    PREINIT)
        # The DHCP client is requesting that an interface be
        # configured as required in order to send packets prior to
        # receiving an actual address. - dhclient-script(8)

        # ensure interface is up
        ip link set dev ${interface} up

        if [ -n "$alias_ip_address" ]; then
            # flush alias IP from interface
            ip -4 addr flush dev ${interface} label ${interface}:0
        fi
        ;;

    BOUND|RENEW|REBIND|REBOOT)
        if [ -n "$old_ip_address" ] && [ -n "$alias_ip_address" ] &&
           [ "$alias_ip_address" != "$old_ip_address" ]; then
            # alias IP may have changed => flush it
            ip -4 addr flush dev ${interface} label ${interface}:0
        fi

        if [ -n "$old_ip_address" ] &&
           [ "$old_ip_address" != "$new_ip_address" ]; then
            # leased IP has changed => flush it
            ip -4 addr flush dev ${interface} label ${interface}
        fi

        if [ -z "$old_ip_address" ] ||
           [ "$old_ip_address" != "$new_ip_address" ] ||
           [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then
            # new IP has been leased or leased IP changed => set it
            ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \
                ${new_broadcast_address:+broadcast $new_broadcast_address} \
                dev ${interface} label ${interface}

            # if we have $new_classless_static_routes then we have to
            # ignore $new_routers entirely
            if [ -n "$new_classless_static_routes" ]; then
                set_classless_static_routes
            else
                # set if_metric if IF_METRIC is set or there's more than one router
                if_metric="$IF_METRIC"
                if [ "${new_routers%% *}" != "${new_routers}" ]; then
                if_metric=${if_metric:-1}
                fi

                for router in $new_routers; do
                if [ "$new_subnet_mask" = "255.255.255.255" ]; then
                    # point-to-point connection => set explicit route
                    ip -4 route add ${router} dev $interface >/dev/null 2>&1
                fi

                # set default route
                ip -4 route add default via ${router} dev ${interface} \
                    ${if_metric:+metric $if_metric} >/dev/null 2>&1

                if [ -n "$if_metric" ]; then
                    if_metric=$((if_metric+1))
                fi
                done
            fi
        fi

        if [ -n "$alias_ip_address" ] &&
           [ "$new_ip_address" != "$alias_ip_address" ]; then
            # separate alias IP given, which may have changed
            # => flush it, set it & add host route to it
            ip -4 addr flush dev ${interface} label ${interface}:0
            ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
                dev ${interface} label ${interface}:0
            ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
        fi
        ;;

    EXPIRE|FAIL|RELEASE|STOP)
        if [ -n "$alias_ip_address" ]; then
            # flush alias IP
            ip -4 addr flush dev ${interface} label ${interface}:0
        fi

        if [ -n "$old_ip_address" ]; then
            # flush leased IP
            ip -4 addr flush dev ${interface} label ${interface}
        fi

        if [ -n "$alias_ip_address" ]; then
            # alias IP given => set it & add host route to it
            ip -4 addr add ${alias_ip_address}${alias_network_arg} \
                dev ${interface} label ${interface}:0
            ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
        fi
        ;;

    TIMEOUT)
        if [ -n "$alias_ip_address" ]; then
            # flush alias IP
            ip -4 addr flush dev ${interface} label ${interface}:0
        fi

        # set IP from recorded lease
        ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \
            ${new_broadcast_address:+broadcast $new_broadcast_address} \
            dev ${interface} label ${interface}

        # if there is no router recorded in the lease or the 1st router answers pings
        if [ -z "$new_routers" ] || ping -q -c 1 "${new_routers%% *}"; then
            # if we have $new_classless_static_routes then we have to
            # ignore $new_routers entirely
            if [ ! "$new_classless_static_routes" ]; then
                if [ -n "$alias_ip_address" ] &&
                   [ "$new_ip_address" != "$alias_ip_address" ]; then
                # separate alias IP given => set up the alias IP & add host route to it
                ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \
                    dev ${interface} label ${interface}:0
                ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1
                fi

                # set if_metric if IF_METRIC is set or there's more than one router
                if_metric="$IF_METRIC"
                if [ "${new_routers%% *}" != "${new_routers}" ]; then
                if_metric=${if_metric:-1}
                fi

                # set default route
                for router in $new_routers; do
                ip -4 route add default via ${router} dev ${interface} \
                    ${if_metric:+metric $if_metric} >/dev/null 2>&1

                if [ -n "$if_metric" ]; then
                    if_metric=$((if_metric+1))
                fi
                done
            fi
        else
            # flush all IPs from interface
            ip -4 addr flush dev ${interface}
        fi

        ;;

    ### DHCPv6 Handlers
    # TODO handle prefix change: ?based on ${old_ip6_prefix} and ${new_ip6_prefix}?

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

        # flush any stale global permanent IPs from interface
        ip -6 addr flush dev ${interface} scope global permanent

        ;;

    BOUND6|RENEW6|REBIND6)
        if [ -n "${new_ip6_address}" -a -n "${new_ip6_prefixlen}" ] &&
           [  "${new_ip6_address}" != "${old_ip6_address}" ]; then
            # set leased IP
            ip -6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \
                dev ${interface} scope global
        fi
        ;;

    DEPREF6)
        if [ -n "${cur_ip6_prefixlen}" ]; then
            # set preferred lifetime of leased IP to 0
            ip -6 addr change ${cur_ip6_address}/${cur_ip6_prefixlen} \
                dev ${interface} scope global preferred_lft 0
        fi
        ;;

    EXPIRE6|RELEASE6|STOP6)
        if [ -n "${old_ip6_address}" -a -n "${old_ip6_prefixlen}" ]; then
            # delete leased IP
            ip -6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \
                dev ${interface}
        fi
        ;;
esac
