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)。
214 lines
9.5 KiB
C
214 lines
9.5 KiB
C
/* test_netmgrd_p2.c — meow_netmgrd 期2 观测层/DNS 代理 纯逻辑宿主自测(本程序非生产代码)。
|
||
*
|
||
* 通过 #include 生产源文件(meow_netmgrd.c,用 #define main 改名避免入口冲突),同一 TU 直接访问
|
||
* FILE-STATIC 函数/全局:dns_parse_query / dns_build_nxdomain / dns_cache_* / suffix_match /
|
||
* rules_load / dns_query_domain_denied / pid_ent_put / entity_for_pid / parse_proc_net /
|
||
* build_conns_text / build_dns_ctl_text / dns_cache_from_response / hex_ip / ip_fmt。
|
||
* 覆盖 DNS wire 编解码、缓存 TTL、域名规则判定、实体归因(app/svc/proc:comm)、/proc/net 解析、
|
||
* 连接表文本构建。真实 amsd/svcd socket + /proc/<pid>/fd 归因留真机验证。
|
||
*
|
||
* 用法:cc -Wall -Wextra -I../meow_launcherd test_netmgrd_p2.c -o test_netmgrd_p2 && ./test_netmgrd_p2
|
||
*/
|
||
#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); }
|
||
}
|
||
|
||
static int make_query(const char *name, uint8_t *out)
|
||
{
|
||
memset(out, 0, 16);
|
||
out[0] = 0x12; out[1] = 0x34; /* id */
|
||
out[2] = 0x01; out[3] = 0x00; /* RD */
|
||
out[4] = 0; out[5] = 1; /* qdcount=1 */
|
||
int off = 12;
|
||
const char *p = name;
|
||
while (*p) {
|
||
const char *dot = strchr(p, '.');
|
||
size_t l = dot ? (size_t)(dot - p) : strlen(p);
|
||
out[off++] = (uint8_t)l;
|
||
memcpy(out + off, p, l); off += (int)l;
|
||
p = dot ? dot + 1 : p + l;
|
||
}
|
||
out[off++] = 0;
|
||
out[off++] = 0; out[off++] = 1; /* qtype=A */
|
||
out[off++] = 0; out[off++] = 1; /* qclass=IN */
|
||
return off;
|
||
}
|
||
|
||
static int make_a_response(const char *name, const char *ip, uint8_t *out)
|
||
{
|
||
int ql = make_query(name, out);
|
||
out[0] = 0x12; out[1] = 0x34;
|
||
out[2] = 0x81; out[3] = 0x80; /* QR|RD|RA */
|
||
out[6] = 0; out[7] = 1; /* ancount=1 */
|
||
int off = ql;
|
||
out[off++] = 0xC0; out[off++] = 0x0C; /* answer name pointer → offset 12 (question) */
|
||
out[off++] = 0; out[off++] = 1; /* type A */
|
||
out[off++] = 0; out[off++] = 1; /* class IN */
|
||
out[off++] = 0; out[off++] = 0; out[off++] = 0; out[off++] = 0x3C; /* ttl=60 */
|
||
out[off++] = 0; out[off++] = 4; /* rdlen=4 */
|
||
int b0 = 0, b1 = 0, b2 = 0, b3 = 0;
|
||
sscanf(ip, "%d.%d.%d.%d", &b0, &b1, &b2, &b3);
|
||
out[off++] = (uint8_t)b0; out[off++] = (uint8_t)b1;
|
||
out[off++] = (uint8_t)b2; out[off++] = (uint8_t)b3;
|
||
return off;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
static const char *T = "/tmp/nmg_p2";
|
||
char path[192];
|
||
mkdir(T, 0755);
|
||
uint8_t q[256], r[512];
|
||
|
||
printf("== dns_parse_query ==\n");
|
||
{
|
||
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, NULL) == 0);
|
||
CHECK(strcmp(dom, "music.163.com") == 0);
|
||
CHECK(qend == ql);
|
||
}
|
||
printf("== dns_build_nxdomain ==\n");
|
||
{
|
||
int ql = make_query("ads.example.com", q);
|
||
uint8_t resp[300];
|
||
int rl = dns_build_nxdomain(q, ql, resp, sizeof resp);
|
||
CHECK(rl > 12);
|
||
CHECK((resp[2] & 0x80) != 0); /* QR */
|
||
CHECK((resp[3] & 0x0f) == 3); /* NXDOMAIN */
|
||
CHECK(resp[0] == 0x12 && resp[1] == 0x34); /* id echoed */
|
||
}
|
||
printf("== dns_cache insert/lookup/flush (TTL 尊重) ==\n");
|
||
{
|
||
dns_cache_flush();
|
||
CHECK(dns_cache_insert("music.163.com", 0x0A4EC132, 60) == 0);
|
||
char out[64];
|
||
CHECK(dns_cache_lookup_ip(0x0A4EC132, out, sizeof out) == 0);
|
||
CHECK(strcmp(out, "music.163.com") == 0);
|
||
CHECK(dns_cache_lookup_name("music.163.com") == 0);
|
||
CHECK(dns_cache_lookup_name("absent.com") != 0);
|
||
dns_cache_flush();
|
||
CHECK(dns_cache_lookup_name("music.163.com") != 0);
|
||
}
|
||
printf("== dns_cache_from_response(A 记录抓取)==\n");
|
||
{
|
||
dns_cache_flush();
|
||
int rl = make_a_response("music.163.com", "39.164.0.12", r);
|
||
dns_cache_from_response(r, rl);
|
||
char out[64];
|
||
CHECK(dns_cache_lookup_ip(0x27A4000C, out, sizeof out) == 0);
|
||
CHECK(strcmp(out, "music.163.com") == 0);
|
||
}
|
||
printf("== suffix_match / rules_load / dns_query_domain_denied ==\n");
|
||
{
|
||
CHECK(suffix_match("ads.example.com", "example.com") == 1);
|
||
CHECK(suffix_match("sub.ads.example.com", "ads.example.com") == 1);
|
||
CHECK(suffix_match("example.com", "ads.example.com") == 0);
|
||
snprintf(path, sizeof path, "%s/policy", T);
|
||
wf(path, "# test\napp:dev.player.ncm domain=-ads.example.com,-tracker.net\nproc:dropbear domain=+k,l\n\n");
|
||
g_net_policy[0] = 0; snprintf(g_net_policy, sizeof g_net_policy, "%s", path);
|
||
rules_load();
|
||
CHECK(g_rule_n >= 2);
|
||
CHECK(dns_query_domain_denied("app:dev.player.ncm", "ads.example.com") == 1);
|
||
CHECK(dns_query_domain_denied("app:dev.player.ncm", "sub.ads.example.com") == 1);
|
||
CHECK(dns_query_domain_denied("app:dev.player.ncm", "music.163.com") == 0);
|
||
CHECK(dns_query_domain_denied("proc:dropbear", "k") == 0); /* allow 覆盖 */
|
||
CHECK(dns_query_domain_denied("proc:dropbear", "l") == 1); /* deny */
|
||
CHECK(dns_query_domain_denied("proc:other", "ads.example.com") == 0); /* 无实体规则 → 默认 allow */
|
||
}
|
||
printf("== refresh_entity_maps(svcd 落盘状态文件 → svc: 实体)==\n");
|
||
{
|
||
snprintf(path, sizeof path, "%s/svcd.status", T);
|
||
wf(path, "# meow_svcd status\ncard_present=1 wifi_up=1 services=2\n"
|
||
"svc=meowpanel state=running pid=1038 restart=0 exit=0 trigger=boot backoff_ms=2000\n"
|
||
"svc=meow_mdns state=running pid=1006 restart=0 exit=0 trigger=boot backoff_ms=2000\n");
|
||
g_svcd_status[0] = 0; snprintf(g_svcd_status, sizeof g_svcd_status, "%s", path);
|
||
g_svcd_sock[0] = 0; snprintf(g_svcd_sock, sizeof g_svcd_sock, "%s/none.sock", T);
|
||
g_ams_sock[0] = 0; snprintf(g_ams_sock, sizeof g_ams_sock, "%s/none.sock", T);
|
||
g_pid_ent_n = 0;
|
||
refresh_entity_maps();
|
||
char ent[48];
|
||
entity_for_pid(1038, ent, sizeof ent);
|
||
CHECK(strcmp(ent, "svc:meowpanel") == 0);
|
||
entity_for_pid(1006, ent, sizeof ent);
|
||
CHECK(strcmp(ent, "svc:meow_mdns") == 0);
|
||
}
|
||
printf("== pid_ent_put / entity_for_pid(app/svc/proc:comm)==\n");
|
||
{
|
||
g_pid_ent_n = 0;
|
||
pid_ent_put(123, "app:dev.player.ncm");
|
||
pid_ent_put(456, "svc:meowpanel");
|
||
char ent[48];
|
||
entity_for_pid(123, ent, sizeof ent);
|
||
CHECK(strcmp(ent, "app:dev.player.ncm") == 0);
|
||
entity_for_pid(456, ent, sizeof ent);
|
||
CHECK(strcmp(ent, "svc:meowpanel") == 0);
|
||
/* proc:<comm>:造一个 /proc-format comm 不可能,直接读当前 pid comm(非空即入 proc:) */
|
||
int self = (int)getpid();
|
||
snprintf(path, sizeof path, "/proc/%d/comm", self);
|
||
if (access(path, F_OK) == 0) {
|
||
entity_for_pid(self, ent, sizeof ent);
|
||
CHECK(strncmp(ent, "proc:", 5) == 0);
|
||
}
|
||
}
|
||
printf("== parse_proc_net(IPv4 地址/端口/inode)==\n");
|
||
{
|
||
snprintf(path, sizeof path, "%s/tcp", T);
|
||
wf(path, " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n"
|
||
" 0: 32C14E0A:5137 0905CD8C:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12345 1\n"
|
||
" 1: 32C14E0A:0A00 00000000:0000 07 00000000:00000000 00:00000000 00000000 1000 0 67890 1\n");
|
||
g_proc_tcp[0] = 0; snprintf(g_proc_tcp, sizeof g_proc_tcp, "%s", path);
|
||
static conn_t cur[CONN_MAX];
|
||
int n = 0;
|
||
CHECK(parse_proc_net(g_proc_tcp, 0, cur, &n) == 2);
|
||
CHECK(cur[0].local_port == 0x5137 && cur[0].remote_port == 0x01BB);
|
||
CHECK(cur[0].inode == 12345 && cur[0].state_code == 1);
|
||
char ip[32];
|
||
ip_fmt(cur[0].local_ip, ip, sizeof ip);
|
||
CHECK(strcmp(ip, "10.78.193.50") == 0);
|
||
ip_fmt(cur[0].remote_ip, ip, sizeof ip);
|
||
CHECK(strcmp(ip, "140.205.5.9") == 0);
|
||
}
|
||
printf("== build_conns_text / build_dns_ctl_text ==\n");
|
||
{
|
||
g_conn_n = 0;
|
||
conn_t c;
|
||
memset(&c, 0, sizeof c);
|
||
c.local_ip = hex_ip("32C14E0A"); c.local_port = 443; /* 10.78.193.50 */
|
||
c.remote_ip = hex_ip("32C34E0A"); c.remote_port = 25565; /* 10.78.195.50 */
|
||
c.state_code = 1; c.type = 0; c.inode = 555; c.pid = 12345;
|
||
snprintf(c.entity, sizeof c.entity, "app:dev.player.ncm");
|
||
snprintf(c.domain, sizeof c.domain, "music.163.com");
|
||
c.mono_open = mono_now(); c.last_seen = mono_now();
|
||
g_conn[g_conn_n++] = c;
|
||
char out[512]; int tr = 0;
|
||
int len = build_conns_text(NULL, out, sizeof out, &tr);
|
||
CHECK(len > 0 && tr == 0);
|
||
CHECK(strstr(out, "app:dev.player.ncm|10.78.193.50:443|10.78.195.50:25565|ESTAB|0|music.163.com") != NULL);
|
||
char dout[256];
|
||
CHECK(build_dns_ctl_text("status", dout, sizeof dout) == MEOW_OK);
|
||
CHECK(strstr(dout, "dns mode=") != NULL);
|
||
CHECK(build_dns_ctl_text("flush", dout, sizeof dout) == MEOW_OK);
|
||
}
|
||
|
||
if (g_fail) { printf("\nNETMGR P2 TEST: %d FAIL\n", g_fail); return 1; }
|
||
printf("\nNETMGR P2 TEST: ALL PASS\n");
|
||
return 0;
|
||
}
|