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/热更设备)。
179 lines
7.3 KiB
C
179 lines
7.3 KiB
C
/* sockdestroy_probe.c — inet_diag SOCK_DESTROY 内核可用性探针(dev 工具,不入固件/不部署)。
|
||
*
|
||
* 目的:内核 4.4(R3 Pro II)是否支持 netlink SOCK_DESTROY 连接级杀 socket。NETWORK_HW_FACTS §5 待验证项。
|
||
* 安全:只对「本进程自建」的 127.0.0.1 临时 TCP 连接发 SOCK_DESTROY,绝不动设备现有连接。
|
||
*
|
||
* 方法:建 loopback TCP 服务端+客户端,accept 得已连接 socket。用 inet_diag DUMP(SOCK_DIAG_BY_FAMILY)
|
||
* 列出全部 TCP,按 tuple(sport/dport/src/dst)匹配出自建 socket 的 idiag_cookie(与 ss -K 同法——
|
||
* 无需 SO_COOKIE);再发 SOCK_DESTROY(tuple + dump cookie),对 accept 出的 socket recv 判是否已杀。
|
||
* 输出:SD_DUMP_DUMPED=<n> SD_MATCH=<0|1> SD_DESTROY_RC=<errno|0> SD_DESTROYED=<0|1>
|
||
* 结论:SD_DESTROY_RC==0 && SD_DESTROYED==1 → 内核支持;-EPERM → 需 root/CAP_NET_ADMIN;-EOPNOTSUPP/ENOENT → 不支持。
|
||
*
|
||
* 用法:交叉编译后 scp 到设备 /usr/data 前台跑(root):`./sockdestroy_probe`(无副作用残留)。
|
||
*/
|
||
#define _GNU_SOURCE
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/time.h>
|
||
#include <linux/netlink.h>
|
||
#include <linux/sock_diag.h>
|
||
#include <linux/inet_diag.h>
|
||
|
||
static int nl = -1;
|
||
|
||
static void open_nl(void)
|
||
{
|
||
nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
|
||
if (nl < 0) { perror("netlink socket"); exit(2); }
|
||
struct sockaddr_nl sa; memset(&sa, 0, sizeof sa); sa.nl_family = AF_NETLINK;
|
||
if (bind(nl, (struct sockaddr *)&sa, sizeof sa) < 0) { perror("bind"); exit(2); }
|
||
}
|
||
|
||
/* dump 全部 TCP,找匹配 tuple 的卡片,取回 cookie。返回 0=找到。 */
|
||
static int diag_find_cookie(uint16_t sport, uint16_t dport, uint32_t sip, uint32_t dip,
|
||
uint32_t *cookie_c0, uint32_t *cookie_c1)
|
||
{
|
||
struct {
|
||
struct nlmsghdr nlh;
|
||
struct inet_diag_req_v2 req;
|
||
} r;
|
||
memset(&r, 0, sizeof r);
|
||
r.nlh.nlmsg_len = sizeof r;
|
||
r.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
|
||
r.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||
r.req.sdiag_family = AF_INET;
|
||
r.req.sdiag_protocol = IPPROTO_TCP;
|
||
r.req.idiag_states = 0xFFFFFFFF;
|
||
struct sockaddr_nl sa; memset(&sa, 0, sizeof sa); sa.nl_family = AF_NETLINK;
|
||
if (sendto(nl, &r, sizeof r, 0, (struct sockaddr *)&sa, sizeof sa) < 0) return -1;
|
||
|
||
struct timeval tv = { 2, 0 };
|
||
setsockopt(nl, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||
int dumped = 0;
|
||
for (;;) {
|
||
char buf[8192];
|
||
ssize_t n = recv(nl, buf, sizeof buf, 0);
|
||
if (n <= 0) break;
|
||
for (struct nlmsghdr *h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned)n); h = NLMSG_NEXT(h, n)) {
|
||
if (h->nlmsg_type == NLMSG_DONE) goto done;
|
||
if (h->nlmsg_type == NLMSG_ERROR) { int e = ((struct nlmsgerr *)NLMSG_DATA(h))->error; if (e) return e; continue; }
|
||
if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY) {
|
||
struct inet_diag_msg *m = (struct inet_diag_msg *)NLMSG_DATA(h);
|
||
dumped++;
|
||
if (ntohs(m->id.idiag_sport) == sport && ntohs(m->id.idiag_dport) == dport &&
|
||
(ntohl(m->id.idiag_src[0]) == sip) && (ntohl(m->id.idiag_dst[0]) == dip)) {
|
||
*cookie_c0 = m->id.idiag_cookie[0];
|
||
*cookie_c1 = m->id.idiag_cookie[1];
|
||
printf("SD_DUMPED=%d SD_MATCH=1\n", dumped);
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
done:
|
||
printf("SD_DUMPED=%d SD_MATCH=0\n", dumped);
|
||
return -1;
|
||
}
|
||
|
||
static int sd_try(uint16_t sport, uint16_t dport, uint32_t sip, uint32_t dip,
|
||
uint32_t c0, uint32_t c1, int with_cookie)
|
||
{
|
||
struct {
|
||
struct nlmsghdr nlh;
|
||
struct inet_diag_req_v2 req;
|
||
} r;
|
||
memset(&r, 0, sizeof r);
|
||
r.nlh.nlmsg_len = sizeof r;
|
||
r.nlh.nlmsg_type = SOCK_DESTROY;
|
||
r.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||
r.req.sdiag_family = AF_INET;
|
||
r.req.sdiag_protocol = IPPROTO_TCP;
|
||
r.req.idiag_states = 0xFFFFFFFF;
|
||
r.req.id.idiag_sport = htons(sport);
|
||
r.req.id.idiag_dport = htons(dport);
|
||
r.req.id.idiag_src[0] = htonl(sip);
|
||
r.req.id.idiag_dst[0] = htonl(dip);
|
||
r.req.id.idiag_cookie[0] = with_cookie ? c0 : 0;
|
||
r.req.id.idiag_cookie[1] = with_cookie ? c1 : 0;
|
||
struct sockaddr_nl sa; memset(&sa, 0, sizeof sa); sa.nl_family = AF_NETLINK;
|
||
if (sendto(nl, &r, sizeof r, 0, (struct sockaddr *)&sa, sizeof sa) < 0) return -errno;
|
||
char buf[512];
|
||
struct timeval tv = { 0, 300000 };
|
||
setsockopt(nl, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||
ssize_t rr = recv(nl, buf, sizeof buf, 0);
|
||
if (rr < 0) return -errno;
|
||
struct nlmsghdr *h = (struct nlmsghdr *)buf;
|
||
if (h->nlmsg_type == NLMSG_ERROR) return ((struct nlmsgerr *)NLMSG_DATA(h))->error;
|
||
return 0;
|
||
}
|
||
|
||
static int check_destroyed(int fd)
|
||
{
|
||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
|
||
char b[16];
|
||
ssize_t r = recv(fd, b, sizeof b, 0);
|
||
if (r == 0) return 1;
|
||
if (r < 0) return (errno == ECONNRESET || errno == ECONNABORTED) ? 1 : 0;
|
||
return 0;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||
int cfd = socket(AF_INET, SOCK_STREAM, 0);
|
||
struct sockaddr_in addr;
|
||
memset(&addr, 0, sizeof addr);
|
||
addr.sin_family = AF_INET;
|
||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||
addr.sin_port = htons(0);
|
||
int one = 1;
|
||
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
|
||
bind(lfd, (struct sockaddr *)&addr, sizeof addr);
|
||
listen(lfd, 8);
|
||
socklen_t alen = sizeof addr;
|
||
getsockname(lfd, (struct sockaddr *)&addr, &alen);
|
||
connect(cfd, (struct sockaddr *)&addr, sizeof addr);
|
||
int sfd = accept(lfd, NULL, NULL);
|
||
struct sockaddr_in sa, sc;
|
||
socklen_t sla = sizeof sa, slc = sizeof sc;
|
||
getsockname(sfd, (struct sockaddr *)&sa, &sla);
|
||
getpeername(sfd, (struct sockaddr *)&sc, &slc);
|
||
uint16_t sport = ntohs(sa.sin_port);
|
||
uint16_t dport = ntohs(sc.sin_port);
|
||
uint32_t sip = ntohl(sa.sin_addr.s_addr);
|
||
uint32_t dip = ntohl(sc.sin_addr.s_addr);
|
||
open_nl();
|
||
|
||
uint32_t c0 = 0, c1 = 0;
|
||
int found = (diag_find_cookie(sport, dport, sip, dip, &c0, &c1) == 0);
|
||
if (found) {
|
||
/* 主测靠谱路径:cookie 版(与 ss -K 同法;无需 SO_COOKIE)。 */
|
||
int rc_c = sd_try(sport, dport, sip, dip, c0, c1, 1);
|
||
int dead_c = check_destroyed(sfd);
|
||
printf("SD_DESTROY(cookie) rc=%d destroyed=%d\n", rc_c, dead_c);
|
||
if (dead_c) {
|
||
printf("CONCL=SOCK_DESTROY_SUPPORTED\n");
|
||
} else if (rc_c == -EPERM) {
|
||
printf("CONCL=SOCK_DESTROY_NEED_CAP(rc=-EPERM; 需 root/CAP_NET_ADMIN)\n");
|
||
} else if (rc_c == 0) {
|
||
printf("CONCL=SOCK_DESTROY_NOOP(rc=0 但未杀; 内核未真正销毁——视为不可用)\n");
|
||
} else {
|
||
printf("CONCL=SOCK_DESTROY_UNSUPPORTED(rc=%d)\n", rc_c);
|
||
}
|
||
/* 附带:tuple+cookie=0 结果(诊断参考) */
|
||
int rc_t = sd_try(sport, dport, sip, dip, c0, c1, 0);
|
||
printf("SD_DESTROY(tuple,cookie=0) rc=%d destroyed=%d\n", rc_t, check_destroyed(sfd));
|
||
} else {
|
||
printf("CONCL=SOCK_DESTROY_UNKNOWN(dump no match)\n");
|
||
}
|
||
close(cfd); close(sfd); close(lfd);
|
||
return 0;
|
||
}
|