#!/bin/sh
# labnet.sh — lab de red para probar `netup` (y luego OpenSSH) in-VM con DHCP/DNS/NAT REALES, sin poner
# la VM en la LAN física (el host sale por WiFi, que no se puede bridgear a L2).
#
# Modelo TAP DIRECTO (sin bridge): para UNA VM no hace falta bridge. El host pone su IP en el tap
# (192.168.100.1/24) y corre dnsmasq escuchando ahí; la VM y el host son pares L2 sobre el tap. Esto
# evita la sutileza del Linux bridge (broadcast entregado localmente desde un puerto con skb_iif =
# puerto, que el SO_BINDTODEVICE de dnsmasq al bridge no matchea ⇒ no recibía el DISCOVER).
#
# Da DHCP+DNS reales (dnsmasq) + salida a internet por NAT hacia el uplink. Ejercita exactamente los
# code paths de netup: netlink real, DHCP DISCOVER/OFFER/REQUEST/ACK reales, ruta + DNS reales.
#
# REQUIERE ROOT.  Levantar:  sudo sh scripts/labnet.sh up        Bajar:  sudo sh scripts/labnet.sh down
# El tap queda owned por $LAB_USER ⇒ QEMU (no-root) lo abre.
#
# Variables: TAP (def hmtap0)  SUBNET (def 192.168.100)  UPLINK (def wlan0)
#            LAB_USER (dueño del tap, def $SUDO_USER)  DNS_UP (upstream DNS, def 192.168.1.1)
set -eu
TAP="${TAP:-hmtap0}"
SUBNET="${SUBNET:-192.168.100}"
UPLINK="${UPLINK:-wlan0}"
LAB_USER="${LAB_USER:-${SUDO_USER:-root}}"
DNS_UP="${DNS_UP:-192.168.1.1}"
PIDF="/run/hmdnsmasq.pid"

[ "$(id -u)" = 0 ] || { echo "labnet: requiere root (sudo sh scripts/labnet.sh $*)" >&2; exit 1; }

fw_clean() {  # quita las reglas "hmlab" que insertamos en el firewall del host
    for ch in input forward; do
        while h=$(nft -a list chain inet filter "$ch" 2>/dev/null | awk '/comment "hmlab"/{print $NF; exit}'); [ -n "${h:-}" ]; do
            nft delete rule inet filter "$ch" handle "$h" 2>/dev/null || break
        done
    done
}

up() {
    echo "==> tap $TAP ($SUBNET.1/24, owner $LAB_USER) — modelo tap directo, sin bridge"
    # auto-limpieza de estado viejo (bridge de versiones previas + tap que pudo quedar enslaved)
    ip link del hmbr0 2>/dev/null || true
    ip link del "$TAP" 2>/dev/null || true
    ip tuntap add dev "$TAP" mode tap user "$LAB_USER"
    ip addr replace "$SUBNET.1/24" dev "$TAP"
    ip link set "$TAP" up

    echo "==> forwarding + NAT (nftables) $SUBNET.0/24 → $UPLINK"
    sysctl -wq net.ipv4.ip_forward=1
    nft list table ip hmnat >/dev/null 2>&1 || nft add table ip hmnat
    nft flush table ip hmnat
    nft add chain ip hmnat hmpost '{ type nat hook postrouting priority 100 ; }'
    nft add rule ip hmnat hmpost ip saddr "$SUBNET.0/24" oifname "$UPLINK" masquerade

    # El host tiene su PROPIO firewall (inet filter, policy drop en input/forward). Insertamos accepts
    # ACOTADOS al tap del lab DENTRO de inet filter (al tope, vía `insert`), reversibles por su comment
    # "hmlab". input: el DISCOVER llega a dnsmasq. forward: el NAT VM→internet pasa.
    if nft list chain inet filter input >/dev/null 2>&1; then
        echo "==> permitir $TAP en el firewall del host (inet filter input/forward)"
        fw_clean
        nft insert rule inet filter input   iifname "$TAP" accept comment "hmlab"
        nft insert rule inet filter forward iifname "$TAP" accept comment "hmlab"
        nft insert rule inet filter forward oifname "$TAP" accept comment "hmlab"
    fi

    echo "==> dnsmasq (DHCP $SUBNET.50-150 + DNS→$DNS_UP) en $TAP"
    [ -f "$PIDF" ] && kill "$(cat "$PIDF")" 2>/dev/null || true
    dnsmasq --interface="$TAP" --bind-interfaces --except-interface=lo \
        --dhcp-range="$SUBNET.50,$SUBNET.150,12h" --dhcp-authoritative \
        --dhcp-option=3,"$SUBNET.1" --dhcp-option=6,"$SUBNET.1" \
        --listen-address="$SUBNET.1" --no-resolv --server="$DNS_UP" \
        --dhcp-leasefile=/tmp/hmdnsmasq.leases \
        --pid-file="$PIDF" --log-dhcp --log-facility=/tmp/hmdnsmasq.log
    chmod 644 /tmp/hmdnsmasq.log /tmp/hmdnsmasq.leases 2>/dev/null || true
    echo "==> listo. QEMU: -netdev tap,id=n0,ifname=$TAP,script=no,downscript=no -device e1000,netdev=n0"
}

down() {
    echo "==> teardown"
    [ -f "$PIDF" ] && kill "$(cat "$PIDF")" 2>/dev/null && rm -f "$PIDF" || true
    nft delete table ip hmnat 2>/dev/null || true
    fw_clean
    ip link del "$TAP" 2>/dev/null || true
    echo "==> red del lab desmontada"
}

case "${1:-}" in
    up) up ;;
    down) down ;;
    *) echo "uso: sudo sh scripts/labnet.sh {up|down}" >&2; exit 1 ;;
esac
