NETMGR_DESIGN.md 期2范围落地(全在 patch_v2/meow_netmgrd/ + meow_ipc.h 追加):
- 连接普查(§4):每 2s 解析 /proc/net/{tcp,udp}(IPv4) → socket inode→pid(由 inode 集驱动扫
/proc/<pid>/fd,不逐 pid 盲扫)→ 三实体归因 app:<pkg>(amsd status)/svc:<svc>(svcd 落盘状态
/tmp/meow_svcd.status,socket 查询兜底)/proc:<comm>(无则 proc:unknown)。连接表静态 ≤512:
entity/五元组/状态/寿命;远端 IP 由 DNS 缓存反查域名。
- DNS 代理(§5):监听 127.0.0.1:53 UDP;启动备份 resolv.conf 写双行(127.0.0.1+原上游,崩溃零断流);
透传转发+缓存(TTL 尊重;≤256 条);按源端口(port→entity)归因;域名规则 per-entity+global allow/deny
(deny→NXDOMAIN+日志);atexit/SIGTERM/SIGHUP 恢复;5s 自检被第三方改回即重写;HibyOS 绝不启用/绝不改 resolv。
DNS wire(deny NXDOMAIN / forward→A+cache / NXDOMAIN id 回显) 宿主+qemu 实测 PASS。
- 事件日志(§8):新连接/关闭/dns deny/接口状态变化;同 entity 同 dst 1 分钟去重(首条+计数)。
- IPC meow_ipc.h 追加 0x00E8 CONNS_REQ/RESULT、0x00EC DNS_CTL/RESULT(纯追加不改既有行;
0x00EA 策略(期3)/0x00EE 事件推送(预留) 保留)。
- CLI meow_net 补 apps / conns [entity] / top(2s 刷新) / dns status|stats|flush。
- 内存:静态表禁 malloc,mipsel bss 228KB → RSS≈374KB,≤600KB 目标 / ≤1MB 预算。
宿主编译 -Wall -Wextra 0 warning;test_netmgrd.sh 48/48(宿主+qemu)、test_netmgrd_p2 全过(宿主+qemu)、
netmgrd_authtest 全过;真机(M6.0/0.8.0)只读实测 apps/conns/dns status 正确
(svc:meowpanel/svc:mdnsd/proc:dropbear 真实三实体归因),resolv.conf 测后核验原值未触碰。
270 lines
12 KiB
Bash
Executable File
270 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
||
# test_netmgrd.sh — meow_netmgrd 宿主桩测试(假 sysfs/procfs 节点 + 桩后端命令,MEOW_* env 覆盖)。
|
||
# 用法:./test_netmgrd.sh [host二进制路径](默认 ./meow_netmgrd_host;可传 mipsel 二进制 + QEMU 前缀)
|
||
# QEMU="qemu-mipsel" ./test_netmgrd.sh ./meow_netmgrd # qemu 验证
|
||
set -u
|
||
cd "$(dirname "$0")"
|
||
BIN="${1:-./meow_netmgrd_host}"
|
||
Q="${QEMU:-}"
|
||
T=$(mktemp -d /tmp/netmgrdtest.XXXXXX)
|
||
trap 'kill $DPID 2>/dev/null; rm -rf "$T"' EXIT
|
||
PASS=0; FAIL=0
|
||
ck() { # ck <名> <期望子串> <实际>
|
||
if echo "$3" | grep -qF "$2"; then PASS=$((PASS+1)); echo "PASS $1";
|
||
else FAIL=$((FAIL+1)); echo "FAIL $1: 期望含 [$2] 实际=[$3]"; fi
|
||
}
|
||
|
||
# ---- 搭假节点 ----
|
||
NET="$T/net"; RF="$T/rfkill"; BT="$T/bt"
|
||
mkdir -p "$NET/lo" "$NET/wlan0" "$RF/rfkill0" "$RF/rfkill1" "$BT/hci0"
|
||
echo up > "$NET/lo/operstate"
|
||
echo up > "$NET/wlan0/operstate"
|
||
echo wlan > "$RF/rfkill0/type"; echo 0 > "$RF/rfkill0/soft"; echo 0 > "$RF/rfkill0/hard"
|
||
echo bluetooth > "$RF/rfkill1/type"; echo 1 > "$RF/rfkill1/soft"; echo 0 > "$RF/rfkill1/hard"
|
||
echo 00:1A:7D:DA:71:13 > "$BT/hci0/address"
|
||
echo on > "$BT/hci0/power"
|
||
cat > "$T/netdev" <<'EOF'
|
||
Inter-| Receive | Transmit
|
||
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
||
lo: 1000 10 0 0 0 0 0 0 1000 10 0 0 0 0 0 0
|
||
wlan0: 5000000 5000 0 0 0 0 0 0 1000000 1000 0 0 0 0 0 0
|
||
EOF
|
||
cat > "$T/route" <<'EOF'
|
||
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||
wlan0 005EC20A 6F3DC20A 0003 0 0 0 00FFFFFF 0 0 0
|
||
lo 0100007F 00000000 0001 0 0 0 000000FF 0 0 0
|
||
EOF
|
||
printf 'nameserver 223.5.5.5\nnameserver 10.78.193.1\n' > "$T/resolv"
|
||
echo meow > "$T/boot_mode"
|
||
|
||
# ---- 期2 观测层打桩:/proc/net/{tcp,udp}(IPv4 LE hex)+ 域名规则 conf + amsd/svcd 缺省 ----
|
||
cat > "$T/tcp" <<'EOF'
|
||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||
0: 32C14E0A:5137 0905CD8C:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12345 1 0 0 10 0
|
||
1: 32C14E0A:4F32 32C34E0A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12346 1 0 0 10 0
|
||
2: 32C14E0A:0A00 00000000:0000 07 00000000:00000000 00:00000000 00000000 1000 0 67890 1 0 0 10 0
|
||
EOF
|
||
cat > "$T/udp" <<'EOF'
|
||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||
0: 0100007F:D301 0100007F:0035 07 00000000:00000000 00:00000000 00000000 0 0 99999 1 0 0 10 0
|
||
EOF
|
||
printf 'app:dev.player.ncm domain=-ads.example.com\nproc:dropbear domain=+x\n' > "$T/policy"
|
||
|
||
# 桩后端命令(popen stubbing)
|
||
cat > "$T/wpa_cli" <<'EOF'
|
||
#!/bin/sh
|
||
case "$1" in
|
||
status) echo "wpa_state=COMPLETED"; echo "ssid=MyWiFi"; echo "ip_address=10.78.193.50"; echo "signal=-45";;
|
||
scan) echo OK;;
|
||
scan_results) printf 'bssid / frequency / signal level / flags / ssid\nxx:xx / 2412 / -50 / WPA2 / TestAP\n';;
|
||
list_networks) printf 'network id / ssid / bssid / flags\n0\tMyWiFi\tany\t[CURRENT]\n';;
|
||
add_network) echo 1;;
|
||
set_network) echo OK;;
|
||
enable_network) echo OK;;
|
||
select_network) echo OK;;
|
||
disconnect) echo OK;;
|
||
esac
|
||
exit 0
|
||
EOF
|
||
cat > "$T/hciconfig" <<'EOF'
|
||
#!/bin/sh
|
||
echo "hci0: Type: Primary Bus: USB"
|
||
echo " BD Address: 00:1A:7D:DA:71:13 ACL MTU: 1021:8 SCO MTU: 64:1"
|
||
echo " UP RUNNING PSCAN"
|
||
EOF
|
||
cat > "$T/bluetoothctl" <<'EOF'
|
||
#!/bin/sh
|
||
echo "Device 00:1A:7D:DA:71:13 My Headset"
|
||
echo "Device 11:22:33:44:55:66 Speaker"
|
||
EOF
|
||
cat > "$T/ip" <<'EOF'
|
||
#!/bin/sh
|
||
for a in "$@"; do
|
||
if [ "$a" = "wlan0" ]; then echo "2: wlan0 inet 10.78.193.50/24 brd 255.255.255.255 scope global wlan0"; fi
|
||
if [ "$a" = "lo" ]; then echo "1: lo inet 127.0.0.1/8 scope host lo"; fi
|
||
done
|
||
exit 0
|
||
EOF
|
||
for f in wpa_cli hciconfig bluetoothctl ip; do chmod +x "$T/$f"; done
|
||
|
||
mkdir -p "$T/logs" "$T/pid"
|
||
# 期2:DNS 禁用(宿主无 root 不绑 53),观测层经 MEOW_TCP/UDP 打桩;amsd/svcd 缺省(实体归因走 proc:comm→unknown)。
|
||
ENVS="MEOW_NET_DIR=$NET MEOW_NETDEV=$T/netdev MEOW_NET_ROUTE=$T/route MEOW_RESOLV=$T/resolv
|
||
MEOW_RFKILL_DIR=$RF MEOW_BT_DIR=$BT MEOW_BOOT_MODE=$T/boot_mode MEOW_BOOTTS_CACHE=$T/bootts
|
||
MEOW_IP=$T/ip MEOW_WPA_CLI=$T/wpa_cli MEOW_HCICONFIG=$T/hciconfig MEOW_BLUETOOTHCTL=$T/bluetoothctl
|
||
MEOW_NET_SOCK=$T/net.sock MEOW_NET_POLL_SEC=1 MEOW_LOG_DIR=$T/logs
|
||
MEOW_TCP=$T/tcp MEOW_UDP=$T/udp MEOW_NET_POLICY=$T/policy
|
||
MEOW_AMS_SOCK=$T/no_ams MEOW_SVCD_SOCK=$T/no_svcd MEOW_DNS_DISABLE=1"
|
||
ln -sf "$(readlink -f "$BIN")" "$T/meow_net" # argv[0] 双形态 → CLI
|
||
|
||
# ---- T0: 守护起 ----
|
||
env $ENVS $Q "$BIN" --sock "$T/net.sock" >"$T/daemon.log" 2>&1 &
|
||
DPID=$!
|
||
for i in $(seq 1 30); do [ -S "$T/net.sock" ] && break; sleep 0.2; done
|
||
[ -S "$T/net.sock" ] || { echo "FAIL T0: daemon socket 未出现"; cat "$T/daemon.log"; exit 1; }
|
||
PASS=$((PASS+1)); echo "PASS T0 daemon up"
|
||
|
||
# ---- T1: status(读级,任意调用者)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T1a "mode=meow src=daemon" "$OUT"
|
||
ck T1b "iface:lo kind=lo up=true" "$OUT"
|
||
ck T1c "iface:wlan0 kind=wifi up=true" "$OUT"
|
||
ck T1d "ip=10.78.193.50" "$OUT" # 若 iface 行 ip 解析成功(ip 桩已修)——实际由 wlan gw 行给,见下
|
||
ck T1e "gw=10.194.61.111" "$OUT"
|
||
ck T1f "wifi connected=1 state=COMPLETED ssid=MyWiFi" "$OUT"
|
||
ck T1g "bt available=1 powered=1 address=00:1A:7D:DA:71:13 paired=2" "$OUT"
|
||
ck T1h "rfkill:wlan soft=0 hard=0" "$OUT"
|
||
ck T1i "rfkill:bluetooth soft=1 hard=0" "$OUT"
|
||
|
||
# ---- T2: iface 单/全 ----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" iface wlan0 2>&1)
|
||
ck T2a '"name":"wlan0"' "$OUT"
|
||
ck T2b '"kind":"wifi"' "$OUT"
|
||
ck T2c '"ip":"10.78.193.50"' "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" iface 2>&1)
|
||
ck T2d '"name":"lo"' "$OUT"
|
||
|
||
# ---- T3: wifi networks(读级)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi networks 2>&1)
|
||
ck T3a "MyWiFi" "$OUT"
|
||
|
||
# ---- T4: wifi scan(写级;宿主 CLI=CALL_OTHER → 拒)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi scan 2>&1)
|
||
ck T4a "permission denied" "$OUT"
|
||
|
||
# ---- T5: bt status / bt paired(读级)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt status 2>&1)
|
||
ck T5a "available=1 powered=1" "$OUT"
|
||
ck T5b "paired=2" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt paired 2>&1)
|
||
ck T5c "My Headset" "$OUT"
|
||
ck T5d "Speaker" "$OUT"
|
||
|
||
# ---- T6: HibyOS 只读降级(读可查,写拒)----
|
||
echo hiby > "$T/boot_mode"
|
||
sleep 2 # 等守护下一拍热切换
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T6a "mode=readonly" "$OUT"
|
||
ck T6b "iface:wlan0" "$OUT" # 读侧仍可查
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi networks 2>&1)
|
||
ck T6c "MyWiFi" "$OUT" # 读级子命令仍可用
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi on 2>&1); RC=$?
|
||
ck T6d "permission denied" "$OUT"
|
||
echo meow > "$T/boot_mode"
|
||
sleep 2
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T6e "mode=meow" "$OUT" # 热切换回全功能
|
||
|
||
# ---- T7: 守护日志(每次开机新文件;接口 add/写拒绝 必记)----
|
||
sleep 1
|
||
LOGS=$(ls "$T/logs"/net_*.log 2>/dev/null | head -1)
|
||
[ -n "$LOGS" ] && { PASS=$((PASS+1)); echo "PASS T7a log file: $(basename "$LOGS")"; } || { FAIL=$((FAIL+1)); echo "FAIL T7a 无日志文件"; }
|
||
[ -n "$LOGS" ] && { grep -q "iface add kind=wifi name=wlan0" "$LOGS" && { PASS=$((PASS+1)); echo "PASS T7b iface add 已记"; } || { FAIL=$((FAIL+1)); echo "FAIL T7b 未记 iface add"; }; }
|
||
|
||
# ---- T8: 守护停 → CLI help 本地可用;status 报守护不可达 ----
|
||
kill $DPID 2>/dev/null; wait $DPID 2>/dev/null; DPID=""
|
||
sleep 0.5
|
||
OUT=$(env $ENVS $Q "$T/meow_net" help 2>&1)
|
||
ck T8a "usage: meow_net" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T8b "守护不可达" "$OUT"
|
||
|
||
# ---- T9: 无参 → help;未知子命令 → usage ----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" 2>&1)
|
||
ck T9a "usage: meow_net" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bogus 2>&1); RC=$?
|
||
ck T9b "usage: meow_net" "$OUT"
|
||
|
||
# ---- 重启 daemon A(T8 已停)供期2 观测/连接表/日志测试 ----
|
||
env $ENVS $Q "$BIN" --sock "$T/net.sock" >"$T/daemon2.log" 2>&1 &
|
||
DPID=$!
|
||
for i in $(seq 1 30); do [ -S "$T/net.sock" ] && break; sleep 0.2; done
|
||
[ -S "$T/net.sock" ] || { echo "FAIL T10-pre: daemon 未重启"; cat "$T/daemon2.log"; exit 1; }
|
||
|
||
# ---- T10: 期2 conns(读级;观测层经 MEOW_TCP/UDP 桩 + amsd/svcd 缺省 → proc:unknown 归因)----
|
||
sleep 1 # 等守护普查一拍
|
||
OUT=$(env $ENVS $Q "$T/meow_net" conns 2>&1)
|
||
ck T10a "proc:unknown" "$OUT"
|
||
ck T10b "10.78.193.50:20791" "$OUT"
|
||
ck T10b2 "140.205.5.9:443" "$OUT"
|
||
ck T10c "ESTAB" "$OUT"
|
||
ck T10d "UDP" "$OUT"
|
||
ck T10e "connection" "$OUT"
|
||
# ---- T11: apps(实体聚合)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" apps 2>&1)
|
||
ck T11a "entity" "$OUT"
|
||
ck T11b "proc:unknown" "$OUT"
|
||
# ---- T12: dns status/stats(DNS 禁用 → 显示 disabled)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" dns status 2>&1)
|
||
ck T12a "proxy disabled" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" dns stats 2>&1)
|
||
ck T12b "proxy disabled" "$OUT"
|
||
# ---- T13: dns flush(写级;宿主 CLI=CALL_OTHER → 拒)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" dns flush 2>&1)
|
||
ck T13a "permission denied" "$OUT"
|
||
|
||
# ---- T14: DNS 代理 wire 实测(守护 B:高端口 + 桩上游 + 全局 deny;测 deny/forward/cache)----
|
||
HOSTIP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||
[ -n "$HOSTIP" ] || HOSTIP="127.0.0.1"
|
||
cat > "$T/policy_dns" <<'EOF'
|
||
global domain=-ads.example.com
|
||
EOF
|
||
printf 'nameserver %s\n' "$HOSTIP" > "$T/resolv_dns"
|
||
cat > "$T/dns_test.py" <<'PYEOF'
|
||
import socket, struct, threading, sys, os
|
||
UP_IP = os.environ.get("T_UP_IP", "0.0.0.0")
|
||
up = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
up.bind((UP_IP, 5311)) # 0.0.0.0 通配,绑到宿主自身 IP
|
||
up.settimeout(6)
|
||
def serve():
|
||
try:
|
||
while True:
|
||
d,a = up.recvfrom(1536)
|
||
off=12
|
||
while d[off]!=0: off += 1+d[off]
|
||
off += 1+4
|
||
qsec = d[12:off]
|
||
resp = d[:2] + b'\x81\x80' + b'\x00\x01\x00\x01\x00\x00\x00\x00' + qsec
|
||
resp += b'\xc0\x0c' + b'\x00\x01\x00\x01' + struct.pack('>I',60) + b'\x00\x04' + socket.inet_aton('11.22.33.44')
|
||
up.sendto(resp, a)
|
||
except Exception:
|
||
pass
|
||
threading.Thread(target=serve, daemon=True).start()
|
||
def query(name):
|
||
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.settimeout(3)
|
||
hdr=b'\x12\x34'+b'\x01\x00'+b'\x00\x01\x00\x00\x00\x00\x00\x00'
|
||
q=b''.join(bytes([len(l)])+l.encode() for l in name.split('.'))+b'\x00'+b'\x00\x01\x00\x01'
|
||
s.sendto(hdr+q, ("127.0.0.1", 5300))
|
||
r,_=s.recvfrom(1536); rco=r[3]&0xf; an=struct.unpack('>H',r[6:8])[0]
|
||
return rco,an
|
||
try:
|
||
d1,a1 = query("ads.example.com") # deny → NXDOMAIN (rcode 3)
|
||
d2,a2 = query("music.163.com") # allow → forward → A 11.22.33.44
|
||
sys.stdout.write("DENY_RCODE=%d\nALLOW_AN=%d\n" % (d1, a2))
|
||
except Exception as e:
|
||
sys.stdout.write("ERR=%s\n" % e)
|
||
PYEOF
|
||
# 追加式覆写(env 左→右,后者胜出):DNS 启用、换独立 socket/resolv/policy、上游端口。
|
||
DNSENV="$ENVS MEOW_DNS_DISABLE=0 MEOW_NET_SOCK=$T/dns.sock MEOW_RESOLV=$T/resolv_dns
|
||
MEOW_NET_POLICY=$T/policy_dns MEOW_DNS_PORT=5300 MEOW_DNS_UPSTREAM_PORT=5311"
|
||
ln -sf "$(readlink -f "$BIN")" "$T/dns_netmgrd"
|
||
env $DNSENV $Q "$BIN" --sock "$T/dns.sock" >"$T/dnsd.log" 2>&1 &
|
||
D2PID=$!
|
||
for i in $(seq 1 30); do [ -S "$T/dns.sock" ] && break; sleep 0.2; done
|
||
sleep 1
|
||
OUT=$(env $DNSENV $Q "$T/meow_net" dns status 2>&1)
|
||
ck T14a "enabled=1 upstream=$HOSTIP" "$OUT"
|
||
WIRE=$(python3 "$T/dns_test.py" 2>&1)
|
||
ck T14b "DENY_RCODE=3" "$WIRE"
|
||
ck T14c "ALLOW_AN=1" "$WIRE"
|
||
OUT=$(env $DNSENV $Q "$T/meow_net" dns stats 2>&1)
|
||
ck T14d "q=2" "$OUT"
|
||
ck T14e "deny=1" "$OUT"
|
||
kill $D2PID 2>/dev/null; wait $D2PID 2>/dev/null
|
||
# 恢复守护 A 的 MEOW_RESOLV(其未被 DNS 触碰,此处确认)
|
||
grep -q "nameserver 223.5.5.5" "$T/resolv" && { PASS=$((PASS+1)); echo "PASS T14f daemonA resolv untouched"; } || { FAIL=$((FAIL+1)); echo "FAIL T14f daemonA resolv touched"; }
|
||
|
||
echo "=============================="
|
||
echo "PASS=$PASS FAIL=$FAIL"
|
||
[ $FAIL -eq 0 ]
|