#!/bin/sh
#
# cmnSense - Firewall Distribution
#
# Copyright (C) 2026  Szabolcs Gyurko
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#

set -eu

usage() {
    echo "usage: cmnsense-network-takeover --yes [--snapshot FILE]" >&2
    exit 2
}

if [ "$(id -u)" -ne 0 ]; then
    echo "cmnsense-network-takeover: must be run as root" >&2
    exit 1
fi

yes=
from=
while [ $# -gt 0 ]; do
    case "$1" in
        --yes) yes=1 ;;
        --snapshot)
            [ $# -ge 2 ] || usage
            from=$2
            shift
            ;;
        *) usage ;;
    esac
    shift
done

if [ -z "$yes" ]; then
    cat <<EOF
This hands all network configuration of this system over to cmnSense:

  - the current interface addresses, static routes and name servers are
    saved, and imported into the configuration, which is then saved as the
    startup configuration (existing settings are kept); interfaces with an
    address from DHCP get "ip address dhcp"
  - systemd-networkd, systemd-resolved, NetworkManager, ConnMan, dhcpcd and
    other DHCP clients are stopped and disabled; whatever they remove on the
    way out is put back from the saved state
  - Ethernet interface names are pinned to their MAC addresses
  - cloud-init networking is disabled and /etc/resolv.conf becomes managed
    by cmnSense

Connectivity is kept throughout. Run again with --yes to proceed.
EOF
    exit 2
fi

if ! systemctl is-active --quiet cmnsense-server.service; then
    echo "cmnsense-network-takeover: cmnsense-server is not running" >&2
    exit 1
fi

work=$(mktemp -d /run/cmnsense-takeover.XXXXXX)
trap 'rm -rf "$work"' EXIT

stop_dhcp_clients() {
    for dir in /proc/[0-9]*; do
        comm=$(cat "$dir/comm" 2>/dev/null) || continue
        case "$comm" in
            dhcpcd | dhclient | udhcpc) kill -TERM "${dir#/proc/}" 2>/dev/null || true ;;
        esac
    done
}

if [ -n "$from" ]; then
    snapshot=$from
    echo "Using the network state saved in $from"
else
    snapshot=$work/network.json
    cmnsense-server snapshot-network >"$snapshot"
fi

for unit in NetworkManager.service connman.service dhcpcd.service networking.service \
            systemd-networkd-wait-online.service systemd-networkd.socket systemd-networkd-varlink.socket \
            systemd-networkd.service systemd-resolved.service; do
    if systemctl cat "$unit" >/dev/null 2>&1; then
        systemctl disable --now "$unit" >/dev/null 2>&1 || true
        systemctl mask "$unit" >/dev/null
    fi
done

if [ -z "$from" ]; then
    stop_dhcp_clients
    restored=0
    cmnsense-server restore-network --snapshot "$snapshot" >"$work/restore.log" 2>&1 || restored=$?
    if [ -s "$work/restore.log" ]; then
        echo "Restoring what the previous network manager removed:"
        sed 's/^/    /' "$work/restore.log"
    fi
    if [ "$restored" -ne 0 ]; then
        echo "cmnsense-network-takeover: some of the network state could not be restored, continuing" >&2
    fi
fi

cmnsense-server import-network --snapshot "$snapshot" >"$work/import.conf"
echo "Importing the network configuration:"
sed 's/^/    /' "$work/import.conf"
cmnsense-cli -c "copy $work/import.conf running-config" -c "write memory"

if grep -q '^  ip address dhcp$' "$work/import.conf"; then
    for _ in $(seq 1 20); do
        ip -4 route show default proto cmnsense 2>/dev/null | grep -q . && break
        sleep 1
    done
    if ip -4 route show default proto cmnsense 2>/dev/null | grep -q .; then
        ip -4 route flush proto dhcp 2>/dev/null || true
    fi
fi

echo "Pinning interface names:"
cmnsense-server pin-interface-names | sed 's/^/    /'

mkdir -p /etc/cloud/cloud.cfg.d
printf 'network: {config: disabled}\n' >/etc/cloud/cloud.cfg.d/99-cmnsense-network.cfg

for _ in 1 2 3 4 5 6 7 8 9 10; do
    [ -f /run/cmnsense/net/resolv.conf ] && break
    sleep 1
done
if [ -f /run/cmnsense/net/resolv.conf ]; then
    ln -sfn /run/cmnsense/net/resolv.conf /etc/resolv.conf
fi

if [ -n "$from" ]; then
    rm -f "$from"
fi

echo "Network configuration is now managed by cmnSense and saved as the startup configuration."
