#!/bin/bash

# For Ethernet interfaces (e.g., eth0) configured with static IP
# address check that the intended address is not already in use.

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

# Skip if not inet
[ "$ADDRFAM" = "inet" ] || exit 0

# Skip if not static assignment
[ "$METHOD" = "static" ] || exit 0

# 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

# Check that there is an IP address
[ -n "$IF_ADDRESS" ] || exit 1

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

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

# Flush leftover IPv4 addresses from interface, so that it has none when
# we bring it up (other addresses managed by other daemons may be active,
# we leave those alone based on the label)
if [ -z "$SKIP_ADDR_FLUSH" ]; then
    ip -4 addr flush dev "$IFACE" label "$IFACE" 2>/dev/null
fi

# Bring up the interface
if ! ip link set "$IFACE" up; then
    echo "ERROR: cannot enable $IFACE" >&2
    exit 1
fi

# Perform duplicate address detection (RFC2131, 4.4.1 and RFC3927, 2.2.1)
# We run it at the highest possible priority so that it does not timeout
# due to lack of scheduling.
# If the interface was just brought up it may take a few seconds for the
# carrier to be detected, so lengthen the time if no carrier yet.
[ "$(< /sys/class/net/$IFACE/carrier)" = 1 ] && cnt=3 || cnt=6
nice -n -20 arping -q -c $cnt -w $cnt -D -I "$IFACE" "$IF_ADDRESS"
RET_DAD=$?
if [ $RET_DAD -ne 0 ]; then
    echo "IP address $IF_ADDRESS is already in use, failing $IFACE" >&2
fi

# NOTE: we do not bring down the interface, because we want to
# keep its IPv6 address alive, link detection, etc.
#if ! ip link set "$IFACE" down; then
#    echo "ERROR: cannot disable $IFACE" >&2
#fi

exit $RET_DAD
