Files
MeowOS-For-R3P2/patch_v2/meow_netmgrd/test_netmgrd_p3.c
T
MeowOS b37dc23cd9 meow_netmgrd 期3:管控层(三实体策略引擎+防自锁+SOCK_DESTROY 实测→仅 DNS 层)
NETMGR_DESIGN.md §6/§13 期3 落地(全在 patch_v2/meow_netmgrd/ + meow_ipc.h 追加):
1. 策略引擎全量:/data/meow/netmgrd/net_policy.conf(/data=/usr/data)读改写单键+原子写
   (temp+rename,绝不 O_TRUNC 整文件重写——watchdog 毁设置教训);per-实体(app:/svc:/proc:)
   net=allow|block + domain 白/黑(接通 DNS 层执行)+ global log_level;默认 allow。
2. 防自锁硬编码:dropbear/wpa_supplicant/udhcpc/meow_netmgrd/meow_svcd/meow_sysui/init 拒绝 block
   (CLI/IPC 拒 + 加载校验把手工改坏的文件里这些实体的 net=block 同样强制复位 allow——§14 红线)。
3. SOCK_DESTROY 真机实测(sockdestroy_probe.c,root @内核4.4):**内核接受 SOCK_DESTROY 请求(rc=0)
   但未真正销毁 socket(cookie=0 与 dump-cookie 两法均 dead=0)→ 判定 no-op/不可用**。
   ⇒ 连接级执行降级为「仅 DNS 层生效」(net=block → dns_query_domain_denied 全量 deny,DNS 层落地),
   policy show 头 enfor=dns_only 明示。SDK 自愿通道/设置页迁移期外。
4. IPC 0x00EA MSG_NET_POLICY(op: show/set net/set domain/del)+ RESULT 0x00EB:读级任意、写级仅系统身份。
   CLI:policy show [entity] | set <entity> allow|block | domain <entity> +host|-host | del <entity>。
5. 两小修:①classify_exe 增认 MeowPanel 精确路径(修面板 dns flush 403;面板已有 PIN 门禁)
   ②连接未归因(inode=0/查不到)一律落 proc:unknown——修 apps 表出现「IP:port」原始端点行。
6. WiFi connect 持久化:MeowOS 模式 connect 后 wpa_cli save_config(HibyOS 只读由服务端门控)。

验证:宿主 test_netmgrd.sh 61/61 + qemu 61/61;test_netmgrd_p2/p3 宿主+qemu 全过;netmgrd_authtest 全过;
交叉 -Wall -Wextra 0 warning。真机只读实测(DNS-on 固件共存,用独立 socket+/tmp 临时 policy.json):
policy set/domain/del 全链 + 防自锁拒绝 proc:dropbear(rc=1) + 空文件 del(size=0) + apps 无原始端点 +
resolv.conf 未触碰(测后核验固件两行形式);Firmware meow_netmgrd(pid 8142) running 未动。

新 md5:meow_netmgrd 62369ebe29e7ef4b55067a0f14e240d0(主会话更 fw_content.conf/热更设备)。
2026-09-01 22:08:10 +08:00

120 lines
5.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_netmgrd_p3.c — meow_netmgrd 期3 管控层 纯逻辑宿主自测(本程序非生产代码)。
*
* 通过 #include 生产源,同一 TU 直接访问 FILE-STATIC:policy_cmd / rules_load / policy_persist_entity /
* policy_net_blocked / dns_query_domain_denied / is_self_lock / policy_find / g_policy_log_level。
* 覆盖:policy set net(per-entity 持久化读改写)、防自锁拒绝 + 加载校验剔除、domain 白/黑、
* net=block → DNS 层全量 deny、del、global log_level、RMW 保留他行。真实 IPC 鉴权(CALL_OTHER 拒)
* 由 test_netmgrd.sh 覆盖。
*
* 用法:cc -Wall -Wextra -I../meow_launcherd test_netmgrd_p3.c -o test_netmgrd_p3 && ./test_netmgrd_p3
*/
#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 char rd[512];
static const char *readfile(const char *p)
{
FILE *f = fopen(p, "r");
if (!f) { rd[0] = 0; return rd; }
size_t n = fread(rd, 1, sizeof rd - 1, f); rd[n] = 0; fclose(f);
return rd;
}
int main(void)
{
static const char *T = "/tmp/nmg_p3";
char policy[192];
mkdir(T, 0755);
snprintf(policy, sizeof policy, "%s/policy", T);
g_net_policy[0] = 0; snprintf(g_net_policy, sizeof g_net_policy, "%s", policy);
FILE *f = fopen(policy, "w"); /* 先建空 policy 文件 */
if (f) { fputs("# boot\n", f); fclose(f); }
char out[512];
int rc;
printf("== policy_cmd SET_NET(per-entity 持久化)==\n");
rules_load();
CHECK(g_rule_n == 0);
rc = policy_cmd(NET_POLICY_SET_NET, "proc:thttpd", "block", out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(strstr(readfile(policy), "proc:thttpd net=block") != NULL);
CHECK(policy_net_blocked("proc:thttpd") == 1);
CHECK(policy_net_blocked("proc:unknown") == 0); /* 未显式规则 → 默认 allow */
printf("== 防自锁:拒绝 block + 加载校验剔除 ==\n");
rc = policy_cmd(NET_POLICY_SET_NET, "proc:dropbear", "block", out, sizeof out);
CHECK(rc == MEOW_ERR_PERM);
CHECK(strstr(out, "防自锁") != NULL);
CHECK(policy_net_blocked("proc:dropbear") == 0);
CHECK(policy_net_blocked("svc:meow_netmgrd") == 0);
/* 手工改坏文件塞入 dropbear net=block → 加载校验剔除 */
f = fopen(policy, "w");
if (f) { fputs("proc:dropbear net=block\nproc:thttpd net=block\n", f); fclose(f); }
rules_load();
CHECK(policy_net_blocked("proc:dropbear") == 0);
CHECK(policy_net_blocked("proc:thttpd") == 1);
printf("== policy SET_DOMAIN(白/黑;allow 覆盖)+ net=block → DNS 全量 deny ==\n");
rc = policy_cmd(NET_POLICY_SET_DOMAIN, "proc:web", "+music.163.com", out, sizeof out);
CHECK(rc == MEOW_OK);
rc = policy_cmd(NET_POLICY_SET_DOMAIN, "proc:web", "-ads.example.com", out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(strstr(readfile(policy), "proc:web domain=+music.163.com,-ads.example.com") != NULL);
CHECK(dns_query_domain_denied("proc:web", "music.163.com") == 0); /* 白名单允许 */
CHECK(dns_query_domain_denied("proc:web", "p.music.163.com") == 0); /* 子域也允许 */
CHECK(dns_query_domain_denied("proc:web", "ads.example.com") == 1); /* 黑名单拒绝 */
CHECK(dns_query_domain_denied("proc:web", "other.com") == 0);
/* net=block 的实体 → DNS 全量 deny(即使白名单);且已有 domain 规则的实体被 block 后同样全 deny */
rc = policy_cmd(NET_POLICY_SET_NET, "proc:web", "block", out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(dns_query_domain_denied("proc:web", "music.163.com") == 1); /* block 覆盖白名单 */
rc = policy_cmd(NET_POLICY_SET_NET, "app:dev.player.ncm", "block", out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(dns_query_domain_denied("app:dev.player.ncm", "music.163.com") == 1);
printf("== del 移除实体(RMW 保留其它行)==\n");
rc = policy_cmd(NET_POLICY_DEL, "proc:thttpd", NULL, out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(strstr(readfile(policy), "proc:thttpd") == NULL);
CHECK(strstr(readfile(policy), "app:dev.player.ncm net=block") != NULL); /* 其它实体行保留 */
/* RMW 保留注释/无关行(写回前 seed 应有 # 头) */
f = fopen(policy, "w");
if (f) { fputs("# commentary\nproc:thttpd net=block\napp:dev.player.ncm domain=-tracker.net\n", f); fclose(f); }
rules_load();
rc = policy_cmd(NET_POLICY_SET_NET, "app:dev.player.ncm", "block", out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(strstr(readfile(policy), "# commentary") != NULL);
CHECK(strstr(readfile(policy), "app:dev.player.ncm domain=-tracker.net") != NULL); /* domain 行保留 */
CHECK(strstr(readfile(policy), "app:dev.player.ncm net=block") != NULL);
printf("== global log_level ==\n");
f = fopen(policy, "w");
if (f) { fputs("global log_level=debug\n", f); fclose(f); }
rules_load();
CHECK(strcmp(g_policy_log_level, "debug") == 0);
printf("== del 到空文件(write 0 字节场景)==\n");
f = fopen(policy, "w");
if (f) { fputs("proc:thttpd net=block\n", f); fclose(f); }
rules_load();
rc = policy_cmd(NET_POLICY_DEL, "proc:thttpd", NULL, out, sizeof out);
CHECK(rc == MEOW_OK);
CHECK(strstr(readfile(policy), "proc:thttpd") == NULL); /* 文件已无该实体(可为空) */
CHECK(g_rule_n == 0);
if (g_fail) { printf("\nNETMGR P3 TEST: %d FAIL\n", g_fail); return 1; }
printf("\nNETMGR P3 TEST: ALL PASS\n");
return 0;
}