Files
MeowOS-For-R3P2/patch_v2/meow_netmgrd/sockdestroy_probe.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

179 lines
7.3 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.
/* 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;
}