#!/bin/bash

# Make sure that the interface has no state leftover from any previous
# network settings.
#

[ -n "$IFACE" ] || exit 1

# Skip if not inet or inet6
[ "$ADDRFAM" = "inet" -o "$ADDRFAM" = "inet6" ] || exit 0

# Skip if zeroconf, it uses addresses on aliased (or labeled) interfaces
# and manages them correctly itself and we do not want to interfere
# with addresses on the non-aliased interface.
[ "$METHOD" = "zeroconf" ] && exit 0

# Skips flushing existing IPv4 addresses if non-empty
SKIP_ADDR_FLUSH=

# Skips flushing existing IPv4 routes if non-empty
SKIP_ROUTE_FLUSH=

if [ "$METHOD" = "static" ] && [ "$ADDRFAM" = "inet" ]; then
    if [ -n "$IF_NETMASK" ]; then
        # Get the current IP address and prefixlen on the interface (IPv4)
        CURRENT_IP_AND_PREFIX=$(ip -4 -j -p addr show dev "$IFACE" label "$IFACE" | jq -r '.[].addr_info[] | "\(.local)/\(.prefixlen)"')

        # Get the current IP address
        CURRENT_IP="${CURRENT_IP_AND_PREFIX%/*}"

        # Get the current prefixlen needed to find the current netmask
        CURRENT_PREFIXLEN="${CURRENT_IP_AND_PREFIX#*/}"

        # Get the current IP address netmask
        CURRENT_NETMASK=$(ipcalc -m -s "$CURRENT_IP_AND_PREFIX" | cut -d'=' -f2)

        # Skip removing the static IP address when the current IP address matches the target IP address
        if [ "$CURRENT_IP" = "$IF_ADDRESS" ] && [ "$CURRENT_NETMASK" = "$IF_NETMASK" ]; then
            SKIP_ADDR_FLUSH=1
        fi
    fi

    if [ -n "$IF_GATEWAY" ]; then
        # Get the current gateway on the interface (IPv4)
        CURRENT_GATEWAY=$(ip -4 -j -p route show proto boot dev "$IFACE" | jq -r '.[].gateway')

        # Skip removing the route when the current gateway matches the target gateway
        if [ "$CURRENT_GATEWAY" = "$IF_GATEWAY" ]; then
            SKIP_ROUTE_FLUSH=1
        fi
    fi
fi

# Skip if interface does not exist yet
[ -e /sys/class/net/"$IFACE" ] || exit 0

# Skip if not of Ethernet or WLAN type
[ "$(< /sys/class/net/$IFACE/type)" = 1 ] || exit 0

# Avoid running this for the main interface (e.g., eth0) on nfsroot systems
# but clear any saved dhcpcd leases just in case the current IP address is
# not the one in the dhcpcd lease database (otherwise dhcpcd may drop the
# current address and try to reacquire the one saved in the lease database).
[ -f /etc/spinetix/identifiers ] && . /etc/spinetix/identifiers
if [ "$IFACE" = "$SPX_MAIN_IFACE_NAME" ] && grep -q nfsroot= /proc/cmdline; then
    [ "$METHOD" = "dhcp" ] && rm -f /var/lib/dhcpcd/"$IFACE".lease
    exit 0
fi

# Make sure that no leftover IP addresses from previously half
# configured or half unconfigured interfaces are associated with the
# interface; note that if all IPv4 / IPv6 addresses are removed then all
# IPv4 / IPv6 routes associated with the interface are removed as well..
# We keep other addresses (e.g., IPv6 auto-conf, IPv4LL) available.
if [ "$ADDRFAM" = "inet" ] && [ -z "$SKIP_ADDR_FLUSH" ]; then
    ip -4 addr flush dev "$IFACE" label "$IFACE" 2>/dev/null
elif [ "$ADDRFAM" = "inet6" ]; then
    # we should leave alone automatic link-local address as well as any
    # auto-configured addresses (dynamic), hence the global scope and
    # permanent attribute
    ip -6 addr flush dev "$IFACE" label "$IFACE" scope global permanent 2>/dev/null
fi

# Remove all routes which have not been truly statically assigned, as
# since other IPv4 / IPv6 addresses may be present the command above does
# not always remove the IPv4 / IPv6 routes through the interface (the boot
# protocol is the default protocol for routes added from user space).
if [ "$ADDRFAM" = "inet" ] && [ -z "$SKIP_ROUTE_FLUSH" ]; then
    ip -4 route flush proto boot dev "$IFACE" 2>/dev/null
elif [ "$ADDRFAM" = "inet6" ]; then
    # For IPv6 multicast the kernel adds a route for ff00::/8, but uses
    # the boot protocol, and it is added to the main table if the kernel
    # is not compiled with support for multiple IPv6 routing tables. So
    # just flushing all the routes with boot protocol would get rid of it
    # so we just remove default routes, which are the only type we add.
    # See https://bugzilla.kernel.org/show_bug.cgi?id=197319
    ip -6 route flush proto boot to match ::/0 dev "$IFACE"
fi

# Bring up the interface, as some methods (e.g., manual) will not do it
if ! ip link set "$IFACE" up; then
    echo "ERROR: cannot enable $IFACE" >&2
    exit 1
fi

exit 0
