#!/bin/sh

set -e

# Read the KEY=value lines omr-admin writes for this user without running the
# file as shell code: it carries values a router sent through the API, and a
# `. file` as root would run anything one of them smuggled in. Only the keys
# given are set, and only to a value that can be an address, a number or a
# name, which every one of them is.
read_conf() {
        conf="$1"
        shift
        while IFS= read -r line || [ -n "$line" ]; do
                key="${line%%=*}"
                [ "$key" = "$line" ] && continue
                case "$key" in
                        ""|*[!A-Z0-9_]*) continue ;;
                esac
                case " $* " in
                        *" $key "*) ;;
                        *) continue ;;
                esac
                value="${line#*=}"
                case "$value" in
                        *[!A-Za-z0-9:./_-]*)
                                echo "$(basename "$0"): ignoring invalid $key in $conf" >&2
                                continue
                                ;;
                esac
                eval "$key=\$value"
        done < "$conf"
}

if [ ! -f "$2" ]; then
        echo "usage: $(basename "$0") start FILE"
        exit 0
fi

read_conf "$(readlink -f "$2")" VNI PORT LOCALIP REMOTEIP MODE BRIDGE LOCALTUNIP LOCALTUNIP6 MTU

DEV="vx-$(basename "$2")"
PORT=${PORT:-4789}

MODE=${MODE:-l3}
SYSFS_NET=${SYSFS_NET:-/sys/class/net}

if [ "$1" = "start" ]; then
        if [ -n "$(ip link show ${DEV} 2>/dev/null)" ]; then
                ip link del ${DEV} 2>&1 >/dev/null
        fi
        ip link add ${DEV} type vxlan id ${VNI} local ${LOCALIP} remote ${REMOTEIP} dstport ${PORT}
        if [ "${MODE}" = "l2" ]; then
                # L2: no address on the raw device, it only ever switches frames
                # for whichever bridge it joins (usually shared by every client on
                # the same VNI, so their LANs merge into one broadcast domain)
                if [ -n "${BRIDGE}" ]; then
                        if [ ! -d "${SYSFS_NET}/${BRIDGE}" ]; then
                                ip link add ${BRIDGE} type bridge
                        fi
                        ip link set ${BRIDGE} up
                        ip link set ${DEV} master ${BRIDGE}
                fi
        else
                if [ -n "${LOCALTUNIP}" ]; then
                        ip addr add ${LOCALTUNIP} dev ${DEV}
                fi
                if [ -n "${LOCALTUNIP6}" ]; then
                        ip -6 addr add ${LOCALTUNIP6} dev ${DEV}
                fi
        fi
        if [ -n "${MTU}" ]; then
                ip link set mtu ${MTU} dev ${DEV}
        fi
        ip link set ${DEV} up
fi
if [ "$1" = "stop" ]; then
        ip link del ${DEV}
        # Drop the shared bridge once its last VXLAN port is gone; harmless if
        # another user's device is still enslaved to it (brif stays non-empty)
        if [ -n "${BRIDGE}" ] && [ -d "${SYSFS_NET}/${BRIDGE}/brif" ] && [ -z "$(ls ${SYSFS_NET}/${BRIDGE}/brif 2>/dev/null)" ]; then
                ip link del ${BRIDGE}
        fi
fi
