欢迎光临
我们一直在努力

linux kernel CONFIG_INET_DIAG解析

CONFIG_INET_DIAG 是 Linux 内核里负责导出 IPv4/IPv6 套接字信息给用户态的核心开关,从 2.6.14 引入,主流发行版默认 =y(内置)。下面从配置、源码逐行拆解、关键流程、常用工具与关联配置几部分说明。


一、配置项说明(Kconfig)

路径:net/ipv4/Kconfig

config INET_DIAG
tristate "INET: socket monitoring interface"
depends on INET
help
Support for INET (TCP, UDP, etc) socket monitoring interface
used by native Linux tools such as ss.
ss is included in iproute2.
If unsure, say Y.

  • 类型:tristate(y/m/n)
  • 依赖:CONFIG_INET=y(必须启用 IPv4)
  • 作用:编译 net/ipv4/inet_diag.c,提供 NETLINK_SOCK_DIAG 接口。
  • 典型默认:y(内置),部分极简内核设为 m 或 n。

二、源码文件与整体框架

文件:net/ipv4/inet_diag.c(约 1000 行)核心功能:

  • 建立 netlink 套接字,接收用户态请求(如 ss)。
  • 遍历内核 TCP/UDP/RAW 套接字链表,收集状态、地址、端口、内存、拥塞等信息。
  • 通过 netlink 回包 发给用户态工具。
  • 支持 销毁套接字(INET_DIAG_DESTROY)。

  • 三、逐行拆解(核心片段,基于 5.15+)

    1. 头文件与全局变量

    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/fcntl.h>
    #include <linux/random.h>
    #include <linux/slab.h>
    #include <linux/cache.h>
    #include <linux/init.h>
    #include <linux/time.h>
    #include <net/icmp.h>
    #include <net/tcp.h>
    #include <net/ipv6.h>
    #include <net/inet_common.h>
    #include <net/sock.h>
    #include <net/netlink.h>
    #include <net/inet_diag.h>
    #include <linux/nl80211.h>
    #include <linux/export.h>

    // 协议处理表:TCP/UDP/RAW 等注册自己的回调
    static const struct inet_diag_handler **inet_diag_table;
    static struct sock *inet_diag_nlsk; // netlink 套接字
    static DEFINE_SPINLOCK(inet_diag_lock);

    • 引入网络栈、netlink、sock 相关头文件。
    • inet_diag_table:核心,每个协议(TCP/UDP)注册自己的 diag_handler。
    • inet_diag_nlsk:内核侧 netlink 套接字,监听用户态请求。
    2. 协议注册接口(供 TCP/UDP 调用)

    int inet_diag_register(const struct inet_diag_handler *h)
    {
    int err = -EEXIST;
    spin_lock(&inet_diag_lock);
    if (!inet_diag_table[h->idiag_type]) {
    inet_diag_table[h->idiag_type] = h;
    err = 0;
    }
    spin_unlock(&inet_diag_lock);
    return err;
    }
    EXPORT_SYMBOL_GPL(inet_diag_register);

    • 每个协议(如 TCP)调用此函数,把自己的 handler 挂到 inet_diag_table。
    • idiag_type:协议号(如 IPPROTO_TCP=6)。
    • 作用:解耦各协议,统一查询入口。
    3. 初始化函数(module_init)

    static int __init inet_diag_init(void)
    {
    int err;
    // 分配协议表:最大 256 项(覆盖所有传输层协议)
    inet_diag_table = kcalloc(256, sizeof(*inet_diag_table), GFP_KERNEL);
    if (!inet_diag_table)
    return -ENOMEM;

    // 创建 NETLINK_SOCK_DIAG 套接字
    inet_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, 0,
    inet_diag_rcv_msg, NULL, THIS_MODULE);
    if (!inet_diag_nlsk) {
    err = -ENOMEM;
    goto out_free;
    }

    // 注册 IPv4/IPv6 处理函数到 sock_diag 子系统
    err = sock_diag_register(&inet_diag_handler);
    if (err)
    goto out_free_nl;
    err = sock_diag_register(&inet6_diag_handler);
    if (err)
    goto out_free_inet;

    out:
    return 0;
    out_free_inet:
    sock_diag_unregister(&inet_diag_handler);
    out_free_nl:
    netlink_kernel_release(inet_diag_nlsk);
    out_free:
    kfree(inet_diag_table);
    goto out;
    }
    module_init(inet_diag_init);

    • 分配协议表,大小 256,覆盖所有可能的传输层协议。
    • 创建 NETLINK_SOCK_DIAG 类型的 netlink 套接字,回调 inet_diag_rcv_msg 处理用户态请求。
    • 注册 IPv4/IPv6 的 handler 到内核 sock_diag 子系统(net/sock_diag.c)。
    4. 消息接收回调(核心:处理用户态请求)

    static void inet_diag_rcv_msg(struct sk_buff *skb)
    {
    struct nlmsghdr *nlh;
    u32 portid;
    struct net *net;

    // 遍历 netlink 消息
    while (nlmsg_ok(nlh, skb->len)) {
    portid = nlh->nlmsg_pid;
    net = &init_net; // 初始网络命名空间

    // 根据消息类型分发
    switch (nlh->nlmsg_type) {
    case INET_DIAG_GETSOCK:
    // 查询单个套接字
    inet_diag_get_sock(skb, nlh, portid, net);
    break;
    case INET_DIAG_DUMP:
    // 遍历所有套接字(ss -a 的核心)
    inet_diag_dump(skb, nlh, portid, net);
    break;
    #ifdef CONFIG_INET_DIAG_DESTROY
    case INET_DIAG_DESTROY:
    // 销毁指定套接字
    inet_diag_destroy(skb, nlh, portid, net);
    break;
    #endif
    default:
    nlmsg_answer(skb, nlh, portid, -EINVAL);
    }
    nlh = nlmsg_next(nlh, &skb->len);
    }
    }

    • 用户态(如 ss)发 netlink 消息到内核。
    • INET_DIAG_GETSOCK:查单个套接字(按 inode / 地址)。
    • INET_DIAG_DUMP:遍历所有 TCP/UDP 套接字,ss -tuln 就是走这个。
    • INET_DIAG_DESTROY:销毁套接字(需 CONFIG_INET_DIAG_DESTROY=y)。
    5. Dump 流程(遍历套接字链表)

    static void inet_diag_dump(struct sk_buff *skb, struct nlmsghdr *nlh,
    u32 portid, struct net *net)
    {
    struct inet_diag_req_v2 *r = nlmsg_data(nlh);
    struct inet_diag_handler *h;
    int proto = r->sdiag_protocol;

    // 协议合法性检查
    if (proto < 0 || proto >= 256 || !inet_diag_table[proto])
    return;

    h = (struct inet_diag_handler *)inet_diag_table[proto];
    // 调用对应协议的 dump 函数(如 tcp_diag_dump)
    h->dump(skb, nlh, r, portid, net);
    }

    • 从用户态请求里取协议号(TCP=6/UDP=17)。
    • 查 inet_diag_table,找到对应协议的 handler。
    • 调用协议自己的 dump 函数(如 tcp_diag_dump),遍历该协议的套接字链表。
    6. TCP 协议的 diag 注册(tcp_diag.c)

    static const struct inet_diag_handler tcp_diag_handler = {
    .idiag_type = IPPROTO_TCP,
    .dump = tcp_diag_dump,
    .get_info = tcp_diag_get_info,
    .destroy = tcp_diag_destroy,
    };

    static int __init tcp_diag_init(void)
    {
    return inet_diag_register(&tcp_diag_handler);
    }
    module_init(tcp_diag_init);

    • TCP 模块注册自己的 handler:
      • dump:遍历 TCP 套接字链表(net->ipv4.tcp_sock_head)。
      • get_info:填充 TCP 状态、窗口、拥塞、内存等。
      • destroy:关闭 TCP 连接。
    7. 清理函数

    static void __exit inet_diag_exit(void)
    {
    sock_diag_unregister(&inet6_diag_handler);
    sock_diag_unregister(&inet_diag_handler);
    netlink_kernel_release(inet_diag_nlsk);
    kfree(inet_diag_table);
    }
    module_exit(inet_diag_exit);

    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("INET socket monitoring interface");

    • 模块卸载时,反注册、释放 netlink 套接字、释放协议表。

    四、关键依赖与关联配置

    表格

    配置项作用依赖
    CONFIG_INET_DIAG=y 主开关,启用 inet_diag 框架 CONFIG_INET
    CONFIG_INET_TCP_DIAG=y TCP 套接字查询 INET_DIAG
    CONFIG_INET_UDP_DIAG=y UDP 套接字查询 INET_DIAG
    CONFIG_INET_RAW_DIAG=y RAW 套接字查询 INET_DIAG
    CONFIG_INET_DIAG_DESTROY=y 允许销毁套接字 INET_DIAG

    主流发行版(RHEL/CentOS/Debian/Ubuntu)默认:

    plaintext

    CONFIG_INET_DIAG=y
    CONFIG_INET_TCP_DIAG=y
    CONFIG_INET_UDP_DIAG=y


    五、用户态工具:ss 命令(依赖 INET_DIAG)

    ss 是 iproute2 工具集,替代老旧的 netstat,数据完全来自 INET_DIAG。常用命令:

    ss -tuln # 查所有监听的 TCP/UDP 端口
    ss -ti # 查 TCP 连接详情(窗口、拥塞、状态)
    ss -s # 统计套接字数量

    • 若内核关闭 CONFIG_INET_DIAG=n,ss 会报错:Netlink error: No such file or directory。

    六、常见问题与影响

  • 关闭 CONFIG_INET_DIAG=n:
    • 优点:极小节省内核内存(可忽略)。
    • 缺点:ss 不可用,网络排障困难;容器 / 云网络工具(如 Cilium、Kube-Proxy)依赖此功能。
  • 开启 CONFIG_INET_DIAG_DESTROY=y:
    • 允许用户态工具强制关闭内核套接字,安全敏感环境可关闭。

  • 七、总结

    • CONFIG_INET_DIAG 是 Linux 网络诊断的基础设施,2005 年引入,主流内核必开。
    • 核心是 netlink + 协议注册表,统一导出 TCP/UDP/RAW 套接字信息给用户态。
    • ss、iproute2、容器网络工具强依赖此功能,生产环境不要关闭。
    赞(0)
    未经允许不得转载:171主机测评 » linux kernel CONFIG_INET_DIAG解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址