#!/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