netlink(1)

本项目提供了一个完整的 Linux Netlink 通信示例,用于学习 Netlink 的核心工作原理。

项目结构

复制代码
.
├── kernel_netlink.c    # 内核模块源代码
├── user_netlink.c      # 用户空间客户端源代码
├── Makefile            # 编译脚本
├── test.sh             # 自动化测试脚本
└── README.md           # 本文档

核心概念

基于PID的单播通信
  • 每个Netlink套接字绑定到一个唯一的进程ID (PID)
  • 内核通过PID将消息发送到特定的用户空间进程
  • nlmsg_pid 字段标识消息发送者的PID
基于多播组的通信
  • 多播组允许多个进程同时接收相同的消息
  • 进程通过 setsockopt() 加入多播组
  • nl_groups 字段指定多播组ID

2. Netlink消息格式

复制代码
+------------------+
|   nlmsghdr       |  <- 消息头 (16字节)
+------------------+
|                  |
|   Payload Data   |  <- 数据负载
|                  |
+------------------+
nlmsghdr 结构体
c 复制代码
struct nlmsghdr {
    __u32 nlmsg_len;    // 消息总长度(包括头部)
    __u16 nlmsg_type;   // 消息类型
    __u16 nlmsg_flags;  // 消息标志
    __u32 nlmsg_seq;    // 序列号
    __u32 nlmsg_pid;    // 发送者PID
};

3. 常用Netlink宏

功能 说明
NLMSG_LENGTH(len) 计算消息长度 头部长度 + len(未对齐)
NLMSG_SPACE(len) 计算空间大小 头部长度 + len(对齐到4字节)
NLMSG_DATA(nlh) 获取数据指针 返回消息头之后的数据区地址
NLMSG_NEXT(nlh, len) 获取下一条消息 用于遍历多条消息
NLMSG_OK(nlh, len) 验证消息有效性 检查消息是否完整
NLMSG_PAYLOAD(nlh, len) 获取负载长度 返回数据负载的实际长度
NLMSG_ALIGN(len) 对齐长度 将len对齐到4字节边界
NLMSG_HDRLEN 消息头长度 对齐后的头部长度

编译和运行

前提条件

  • Linux内核头文件
  • GCC编译器
  • make工具
  • root权限(用于加载内核模块)

编译步骤

bash 复制代码
# 编译内核模块和用户空间程序
make

# 或者分别编译
make kernel_module    # 仅编译内核模块
make user_program     # 仅编译用户程序

运行步骤

1. 加载内核模块
bash 复制代码
sudo insmod kernel_netlink.ko
2. 检查模块是否加载成功
bash 复制代码
lsmod | grep kernel_netlink
dmesg | tail
3. 运行用户空间程序
bash 复制代码
# 单播通信演示
./user_netlink unicast

# 多播通信演示(需要两个终端)
# 终端1:接收多播消息
./user_netlink multicast

# 终端2:触发内核发送多播消息
./user_netlink trigger

# 演示Netlink宏
./user_netlink macros
4. 卸载内核模块
bash 复制代码
sudo rmmod kernel_netlink

自动化测试

bash 复制代码
chmod +x test.sh
./test.sh

测试场景详解

场景1:单播通信

单播通信演示了基于PID的寻址方式:

  1. 用户程序创建Netlink套接字并绑定到自己的PID
  2. 构造Netlink消息,设置目标PID为0(内核)
  3. 通过sendmsg()发送消息
  4. 内核模块接收消息,根据nlmsg_pid识别发送者
  5. 内核构造响应消息,通过nlmsg_unicast()发送回用户程序
bash 复制代码
./user_netlink unicast

场景2:多播通信

多播通信演示了基于多播组的寻址方式:

  1. 接收者进程通过setsockopt()加入多播组
  2. 触发者进程发送MULTICAST类型消息给内核
  3. 内核通过nlmsg_multicast()向多播组发送消息
  4. 所有加入该多播组的进程都会收到消息
bash 复制代码
# 终端1
./user_netlink multicast

# 终端2
./user_netlink trigger

场景3:观察消息处理

使用dmesg查看内核日志:

bash 复制代码
# 实时查看内核日志
dmesg -w | grep netlink_kernel

# 查看最近的日志
dmesg | tail -50

消息类型定义

本示例定义了三种消息类型:

类型 说明
NLMSG_SETPID 0x11 客户端注册PID
NLMSG_GETPID 0x12 请求内核信息
NLMSG_MULTICAST 0x13 触发多播消息

关键代码解析

内核模块关键函数

  1. netlink_kernel_create() - 创建Netlink套接字
  2. nlmsg_new() - 分配新的Netlink消息
  3. nlmsg_put() - 填充消息头
  4. nlmsg_unicast() - 发送单播消息
  5. nlmsg_multicast() - 发送多播消息

用户空间关键函数

  1. socket(AF_NETLINK, ...) - 创建Netlink套接字
  2. bind() - 绑定到本地地址(PID)
  3. setsockopt() - 加入多播组
  4. sendmsg() - 发送消息
  5. recvmsg() - 接收消息

调试技巧

1. 使用strace跟踪系统调用

bash 复制代码
strace -e socket,bind,sendmsg,recvmsg ./user_netlink unicast

2. 查看Netlink套接字状态

bash 复制代码
cat /proc/net/netlink

3. 使用netlink监控工具

bash 复制代码
# 安装libnl工具
sudo apt-get install libnl-3-dev libnl-genl-3-dev

# 使用nl-sock-list查看Netlink套接字
nl-sock-list

常见问题

Q1: 加载内核模块失败

确保已安装正确的内核头文件:

bash 复制代码
sudo apt-get install linux-headers-$(uname -r)

Q2: 权限不足

Netlink操作需要root权限:

bash 复制代码
sudo ./user_netlink unicast

Q3: 多播消息接收不到

确保接收进程先启动并加入多播组,然后再触发多播。

Q4: 内核模块无法卸载

检查是否有进程正在使用:

bash 复制代码
lsmod | grep kernel_netlink

扩展学习

Generic Netlink提供了更灵活的Netlink使用方式,适合需要动态注册协议的场景。

2. Netlink属性

使用nla_*系列函数可以更方便地处理复杂的数据结构。

查看系统支持的Netlink协议族:

bash 复制代码
cat /usr/include/linux/netlink.h | grep NETLINK_

参考资料

相关推荐
王da魔2 小时前
Keepalived
网络·云原生
hzulwy2 小时前
Linux网络配置与测试
linux·运维·网络
zxnbmk2 小时前
磁盘挂载与迁移【自用复制】
linux
WW、forever2 小时前
【服务器】上传服务器中数据至 FigShare(Python)
运维·服务器·python
五阿哥永琪2 小时前
HTTP包含哪些内容?
网络·网络协议·http
YQ_013 小时前
Ubuntu下安装WPS
linux·ubuntu·wps
小义_3 小时前
【Docker】知识四
linux·运维·docker·容器
小志biubiu3 小时前
Linux_进程概念(A)-进程部分【Ubuntu】
linux·运维·服务器·ubuntu·操作系统·进程
普通网友3 小时前
Ubuntu 入门及安装全指南
linux·运维·ubuntu