#!/bin/sh
# Aurora router (B12R, runtime state only in /run): aurora-router up | down | status
#   up:   wlan0 192.168.77.1/24, default route via wwan0 (only if none), nftables table inet aurora_router (NAT LAN->wwan0, default-deny forward,
#         no LTE->LAN, Wi-Fi blocked from the USB mgmt subnet and from every board address except 192.168.77.1 DHCP/DNS/ping), ip_forward=1,
#         dnsmasq bound to wlan0 only (upstream = bearer DNS).
#   down: undo exactly that. Needs kernel nf_tables (7.2.7-aurora-b12).
. /etc/aurora/router.conf
S=/run/aurora-router; LOG=$S/router.log; mkdir -p $S
now() { cut -d' ' -f1 /proc/uptime; }
log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-router] $*" > /dev/kmsg 2>/dev/null; }
fail() { log "FAIL: $*"; exit 1; }
dpid() { [ -f $S/dnsmasq.pid ] && kill -0 "$(cat $S/dnsmasq.pid)" 2>/dev/null && cat $S/dnsmasq.pid; }
case "$1" in
up)
	log "=== ROUTER UP"
	nft list tables >/dev/null 2>&1 || fail "kernel has no nf_tables ($(uname -r))"
	[ -e /sys/class/net/$LAN_IF ] || fail "$LAN_IF absent"; [ -e /sys/class/net/$WAN_IF ] || fail "$WAN_IF absent"
	DNS=$(sed -n 's/^DNS=//p' /run/aurora-modem/ipcfg 2>/dev/null | tr ',' ' '); [ -n "$DNS" ] || DNS=$FALLBACK_DNS
	[ -f $S/ip_forward.saved ] || cat /proc/sys/net/ipv4/ip_forward > $S/ip_forward.saved
	ip -4 addr show dev $LAN_IF | grep -q "inet $LAN_ADDR/" || ip addr add $LAN_ADDR/$LAN_PFX dev $LAN_IF || fail "addr $LAN_IF"
	if ! ip route | grep -q "^default"; then ip route add default dev $WAN_IF && echo $WAN_IF > $S/default.added || fail "default route"; fi
	cat > $S/ruleset.nft <<N
table inet aurora_router
delete table inet aurora_router
table inet aurora_router {
	chain input {
		type filter hook input priority filter; policy accept;
		ct state established,related accept
		iifname "lo" accept
		iifname "$MGMT_IF" accept
		iifname "$LAN_IF" jump lan_in
		iifname "$WAN_IF" jump wan_in
	}
	chain lan_in {
		udp sport 68 udp dport 67 accept comment "DHCP (incl. broadcast)"
		ip daddr != $LAN_ADDR counter drop comment "Wi-Fi may not touch mgmt/WAN addresses of the board"
		udp dport 53 accept
		tcp dport 22 accept comment "B17: SSH (dropbear) from Wi-Fi to the board LAN address only"
		tcp dport 53 accept
		icmp type echo-request limit rate 20/second accept
		meta l4proto ipv6-icmp accept
		counter drop
	}
	chain wan_in {
		icmp type { echo-reply, destination-unreachable, time-exceeded } accept
		counter drop comment "no unsolicited inbound from LTE"
	}
	chain forward {
		type filter hook forward priority filter; policy drop;
		ct state invalid counter drop
		iifname "$LAN_IF" oifname "$WAN_IF" ip saddr $LAN_NET counter accept
		iifname "$WAN_IF" oifname "$LAN_IF" ct state established,related counter accept
		iifname "$LAN_IF" oifname "$MGMT_IF" counter drop comment "Wi-Fi -> USB mgmt blocked"
		iifname "$MGMT_IF" oifname "$LAN_IF" counter drop
		counter drop comment "default deny (incl. LTE -> LAN)"
	}
	chain postrouting {
		type nat hook postrouting priority srcnat; policy accept;
		oifname "$WAN_IF" ip saddr $LAN_NET counter masquerade
	}
}
N
	# B15: aurora-vpn owns the firewall while up (fail-closed LAN -> tunnel); a late router up (AP enabled after VPN) must not
	# re-add aurora_router, whose forward chain drops LAN -> wg0/singtun0 (both forward hooks must accept).
	if nft list table inet aurora_vpn >/dev/null 2>&1; then log "aurora_vpn table present: keep VPN firewall, skip aurora_router ruleset"
	else nft -f $S/ruleset.nft || fail "nft ruleset"; fi
	echo 1 > /proc/sys/net/ipv4/ip_forward
	if [ -z "$(dpid)" ]; then
		{ echo "interface=$LAN_IF"; echo "except-interface=lo"; echo "bind-interfaces"; echo "no-resolv"; echo "no-hosts"
		  for d in $DNS; do echo "server=$d"; done
		  echo "dhcp-range=$DHCP_RANGE"; echo "dhcp-option=option:router,$LAN_ADDR"; echo "dhcp-option=option:dns-server,$LAN_ADDR"
		  echo "dhcp-leasefile=$S/dnsmasq.leases"; echo "dhcp-authoritative"; echo "cache-size=300"; echo "domain-needed"; echo "bogus-priv"
		  echo "pid-file=$S/dnsmasq.pid"; echo "log-facility=$S/dnsmasq.log"; echo "log-dhcp"; echo "user=root"; } > $S/dnsmasq.conf
		dnsmasq --conf-file=$S/dnsmasq.conf || fail "dnsmasq"; sleep 1
	fi
	[ -n "$(dpid)" ] || fail "dnsmasq not running"
	log "=== ROUTER UP OK lan $LAN_IF $LAN_ADDR/$LAN_PFX dhcp $DHCP_RANGE dns [$DNS] wan $WAN_IF default=[$(ip route | grep "^default")] fwd=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dpid)";;
down)
	log "=== ROUTER DOWN"
	p=$(dpid); [ -n "$p" ] && { kill $p; sleep 1; }; rm -f $S/dnsmasq.pid
	nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router
	[ -f $S/ip_forward.saved ] && { cat $S/ip_forward.saved > /proc/sys/net/ipv4/ip_forward; rm -f $S/ip_forward.saved; }
	ip addr del $LAN_ADDR/$LAN_PFX dev $LAN_IF 2>/dev/null
	[ -f $S/default.added ] && { ip route del default dev "$(cat $S/default.added)" 2>/dev/null; rm -f $S/default.added; }
	log "=== ROUTER DOWN OK fwd=$(cat /proc/sys/net/ipv4/ip_forward) tables=[$(nft list tables 2>/dev/null | tr '\n' ' ')] default=[$(ip route | grep "^default")]";;
status)
	echo "kernel $(uname -r) ip_forward=$(cat /proc/sys/net/ipv4/ip_forward) dnsmasq=$(dpid)"
	ip -4 addr show dev $LAN_IF | grep inet; ip route | grep "^default"
	echo "-- leases"; cat $S/dnsmasq.leases 2>/dev/null
	echo "-- stations"; iw dev $LAN_IF station dump 2>/dev/null | grep -E "^Station|signal:|connected time"
	echo "-- conntrack $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null)"
	nft list table inet aurora_router 2>&1 | grep -E "counter|chain"
	tail -3 $LOG 2>/dev/null;;
*) echo "usage: $0 up|down|status"; exit 2;;
esac
