10 Netlink 内核-用户通信机制与实现详解
1. 概述
Netlink 是 Linux 内核与用户态之间的 配置与事件总线,对应协议族 PF_NETLINK(socket(AF_NETLINK, SOCK_DGRAM, protocol))。它不承载 IP 数据面流量,而是承载 路由、链路、防火墙、无线、审计 等控制消息。
| 项目 | 说明 |
|---|---|
| 实现目录 | net/netlink/(7 文件) |
| 头文件 | include/linux/netlink.h、include/uapi/linux/netlink.h |
| 系统调用入口 | net/socket.c(sendmsg/recvmsg 分发给 netlink_ops) |
| 与数据面 | 与 NAPI/收包路径正交;配置网卡/路由后影响数据面行为 |
1.1 目录与编译单元
1 | net/netlink/ |
Makefile:obj-y := af_netlink.o genetlink.o policy.o
2. 架构总览
2.1 两类 Netlink
| 类型 | protocol 常量 | 典型用途 | 内核注册 |
|---|---|---|---|
| 固定协议 | NETLINK_ROUTE(0)、NETLINK_NETFILTER(12) 等 |
rtnetlink、nfnetlink | netlink_kernel_create() + cfg.input |
| Generic Netlink | NETLINK_GENERIC(16) |
nl80211、tipc、ovs | genl_register_family() |
固定协议号有限;新子系统普遍走 genetlink,在 NETLINK_GENERIC 上再分 family id + cmd。
3. 核心数据结构
3.1 struct netlink_sock(af_netlink.h)
在 struct sock 之上扩展:
1 | struct netlink_sock { |
用户态 socket:netlink_rcv == NULL,消息入 sk_receive_queue,由 recvmsg 读出。
内核 socket:netlink_rcv 指向如 rtnetlink_rcv、genl_rcv,在 netlink_unicast 中同步调用。
3.2 nl_table[MAX_LINKS]
全局表,每个 protocol 号一项:
| 字段 | 作用 |
|---|---|
hash |
rhashtable,按 (netns, portid) 查找 socket |
mc_list |
已 bind 组播的 socket 链表 |
listeners |
内核侧组播监听者位图 |
registered |
该协议是否已由内核模块注册 |
module / cb_mutex / bind / unbind |
模块引用与 per-protocol 锁 |
查找用 RCU;插入/删除用 nl_table_lock + per-bucket 锁。
3.3 消息格式:nlmsghdr + 载荷
1 | struct nlmsghdr { |
skb 的 NETLINK_CB(skb)(struct netlink_skb_parms)携带路由元数据:portid、dst_group、creds 等。
Generic Netlink 在 nlmsghdr 后还有 genlmsghdr(cmd、version),属性区用 NLA(struct nlattr)。
4. 初始化与挂接
4.1 netlink_proto_init()(core_initcall)
af_netlink.c 启动时:
proto_register(&netlink_proto)nl_table = kcalloc(MAX_LINKS, ...),每表项初始化rhashtablenetlink_add_usersock_entry()— 预注册NETLINK_USERSOCKsock_register(&netlink_family_ops)— 挂到socket.c的net_families[PF_NETLINK]register_pernet_subsys— 每 netns 的/proc/net/netlinkrtnetlink_init()— 在net/core/rtnetlink.c注册NETLINK_ROUTE
4.2 用户态创建 socket
1 | socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE) |
netlink_create() 若协议未注册,可 request_module("net-pf-16-proto-N") 加载对应模块。
4.3 内核创建监听 socket
1 | struct netlink_kernel_cfg cfg = { |
__netlink_kernel_create() 会:创建内核 socket、设置 NETLINK_F_KERNEL_SOCKET、netlink_insert(sk, 0)、填充 nl_table[unit]。
5. 用户态 → 内核(发送路径)
5.1 netlink_sendmsg()
- 从
msg_name(struct sockaddr_nl)或connect()缓存取dst_portid、dst_group - 未 bind →
netlink_autobind()分配portid并插入 hash netlink_alloc_large_skb()分配 skb,用户数据memcpy_from_msgsecurity_netlink_send()LSM 检查- 有
dst_group→netlink_broadcast();否则netlink_unicast()
5.2 netlink_unicast() 分支
1 | netlink_getsockbyportid(ssk, portid) |
5.3 netlink_rcv_skb() — 统一解析入口
内核 handler 普遍通过此函数处理 一个 skb 内多条消息:
1 | while (skb->len >= nlmsg_total_size(0)) { |
仅带 NLM_F_REQUEST 的消息由内核处理;需要确认时发 netlink_ack()(含扩展 ACK TLV)。
6. 内核 → 用户态(接收/通知)
| API | 用途 |
|---|---|
netlink_unicast(ssk, skb, portid, nonblock) |
单播到指定 portid |
netlink_broadcast(ssk, skb, portid, group, gfp) |
组播到订阅 group 的 socket |
nlmsg_notify(sk, skb, portid, group, report, flags) |
组播 + 可选单播回执 |
netlink_broadcast() 遍历 nl_table[protocol].mc_list,对 nlk->groups 中对应 bit 已置位的 socket 调用 netlink_broadcast_deliver()。
用户态 netlink_recvmsg():
skb_recv_datagram()取 skb- 拷贝数据到用户缓冲区
- 填充
sockaddr_nl(来源portid、组) - 若
cb_running且接收缓冲有空 → 触发netlink_dump()继续 dump
7. Dump 机制(大数据量查询)
ip link、RTM_GETLINK 等不会一次返回全部条目:
要点:
__netlink_dump_start()设置nlk->cb与cb->dump回调- 每次
recvmsg可能触发netlink_dump(),按用户max_recvmsg_len分配 skb - 结束发
NLMSG_DONE(netlink_dump_done()) - dump 期间持有 per-protocol
cb_mutex(如rtnl_mutex)
8. Generic Netlink(genetlink.c)
8.1 动机
- 固定
NETLINK_*协议号不够用; - 需要统一的 family / cmd / attribute 模型与动态注册。
8.2 初始化
1 | // genl_pernet_init |
8.3 收包分发
1 | genl_rcv(skb) |
8.4 子系统注册
1 | static struct genl_family nl80211_fam = { |
genl_register_family():校验 ops → idr_alloc 分配 family id → 分配组播组 → 向 genl_ctrl 发 CTRL_CMD_NEWFAMILY 事件。
控制器查询:用户态 GENL_ID_CTRL + CTRL_CMD_GETFAMILY 可发现已注册 family 与 ops。
9. 固定协议示例:rtnetlink
实现于 net/core/rtnetlink.c(非 net/netlink/ 目录,但建立在 netlink 框架上):
| 消息 | 工具对应 | 作用 |
|---|---|---|
RTM_NEWLINK / RTM_DELLINK |
ip link |
接口增删改 |
RTM_NEWADDR / RTM_DELADDR |
ip addr |
地址 |
RTM_NEWROUTE / RTM_DELROUTE |
ip route |
路由 |
RTM_NEWNEIGH |
ip neigh |
邻居 |
注册(每 network namespace):
1 | sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg); |
rtnetlink_rcv → netlink_rcv_skb → 各 rtnl_* 处理函数;变更通过 RTMGRP_* 组播 通知监听者。
10. 其它 netlink 文件
| 文件 | 作用 |
|---|---|
| policy.c | netlink_policy_dump_*:将内核 nla_policy 导出给用户态查询 |
| diag.c | CONFIG_NETLINK_DIAG:通过 sock_diag 导出 netlink socket 状态 |
| af_netlink.c | /proc/net/netlink、BPF iter、netlink tap、notifier |
10.1 常见 protocol 号(节选)
| 常量 | 值 | 模块/用途 |
|---|---|---|
NETLINK_ROUTE |
0 | rtnetlink |
NETLINK_USERSOCK |
2 | 用户自定义 |
NETLINK_SOCK_DIAG |
4 | socket 诊断 |
NETLINK_NETFILTER |
12 | nfnetlink |
NETLINK_GENERIC |
16 | genetlink |
NETLINK_KOBJECT_UEVENT |
15 | udev 热插拔 |
完整列表见 include/uapi/linux/netlink.h。
11. 与 socket.c 的分工
| 层次 | 文件 | 职责 |
|---|---|---|
| 系统调用 | net/socket.c |
socket、bind、sendmsg、recvmsg;sock_register 分发表 |
| Netlink 协议 | net/netlink/af_netlink.c |
netlink_ops、portid、组播、内核 socket、dump、ACK |
| 业务语义 | rtnetlink.c、nl80211.c、nfnetlink.c 等 |
具体消息类型与属性解析 |
netlink_ops 注册的操作(节选):
1 | .sendmsg = netlink_sendmsg, |
12. RK3588 使用场景
Netlink 为上游通用实现,无 Rockchip 专属补丁于 net/netlink/。RK3588 板上典型用法:
| 场景 | 协议 | 用户态工具 |
|---|---|---|
| 以太网 UP/DOWN、IP | NETLINK_ROUTE |
ip link / ip addr |
| 路由 | NETLINK_ROUTE |
ip route |
| WiFi 扫描/连接 | NETLINK_GENERIC + nl80211 |
iw、wpa_supplicant |
| 防火墙 | NETLINK_NETFILTER |
nft |
| 设备热插拔 | NETLINK_KOBJECT_UEVENT |
udev |
| 接口统计/诊断 | NETLINK_SOCK_DIAG |
ss -x 等 |
调试 netlink 可用:strace -e sendmsg,recvmsg ip link show、nlmon(抓 netlink 包)、/proc/net/netlink。
13. 源码索引
| 内容 | 路径 |
|---|---|
| socket 族注册 | net/netlink/af_netlink.c:netlink_proto_init、netlink_ops |
| 内核 socket 创建 | __netlink_kernel_create |
| 单播/组播 | netlink_unicast、netlink_broadcast |
| 消息解析 | netlink_rcv_skb、netlink_ack |
| dump | netlink_dump、__netlink_dump_start |
| Generic Netlink | net/netlink/genetlink.c |
| rtnetlink | net/core/rtnetlink.c |
| UAPI | include/uapi/linux/netlink.h、rtnetlink.h、genetlink.h |
14. 相关文档
- 01-net-core核心机制与实现详解.md — rtnetlink、sock 分层
- 06-二层桥接无线与socket族机制与实现详解.md — nl80211
- 04-netfilter与包过滤机制与实现详解.md — nfnetlink
- 09-NAPI收包轮询机制与实现详解.md — 数据面收包(与 netlink 正交)
15. 小结
| 问题 | 答案 |
|---|---|
| Netlink 是什么? | 内核与用户态的 二进制配置/事件总线(PF_NETLINK) |
| 核心实现在哪? | net/netlink/af_netlink.c + genetlink.c |
| 用户发消息怎么走? | sendmsg → netlink_unicast → 内核 netlink_rcv 或用户队列 |
| 和 rtnetlink 关系? | rtnetlink 是 NETLINK_ROUTE 上的 业务协议 |
| 和 genetlink 关系? | genetlink 是 NETLINK_GENERIC 上的 可扩展 family 框架 |
| 与 NAPI 关系? | 无直接关系;netlink 管 控制面,NAPI 管 收包数据面 |
正在加载留言…