netup = binario hammer-propio (sync, sólo libc, sin tokio) que configura la red: levanta el link (netlink RTM_NEWLINK), negocia un lease DHCPv4 a mano sobre UDP (DISCOVER/OFFER/REQUEST/ACK con flag broadcast), y aplica IP + ruta default + /etc/resolv.conf (RTM_NEWADDR/RTM_NEWROUTE). Hand-roll de netlink y DHCP porque no hay cliente DHCP Rust "maduro" para adoptar al estilo ripgrep, y el workspace es 100% sync (rtnetlink arrastraría tokio). Reemplaza el `ip` estático de busybox. Validado in-VM contra el DHCP de QEMU slirp: ✓ lease 10.0.2.15 + ruta + DNS + egress TCP. Autodetecta la NIC (primera no-loopback) y espera carrier por sysfs. Infra de prueba: - scripts/drive-netup.py: bootea la VM, corre netup y verifica lease/NAT/DNS. SLIRP=1 ⇒ red user-mode (cero setup de host); si no, tap del lab. - scripts/labnet.sh: lab de LAN real opcional (tap directo + dnsmasq + NAT) para fidelidad de hardware; no requerido para validar netup. - scripts/netdiag.sh: diagnóstico del camino DHCP del lab (tcpdump+nft+dnsmasq). Lección: para validar el CÓDIGO conviene slirp primero (cero plomería de host); la LAN real (firewall INPUT, sutilezas del bridge, perms) es fidelidad posterior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
4.5 KiB
Bash
77 lines
4.5 KiB
Bash
#!/bin/bash
|
|
# netdiag.sh — diagnostica (y prueba un fix) el camino DHCP del lab de red, todo como root en una corrida.
|
|
# Corré: sudo bash scripts/netdiag.sh
|
|
# Deja un reporte legible en work/netdiag.txt (chmod 644) y lo imprime.
|
|
set -u
|
|
cd "$(dirname "$0")/.." || exit 1
|
|
BR="${BR:-hmtap0}"; TAP="${TAP:-hmtap0}"; SUBNET="${SUBNET:-192.168.100}"; VMMAC="52:54:00:12:34:56"
|
|
OUT=work/netdiag.txt
|
|
DNSLOG=/tmp/hmdnsmasq.log
|
|
mkdir -p work
|
|
exec > >(tee "$OUT") 2>&1
|
|
|
|
[ "$(id -u)" = 0 ] || { echo "corré con: sudo bash scripts/netdiag.sh"; exit 1; }
|
|
echo "===== netdiag ====="
|
|
|
|
run_test() { KVM=1 MEM=4096 DEADLINE=45 LOG=work/netdiag-run.log python3 scripts/drive-netup.py >/dev/null 2>&1; }
|
|
|
|
echo; echo "### 1. firewall del host (nft) ###"
|
|
echo "-- base chains input + policies --"
|
|
nft list ruleset 2>/dev/null | grep -iE 'table |chain |hook (input|forward|prerouting)|policy|type ' | head -40
|
|
INPUT_DROP=$(nft list ruleset 2>/dev/null | grep -iE 'hook input' | grep -ci 'policy drop')
|
|
echo "input-hook chains con policy drop: $INPUT_DROP"
|
|
|
|
echo; echo "### 2. captura DHCP en $TAP (egress del VM) + $BR mientras manda DISCOVER ###"
|
|
command -v tcpdump >/dev/null || echo "(tcpdump ausente — salteo captura)"
|
|
: > /tmp/netdiag.pcap.txt; : > /tmp/netdiag.br.txt
|
|
echo "estado del tap/bridge ANTES (idle):"; ip -br link show "$TAP"; ip -br link show "$BR"
|
|
if command -v tcpdump >/dev/null; then
|
|
timeout 55 tcpdump -i "$TAP" -nnev 'udp port 67 or udp port 68' > /tmp/netdiag.pcap.txt 2>/dev/null &
|
|
TPID=$!
|
|
timeout 55 tcpdump -i "$BR" -nnev 'udp port 67 or udp port 68' > /tmp/netdiag.br.txt 2>/dev/null &
|
|
TPID2=$!; sleep 1
|
|
fi
|
|
run_test
|
|
sleep 1; for pid in ${TPID:-} ${TPID2:-}; do kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null; done
|
|
echo "-- DISCOVER/Offer en el TAP $TAP (lo que el VM realmente manda/recibe) --"
|
|
grep -icE 'Discover|Offer|Request|Reply|BOOTP' /tmp/netdiag.pcap.txt; grep -iE 'Discover|Offer|cksum' /tmp/netdiag.pcap.txt | head -6
|
|
echo "-- paquetes DHCP capturados en $BR --"
|
|
grep -icE 'Discover|Offer|Request|Reply|BOOTP' /tmp/netdiag.pcap.txt
|
|
echo "-- muestra (Discover/Offer/checksum) --"
|
|
grep -iE 'Discover|Offer|cksum' /tmp/netdiag.pcap.txt | head -10
|
|
echo "-- dnsmasq: ¿recibió/ofreció? --"
|
|
grep -iE 'DHCPDISCOVER|DHCPOFFER|DHCPACK|no address' "$DNSLOG" 2>/dev/null | tail -8 || echo "(sin entradas DHCP)"
|
|
|
|
echo; echo "### 3. veredicto del diagnóstico ###"
|
|
SAW_DISC=$(grep -ic 'Discover' /tmp/netdiag.pcap.txt)
|
|
BAD_CKSUM=$(grep -ic 'bad udp cksum' /tmp/netdiag.pcap.txt)
|
|
DNS_SAW=$(grep -ic 'DHCPDISCOVER' "$DNSLOG" 2>/dev/null)
|
|
echo "DISCOVER en bridge=$SAW_DISC bad-cksum=$BAD_CKSUM dnsmasq-vio-DISCOVER=$DNS_SAW input-drop=$INPUT_DROP"
|
|
if [ "$SAW_DISC" = 0 ]; then echo ">> el DISCOVER NO llega al bridge (problema tap/QEMU)"; fi
|
|
if [ "$SAW_DISC" != 0 ] && [ "$BAD_CKSUM" != 0 ]; then echo ">> DISCOVER llega con CHECKSUM MALO (offload) ⇒ dnsmasq lo descarta"; fi
|
|
if [ "$SAW_DISC" != 0 ] && [ "$DNS_SAW" = 0 ] && [ "$BAD_CKSUM" = 0 ]; then echo ">> DISCOVER llega OK pero dnsmasq no lo procesa ⇒ firewall INPUT (drop=$INPUT_DROP)"; fi
|
|
|
|
echo; echo "### 4. firewall INPUT: inspecciono, meto accepts CONTADOS y retesteo ###"
|
|
echo "-- inet filter input ANTES (con handles) --"
|
|
nft -a list chain inet filter input 2>/dev/null
|
|
echo "-- inserto accepts contados (por iif del bridge y por puerto DHCP/DNS) --"
|
|
nft insert rule inet filter input udp dport '{ 67, 68, 53 }' counter accept comment '"hmlab"' 2>&1 | head -2
|
|
nft insert rule inet filter input iifname "$BR" counter accept comment '"hmlab"' 2>&1 | head -2
|
|
run_test
|
|
echo "-- reglas hmlab DESPUÉS del retest (los counters dicen cuál matcheó) --"
|
|
nft -a list chain inet filter input 2>/dev/null | grep -i hmlab
|
|
echo "-- dnsmasq tras retest --"
|
|
grep -iE 'DHCPDISCOVER|DHCPOFFER|DHCPACK' "$DNSLOG" 2>/dev/null | tail -5 || echo "(nada)"
|
|
echo "-- netup tras retest --"
|
|
grep -aE 'netup: lease|netup: red configurada|sin respuesta DHCP|PING_OK|PING_FAIL|DNS_OK|DNS_FAIL' work/netdiag-run.log | tail -5
|
|
if grep -qa 'netup: red configurada' work/netdiag-run.log; then echo "RESULTADO: ✓ RED OK"; else echo "RESULTADO: ✗ sigue fallando"; fi
|
|
|
|
echo; echo "### 5. dnsmasq log COMPLETO (volcado para análisis) ###"
|
|
tail -30 "$DNSLOG" 2>/dev/null || echo "(no legible)"
|
|
echo; echo "### 6. netup salida COMPLETA del retest ###"
|
|
tr -d '\r' < work/netdiag-run.log 2>/dev/null | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g' | grep -aE 'netup:|e1000.*Link|PING_|DNS_' | tail -12
|
|
|
|
chmod 644 "$OUT" 2>/dev/null
|
|
chmod 666 work/netdiag-run.log 2>/dev/null
|
|
echo; echo "(reporte: $OUT)"
|