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
@@ -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")) {
|
||||
|
||||
Reference in new issue
Block a user