meow_netmgrd 期4:SDK 自愿通道 + wifi forget + RSSI signal_poll + AAAA 过滤 + iptables 评估
NETMGR_DESIGN.md §7/§13 期4 落地(只动 patch_v2/meow_netmgrd/ + patch_v2/sdk/ 追加式): 1. SDK 自愿通道(§6.3/§4):meow_ui.h 新增 meow_net_policy_check(host)(0=放行 1=block; netmgrd 不在/超时→0 乐观放行)+ meow_net_report(rx,tx)(自愿上报,§4 字节级记账自报通道)。 meow_ipc.h 追加 0xEE MSG_NET_SDK(op=CHECK/REPORT)/ 0xEF RESULT。netmgrd 侧:CHECK 以 app_id→实体 app:<pkg> 评估 policy_net_blocked||dns_query_domain_denied;REPORT 记入 per-实体 g_ent_bytes(status JSON 新增 entity_bytes)。SDK 实现 meow_net_ipc.c(一次 connect+send+read, 降级不崩);meow_ui.a 重编。 2. wifi forget <id>:wpa_cli remove_network+save_config(MeowOS;HibyOS 只读门控),CLI+IPC 接通 (286 launcher 录入 UI 缺口)。 3. RSSI:wifi_status 在 status 无 signal= 时回退 wpa_cli signal_poll(真机 rssi=-38,修面板 rssi=null 缺口)。 4. DNS AAAA 过滤:global dns.filter_aaaa=0|1(默认 0 透传;纯 IPv4 设备可置 1 让 AAAA 查询回空答案回落 A)。 5. 静态 iptables 评估(devdocs/plan/NETMGR_IPTABLES_EVAL.md,本机不入库):iptables-legacy 1.8.9 交叉 编译成功(~578KB,含 owner/cgroup/REDIRECT 等全部 extension),但内核实测 matches 仅 quota/icmp/udp/tcp、targets 仅 NFQUEUE/NFLOG/ERROR、无 nat 表、全部进程 UID 0 → **per-app 管控 与 REDIRECT 透明代理判不可行**(与 NETWORK_HW_FACTS 一致);正路为 per-app UID 框架改造(单独立项)。 验证:宿主 test_netmgrd.sh 70/70 + qemu 70/70;test_netmgrd_p2/p3/p5 宿主全过 + 交叉 0 warning; netmgrd_authtest 全过;sdk meow_net_ipc 交叉 0 warning。真机(固件 netmgrd 共存,独立 socket+DNS off): wifi status rssi=-38(signal_poll)、wifi forget 0(DISABLED 安全移除,id1 CURRENT 保留、wifi 未断)、 SDK policy check(CHECK_GOOD=0/CHECK_BLOCK=1/REPORT ok)、policy show 正常;resolv.conf 未触碰、 固件 meow_netmgrd(pid) running 未动;测试产物全清。 新 md5:meow_netmgrd 20243ea1a800b29badefebb395603aaf;meow_ui.a be24214e24faadba63577f3a98c2c9d1 (主会话更 fw_content.conf / 热更设备 / 记录新 meow_ui.a)。
This commit is contained in:
1 parent
4e0e7441fd
commit
8a43f17653
17 files changed
+563
-16
No files matched your search
Binary file not shown.
@@ -327,6 +327,9 @@ typedef struct {
|
||||
long rx, tx; /* 累计字节(/proc/net/dev) */
|
||||
} iface_t;
|
||||
static iface_t g_ifaces[IFACE_MAX];
|
||||
typedef struct { char entity[48]; uint32_t rx, tx; } ent_bytes_t;
|
||||
static ent_bytes_t g_ent_bytes[ENTITY_MAX]; /* SDK 自愿上报(§4 字节级记账自报通道;声明须在 build_status_json 前) */
|
||||
static int g_ent_bytes_n = 0;
|
||||
static int g_iface_n = 0;
|
||||
|
||||
typedef struct {
|
||||
@@ -592,6 +595,15 @@ static void wifi_status(wifi_t *w)
|
||||
if (!nl) break;
|
||||
ln = nl + 1;
|
||||
}
|
||||
/* 期4:status 无 signal=(本机 wpa_cli status 不输出)→ 回退 wpa_cli signal_poll(RSSI=<val>)。 */
|
||||
if (!w->has_rssi) {
|
||||
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s signal_poll 2>/dev/null", g_wpa_cli);
|
||||
char sbuf[256];
|
||||
if (run_cmd(sbuf, sizeof sbuf, RUNCMD_TMO_MS, cmd) > 0) {
|
||||
char *s = strstr(sbuf, "RSSI=");
|
||||
if (s) { w->rssi = atoi(s + 5); w->has_rssi = 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- 蓝牙:hciconfig/bluetoothctl 包装 ---------------- */
|
||||
@@ -896,8 +908,17 @@ static void build_status_json(char *out, size_t cap)
|
||||
if (g_net_rate_rx < 0) jadd(&w, "null,");
|
||||
else jadd(&w, "%ld,", g_net_rate_rx);
|
||||
jadd(&w, "\"tx_bps\":");
|
||||
if (g_net_rate_tx < 0) jadd(&w, "null}}");
|
||||
else jadd(&w, "%ld}}", g_net_rate_tx);
|
||||
if (g_net_rate_tx < 0) jadd(&w, "null},"); /* rate object 闭合 + 逗号(tx_bps 为 rate 末字段) */
|
||||
else jadd(&w, "%ld},", g_net_rate_tx);
|
||||
|
||||
/* 期4:SDK 自愿上报的 per-实体字节统计(§4 字节级记账自报通道);空则 []。 */
|
||||
jadd(&w, "\"entity_bytes\":[");
|
||||
for (int i = 0; i < g_ent_bytes_n; i++) {
|
||||
if (i) jadd(&w, ",");
|
||||
jadd(&w, "{\"entity\":\"%s\",\"rx\":%u,\"tx\":%u}", g_ent_bytes[i].entity,
|
||||
g_ent_bytes[i].rx, g_ent_bytes[i].tx);
|
||||
}
|
||||
jadd(&w, "]}");
|
||||
}
|
||||
|
||||
/* 单/全接口详情 JSON。name 空=全部(数组);否则单对象。返回 MEOW_OK / MEOW_ERR_NOENT。 */
|
||||
@@ -975,6 +996,7 @@ typedef struct {
|
||||
static rule_t g_rules[RULE_ENT_MAX];
|
||||
static int g_rule_n = 0;
|
||||
static char g_policy_log_level[8] = "info"; /* global log_level= */
|
||||
static int g_dns_filter_aaaa = 0; /* global dns.filter_aaaa=:0=透传 AAAA(默认),1=过滤(纯 IPv4 设备弱化 IPv6 优先) */
|
||||
|
||||
/* ---- 期3 防自锁清单(NETMGR_DESIGN §14 红线):这些实体拒绝/强制置 allow,防断网变砖。 ---- */
|
||||
static int _is_selflock_by_name(const char *name)
|
||||
@@ -1185,7 +1207,11 @@ static void rules_load(void)
|
||||
*sp = 0;
|
||||
char *entity = p, *rest = sp + 1;
|
||||
if (strcmp(entity, "global") == 0) {
|
||||
/* global log_level=<lvl> */
|
||||
/* global log_level=<lvl> dns.filter_aaaa=0|1
|
||||
* 注意:log_level 解析会在 rest 中部写 NUL 截断,故 dns.filter_aaaa 必须先解析。 */
|
||||
char *fa = strstr(rest, "dns.filter_aaaa=");
|
||||
if (fa) { char *v = fa + 16; while (*v==' '||*v=='\t') v++;
|
||||
g_dns_filter_aaaa = atoi(v) ? 1 : 0; }
|
||||
char *lv = strstr(rest, "log_level=");
|
||||
if (lv) { char *v = lv + 10; while (*v==' '||*v=='\t'||*v=='"') v++;
|
||||
char *e2 = v; while (*e2 && *e2!=' ' && *e2!='\t' && *e2!='\n') e2++; *e2=0;
|
||||
@@ -1294,7 +1320,8 @@ static void policy_persist_entity(const char *entity)
|
||||
}
|
||||
/* 追加本实体当前态 */
|
||||
if (strcmp(entity, "global") == 0) {
|
||||
int l = snprintf(out + o, sizeof out - o, "global log_level=%s\n", g_policy_log_level);
|
||||
int l = snprintf(out + o, sizeof out - o, "global log_level=%s dns.filter_aaaa=%d\n",
|
||||
g_policy_log_level, g_dns_filter_aaaa);
|
||||
if (l > 0 && o + (size_t)l < sizeof out) o += (size_t)l;
|
||||
} else {
|
||||
rule_t *r = policy_find(entity);
|
||||
@@ -1684,7 +1711,7 @@ static void dns_proxy_stop(void)
|
||||
svc_log("dns proxy off");
|
||||
}
|
||||
|
||||
static int dns_parse_query(const uint8_t *q, int qlen, char *domain, int domcap, int *qend)
|
||||
static int dns_parse_query(const uint8_t *q, int qlen, char *domain, int domcap, int *qend, int *qtype)
|
||||
{
|
||||
if (qlen < 12) return -1;
|
||||
if (q[2] & 0x80) return -1; /* 不是 query 位 */
|
||||
@@ -1703,6 +1730,7 @@ static int dns_parse_query(const uint8_t *q, int qlen, char *domain, int domcap,
|
||||
}
|
||||
if (off + 4 > qlen) return -1;
|
||||
domain[di] = 0;
|
||||
if (qtype) *qtype = (q[off] << 8) | q[off + 1]; /* QTYPE:1=A 28=AAAA */
|
||||
off += 4; /* qtype+qclass */
|
||||
*qend = off;
|
||||
return di > 0 ? 0 : -1;
|
||||
@@ -1711,7 +1739,7 @@ static int dns_build_nxdomain(const uint8_t *q, int qlen, uint8_t *out, int outc
|
||||
{
|
||||
int qend = 0;
|
||||
char dom[DNS_QNAME_MAX];
|
||||
if (dns_parse_query(q, qlen, dom, sizeof dom, &qend) != 0) return -1;
|
||||
if (dns_parse_query(q, qlen, dom, sizeof dom, &qend, NULL) != 0) return -1;
|
||||
int qsec = qend - 12;
|
||||
if (12 + qsec > outcap) return -1;
|
||||
memcpy(out, q, 12);
|
||||
@@ -1783,8 +1811,8 @@ static void dns_handle_packet(void)
|
||||
if (n < 12) return;
|
||||
g_dns_q++;
|
||||
char dom[DNS_QNAME_MAX];
|
||||
int qend = 0;
|
||||
if (dns_parse_query(q, (int)n, dom, sizeof dom, &qend) != 0 || !dom[0]) return;
|
||||
int qend = 0, qtype = 0;
|
||||
if (dns_parse_query(q, (int)n, dom, sizeof dom, &qend, &qtype) != 0 || !dom[0]) return;
|
||||
const char *entity = dns_entity_for_port((uint16_t)ntohs(from.sin_port));
|
||||
/* 域名规则:deny → NXDOMAIN + 日志 */
|
||||
if (dns_query_domain_denied(entity, dom)) {
|
||||
@@ -1795,6 +1823,21 @@ static void dns_handle_packet(void)
|
||||
if (rl > 0) sendto(g_dns_fd, resp, (size_t)rl, 0, (struct sockaddr *)&from, fl);
|
||||
return;
|
||||
}
|
||||
/* 期4 AAAA 过滤(dns.filter_aaaa=1,纯 IPv4 设备弱化 IPv6 优先):回「无 AAAA」空应答 → 客户端回落 A。 */
|
||||
if (g_dns_filter_aaaa && qtype == 28) {
|
||||
int qsec = qend - 12;
|
||||
if (qsec > 0 && 12 + qsec <= (int)sizeof q) {
|
||||
uint8_t resp[1536];
|
||||
memcpy(resp, q, 12);
|
||||
resp[2] = 0x81; resp[3] = 0x80; /* QR|RD|RA, rcode=0 */
|
||||
resp[4] = 0; resp[5] = 1; /* qdcount=1 */
|
||||
resp[6] = 0; resp[7] = 0; resp[8] = 0; resp[9] = 0; resp[10] = 0; resp[11] = 0;
|
||||
memcpy(resp + 12, q + 12, (size_t)qsec);
|
||||
sendto(g_dns_fd, resp, (size_t)(12 + qsec), 0, (struct sockaddr *)&from, fl);
|
||||
net_log("info", "dns", "aaaa filtered entity=%s domain=%s", entity, dom);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* 只记新域名首见(全量 debug 级,防日志爆炸) */
|
||||
int cached = dns_cache_lookup_name(dom);
|
||||
if (cached != 0) { g_dns_first++; net_log("info", "dns", "first entity=%s domain=%s", entity, dom); }
|
||||
@@ -2257,6 +2300,22 @@ static void wifi_ctl_execute(const char *cmd, const char *arg, ctl_result_t *r)
|
||||
ctl_sets(r, n >= 0 ? MEOW_OK : MEOW_ERR_NODEV, "wifi disconnect: 已下发");
|
||||
} else if (!strcmp(cmd, "on") || !strcmp(cmd, "off")) {
|
||||
wifi_onoff(cmd, r);
|
||||
} else if (!strcmp(cmd, "forget")) {
|
||||
int nid = arg ? atoi(arg) : -1;
|
||||
if (nid < 0) { ctl_sets(r, MEOW_ERR_INVAL, "wifi forget 需 <network_id>"); return; }
|
||||
char cmd2[192];
|
||||
snprintf(cmd2, sizeof cmd2, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s remove_network %d 2>/dev/null",
|
||||
g_wpa_cli, nid);
|
||||
char buf[128];
|
||||
int n = run_cmd(buf, sizeof buf, RUNCMD_TMO_MS, cmd2);
|
||||
/* 期4:移除后持久化(MeowOS 模式;HibyOS 只读由服务端门控)。 */
|
||||
if (g_mode_meow) {
|
||||
snprintf(cmd2, sizeof cmd2, "PATH=/bin:/sbin:/usr/bin:/usr/sbin %s save_config 2>/dev/null", g_wpa_cli);
|
||||
run_cmd(buf, sizeof buf, RUNCMD_TMO_MS, cmd2);
|
||||
}
|
||||
char msg[64];
|
||||
snprintf(msg, sizeof msg, "wifi forget: network_id=%d 已移除%s", nid, g_mode_meow ? "+save_config" : "");
|
||||
ctl_sets(r, n >= 0 ? MEOW_OK : MEOW_ERR_NODEV, msg);
|
||||
} else if (!strcmp(cmd, "rfkill")) {
|
||||
rfkill_ctl(arg, r);
|
||||
} else if (!strcmp(cmd, "udhcpc")) {
|
||||
@@ -2576,7 +2635,57 @@ static void handle_conn(int c)
|
||||
send_frame(c, MSG_NET_POLICY_RESULT, req_id, &res, (uint32_t)(8 + tl + 1));
|
||||
continue;
|
||||
}
|
||||
send_err(c, req_id, MEOW_ERR_INVAL, "unknown type (期1 STATUS/IFACE/WIFI_CTL/BT_CTL; 期2 CONNS/DNS_CTL; 期3 POLICY)");
|
||||
if (type == MSG_NET_SDK) {
|
||||
m_net_sdk_t q;
|
||||
memset(&q, 0, sizeof q);
|
||||
if (rlen > sizeof q) rlen = sizeof q;
|
||||
memcpy(&q, payload, rlen);
|
||||
m_net_sdk_result_t res;
|
||||
memset(&res, 0, sizeof res);
|
||||
char ent[48];
|
||||
snprintf(ent, sizeof ent, "app:%s", q.app_id[0] ? q.app_id : "unknown");
|
||||
if (q.op == NET_SDK_CHECK) {
|
||||
int blocked = policy_net_blocked(ent) || dns_query_domain_denied(ent, q.host);
|
||||
res.allow = blocked ? 0 : 1;
|
||||
net_log("info", "sdk", "check app=%s host=%s -> %s", q.app_id, q.host, blocked ? "block" : "allow");
|
||||
snprintf(res.text, sizeof res.text, "sdk check app=%s host=%s %s\n", q.app_id, q.host,
|
||||
blocked ? "block" : "allow");
|
||||
res.code = MEOW_OK;
|
||||
} else if (q.op == NET_SDK_REPORT) {
|
||||
/* 自愿字节上报 → per-实体统计(§4 字节级记账自报)。 */
|
||||
for (int i = 0; i < g_ent_bytes_n; i++)
|
||||
if (strcmp(g_ent_bytes[i].entity, ent) == 0) {
|
||||
g_ent_bytes[i].rx += q.rx; g_ent_bytes[i].tx += q.tx;
|
||||
res.code = MEOW_OK;
|
||||
snprintf(res.text, sizeof res.text, "sdk report app=%s rx=%u tx=%u (累计 %u/%u)\n",
|
||||
q.app_id, q.rx, q.tx, g_ent_bytes[i].rx, g_ent_bytes[i].tx);
|
||||
goto sdk_send;
|
||||
}
|
||||
if (g_ent_bytes_n < ENTITY_MAX) {
|
||||
snprintf(g_ent_bytes[g_ent_bytes_n].entity, sizeof g_ent_bytes[g_ent_bytes_n].entity, "%s", ent);
|
||||
g_ent_bytes[g_ent_bytes_n].rx = q.rx;
|
||||
g_ent_bytes[g_ent_bytes_n].tx = q.tx;
|
||||
g_ent_bytes_n++;
|
||||
res.code = MEOW_OK;
|
||||
snprintf(res.text, sizeof res.text, "sdk report app=%s rx=%u tx=%u\n", q.app_id, q.rx, q.tx);
|
||||
} else {
|
||||
res.code = MEOW_ERR_NOMEM;
|
||||
snprintf(res.text, sizeof res.text, "sdk report: entity 槽满\n");
|
||||
}
|
||||
} else {
|
||||
res.code = MEOW_ERR_INVAL;
|
||||
snprintf(res.text, sizeof res.text, "sdk: 未知 op\n");
|
||||
}
|
||||
sdk_send:
|
||||
{
|
||||
size_t tl = strlen(res.text);
|
||||
if (tl > sizeof res.text - 1) tl = sizeof res.text - 1;
|
||||
res.text_len = (uint16_t)tl;
|
||||
send_frame(c, MSG_NET_SDK_RESULT, req_id, &res, (uint32_t)(12 + tl + 1));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
send_err(c, req_id, MEOW_ERR_INVAL, "unknown type (期1-4: STATUS/IFACE/WIFI_CTL/BT_CTL/CONNS/DNS/POLICY/SDK)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3070,6 +3179,10 @@ static int cli_run(int n, char **v)
|
||||
if (!arg2) { fprintf(stderr, "connect 需 <ssid>[,psk]\n"); return 1; }
|
||||
return cli_cmd_result(MSG_NET_WIFI_CTL, "connect", arg2);
|
||||
}
|
||||
if (!strcmp(arg, "forget")) {
|
||||
if (!arg2) { fprintf(stderr, "forget 需 <network_id>\n"); return 1; }
|
||||
return cli_cmd_result(MSG_NET_WIFI_CTL, "forget", arg2);
|
||||
}
|
||||
return cli_cmd_result(MSG_NET_WIFI_CTL, arg, NULL);
|
||||
}
|
||||
if (!strcmp(cmd, "bt")) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -252,11 +252,56 @@ ck T16e "permission denied" "$OUT"
|
||||
OUT=$(env $ENVS $Q "$T/meow_net" policy del app:dev.player.ncm 2>&1)
|
||||
ck T16f "permission denied" "$OUT"
|
||||
|
||||
# ---- T17: SDK 自愿通道 end-to-end(meow_net_policy_check/report 对守护 A)----
|
||||
SDK_BIN="$T/test_net_ipc"
|
||||
if [ -n "$Q" ]; then
|
||||
"$HOME/Project/R3ProII/.tools/zig-mipsel-cc" -Os -Wall -Wextra -I../sdk -I../sdk/include -I../meow_launcherd \
|
||||
../sdk/test_net_ipc.c ../sdk/meow_net_ipc.c -o "$SDK_BIN" 2>/dev/null
|
||||
else
|
||||
cc -Os -Wall -Wextra -I../sdk -I../sdk/include -I../meow_launcherd \
|
||||
../sdk/test_net_ipc.c ../sdk/meow_net_ipc.c -o "$SDK_BIN" 2>/dev/null
|
||||
fi
|
||||
if [ -x "$SDK_BIN" ]; then
|
||||
OUT=$(env MEOW_LOG_APP=dev.player.ncm MEOW_NET_SOCK="$T/net.sock" $Q "$SDK_BIN" 2>&1)
|
||||
ck T17a "CHECK_GOOD=0" "$OUT" # music.163.com 放行
|
||||
ck T17b "CHECK_BLOCK=1" "$OUT" # ads.example.com 被策略 deny → block
|
||||
ck T17c "REPORT_DONE" "$OUT"
|
||||
else
|
||||
FAIL=$((FAIL+1)); echo "FAIL T17 SDK test 构建失败"
|
||||
fi
|
||||
|
||||
# ---- T18: SDK report → status JSON entity_bytes 回读 ----
|
||||
JOUT2=$(python3 - "$T/net.sock" <<'PYEOF'
|
||||
import socket,struct,sys,json
|
||||
s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.connect(sys.argv[1]); s.settimeout(2)
|
||||
def recvn(n):
|
||||
d=b""
|
||||
while len(d)<n:
|
||||
c=s.recv(n-len(d))
|
||||
if not c: return d
|
||||
d+=c
|
||||
return d
|
||||
s.sendall(struct.pack('<IHHII',0x4D4F5701,1,0x00E0,1,0))
|
||||
H=struct.unpack('<IHHII', recvn(16)); body=recvn(H[4])
|
||||
code,jlen=struct.unpack('<IH',body[:6]); js=body[8:8+jlen].decode('utf-8','replace')
|
||||
o=json.loads(js)
|
||||
eb=o.get("entity_bytes") or []
|
||||
hit=[e for e in eb if e["entity"]=="app:dev.player.ncm"]
|
||||
sys.stdout.write("EB_HIT=%d RX=%d TX=%d\n" % (len(hit), hit[0]["rx"] if hit else -1, hit[0]["tx"] if hit else -1))
|
||||
PYEOF
|
||||
)
|
||||
ck T18a "EB_HIT=1" "$JOUT2"
|
||||
ck T18b "RX=12345" "$JOUT2"
|
||||
ck T18c "TX=67890" "$JOUT2"
|
||||
# ---- RSSI 回读:status wifi rssi 应 >=0(signal_poll 桩已给)----
|
||||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||||
ck T18d "rssi=-45" "$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
|
||||
global domain=-ads.example.com dns.filter_aaaa=1
|
||||
EOF
|
||||
printf 'nameserver %s\n' "$HOSTIP" > "$T/resolv_dns"
|
||||
cat > "$T/dns_test.py" <<'PYEOF'
|
||||
@@ -279,17 +324,18 @@ def serve():
|
||||
except Exception:
|
||||
pass
|
||||
threading.Thread(target=serve, daemon=True).start()
|
||||
def query(name):
|
||||
def query(name, qtype=1):
|
||||
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'
|
||||
q=b''.join(bytes([len(l)])+l.encode() for l in name.split('.'))+b'\x00'+struct.pack('>HH',qtype,1)
|
||||
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))
|
||||
da,aa = query("music.163.com", 28) # AAAA + dns.filter_aaaa=1 → 空应答(回落 A)
|
||||
sys.stdout.write("DENY_RCODE=%d\nALLOW_AN=%d\nAAAA_RCODE=%d AAAA_AN=%d\n" % (d1, a2, da, aa))
|
||||
except Exception as e:
|
||||
sys.stdout.write("ERR=%s\n" % e)
|
||||
PYEOF
|
||||
@@ -306,8 +352,10 @@ 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"
|
||||
ck T14c2 "AAAA_RCODE=0" "$WIRE" # 期4 AAAA 过滤:rcode 0
|
||||
ck T14c3 "AAAA_AN=0" "$WIRE" # 且 0 条应答(回落 A)
|
||||
OUT=$(env $DNSENV $Q "$T/meow_net" dns stats 2>&1)
|
||||
ck T14d "q=2" "$OUT"
|
||||
ck T14d "q=3" "$OUT" # deny + allow + AAAA = 3 查询
|
||||
ck T14e "deny=1" "$OUT"
|
||||
kill $D2PID 2>/dev/null; wait $D2PID 2>/dev/null
|
||||
# 恢复守护 A 的 MEOW_RESOLV(其未被 DNS 触碰,此处确认)
|
||||
|
||||
Binary file not shown.
@@ -80,7 +80,7 @@ int main(void)
|
||||
{
|
||||
int ql = make_query("music.163.com", q);
|
||||
char dom[DNS_QNAME_MAX]; int qend = 0;
|
||||
CHECK(dns_parse_query(q, ql, dom, sizeof dom, &qend) == 0);
|
||||
CHECK(dns_parse_query(q, ql, dom, sizeof dom, &qend, NULL) == 0);
|
||||
CHECK(strcmp(dom, "music.163.com") == 0);
|
||||
CHECK(qend == ql);
|
||||
}
|
||||
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,110 @@
|
||||
/* test_netmgrd_p5.c — meow_netmgrd 期4 纯逻辑宿主自测(非生产代码)。
|
||||
*
|
||||
* 通过 #include 生产源,同一 TU 直接访问 FILE-STATIC:wifi_status / dns_parse_query /
|
||||
* rules_load / wifi_ctl_execute / g_dns_filter_aaaa / g_policy_log_level / policy_cmd。
|
||||
* 覆盖:①wifi status 缺 signal= 时回退 signal_poll 取 RSSI(面板 rssi=null 缺口修复)
|
||||
* ②dns_parse_query 解析 QTYPE(A=1/AAAA=28,供 AAAA 过滤)③global dns.filter_aaaa 配置加载
|
||||
* ④wifi forget(remove_network + save_config,MeowOS)⑤策略引擎回归(net/domain/防自锁)。
|
||||
*
|
||||
* 用法:cc -Wall -Wextra -I../meow_launcherd test_netmgrd_p5.c -o test_netmgrd_p5 && ./test_netmgrd_p5
|
||||
*/
|
||||
#define main meow_netmgrd_prod_main_unused
|
||||
#include "meow_netmgrd.c"
|
||||
#undef main
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if (!(cond)) { printf(" FAIL: %s (line %d)\n", #cond, __LINE__); g_fail++; } \
|
||||
else { printf(" ok : %s\n", #cond); } } while (0)
|
||||
static void wf(const char *p, const char *s) { FILE *f=fopen(p,"w"); if(f){fputs(s,f);fclose(f);} }
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static const char *T = "/tmp/nmg_p5";
|
||||
char path[256];
|
||||
mkdir(T, 0755);
|
||||
|
||||
printf("== wifi_status:status 无 signal= 回退 signal_poll ==\n");
|
||||
snprintf(path, sizeof path, "%s/wpa_st", T);
|
||||
wf(path, "#!/bin/sh\n"
|
||||
"if [ \"$1\" = \"status\" ]; then echo 'wpa_state=COMPLETED'; echo 'ssid=MyWiFi'; echo 'ip_address=10.78.193.50'; fi\n"
|
||||
"if [ \"$1\" = \"signal_poll\" ]; then echo 'RSSI=-56'; echo 'LINKSPEED=54'; fi\n"
|
||||
"exit 0\n");
|
||||
chmod(path, 0755);
|
||||
g_wpa_cli = ""; { static char w[192]; snprintf(w,sizeof w,"%s",path); g_wpa_cli=w; }
|
||||
wifi_t w;
|
||||
wifi_status(&w);
|
||||
CHECK(w.has_rssi == 1);
|
||||
CHECK(w.rssi == -56);
|
||||
CHECK(w.connected == 1 && strcmp(w.ssid, "MyWiFi") == 0);
|
||||
|
||||
printf("== dns_parse_query:QTYPE 解析 ==\n");
|
||||
{
|
||||
uint8_t q[256];
|
||||
int off = 12;
|
||||
const char *name = "music.163.com";
|
||||
memset(q,0,sizeof q); q[2]=0x01; q[4]=0; q[5]=1; /* RD=1 qdcount=1 */
|
||||
for (const char *p=name; *p;) {
|
||||
const char *d = strchr(p, '.');
|
||||
size_t l = d ? (size_t)(d - p) : strlen(p);
|
||||
q[off++] = (uint8_t)l; memcpy(q+off, p, l); off += (int)l;
|
||||
p = d ? d + 1 : p + l;
|
||||
}
|
||||
q[off++] = 0; /* qname 终止 */
|
||||
q[off++] = 0; q[off++] = 1; /* qtype A */
|
||||
q[off++] = 0; q[off++] = 1; /* qclass IN */
|
||||
char dom[DNS_QNAME_MAX]; int qend=0,qt=0;
|
||||
CHECK(dns_parse_query(q,off,dom,sizeof dom,&qend,&qt)==0);
|
||||
CHECK(strcmp(dom,"music.163.com")==0 && qt==1); /* A=1 */
|
||||
q[off-4] = 0; q[off-3] = 28; /* 改 QTYPE=AAAA */
|
||||
CHECK(dns_parse_query(q,off,dom,sizeof dom,&qend,&qt)==0 && qt==28);
|
||||
}
|
||||
|
||||
printf("== global dns.filter_aaaa 配置加载 + policy 回归 ==\n");
|
||||
snprintf(path, sizeof path, "%s/policy", T);
|
||||
wf(path, "global log_level=debug dns.filter_aaaa=1\nproc:thttpd net=block\n");
|
||||
g_net_policy[0]=0; snprintf(g_net_policy,sizeof g_net_policy,"%s",path);
|
||||
rules_load();
|
||||
CHECK(g_dns_filter_aaaa == 1);
|
||||
CHECK(strcmp(g_policy_log_level, "debug") == 0);
|
||||
CHECK(policy_net_blocked("proc:thttpd") == 1);
|
||||
CHECK(dns_query_domain_denied("proc:thttpd", "anything.com") == 1); /* net=block → DNS 全 deny */
|
||||
CHECK(is_self_lock("proc:dropbear") == 1);
|
||||
CHECK(policy_net_blocked("proc:dropbear") == 0);
|
||||
|
||||
printf("== wifi forget(remove_network + save_config)==\n");
|
||||
{
|
||||
char rm_path[192], sv_path[192];
|
||||
snprintf(rm_path, sizeof rm_path, "%s/rm.log", T);
|
||||
snprintf(sv_path, sizeof sv_path, "%s/sv.log", T);
|
||||
snprintf(path, sizeof path, "%s/wpa_fg", T);
|
||||
char script[512];
|
||||
snprintf(script, sizeof script,
|
||||
"#!/bin/sh\n"
|
||||
"if [ \"$1\" = \"remove_network\" ]; then echo \"$@\" > \"%s\"; fi\n"
|
||||
"if [ \"$1\" = \"save_config\" ]; then echo \"$@\" > \"%s\"; fi\n"
|
||||
"exit 0\n", rm_path, sv_path);
|
||||
wf(path, script);
|
||||
chmod(path, 0755);
|
||||
g_wpa_cli = ""; { static char w2[192]; snprintf(w2,sizeof w2,"%s",path); g_wpa_cli=w2; }
|
||||
g_mode_meow = 1;
|
||||
ctl_result_t r;
|
||||
wifi_ctl_execute("forget", "7", &r);
|
||||
CHECK(r.code == MEOW_OK);
|
||||
CHECK(strstr(r.text, "network_id=7") != NULL);
|
||||
CHECK(strstr(r.text, "save_config") != NULL);
|
||||
char buf[64]; read_file_txt(rm_path, buf, sizeof buf);
|
||||
CHECK(strstr(buf, "remove_network 7") != NULL);
|
||||
read_file_txt(sv_path, buf, sizeof buf);
|
||||
CHECK(strstr(buf, "save_config") != NULL);
|
||||
}
|
||||
|
||||
if (g_fail) { printf("\nNETMGR P5 TEST: %d FAIL\n", g_fail); return 1; }
|
||||
printf("\nNETMGR P5 TEST: ALL PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user