今天这篇文章小编想跟大家伙儿聊聊 Netlink 这个强大的通信机制,如果有小伙伴们想从事 Linux C/C++ 开发、内核开发、网络开发、驱动开发,掌握它是必备基础。可以让你从 "只会调用 API 的应用层开发" 升级为 "懂内核 / 用户态交互逻辑的资深开发"~好了,感兴趣的小伙伴可以看看~
在 Linux 系统中,Netlink 是一个用于内核与用户空间之间进行高效通信的协议。Netlink 提供了一种机制,允许内核模块、用户空间应用程序和其他内核子系统之间交换信息。它被广泛应用于网络配置、路由、网络地址转换(NAT)、防火墙规则(iptables)等领域。这篇文章将浅谈 Netlink 的工作原理、使用方式以及实际应用场景,帮助小伙伴们更好地理解并使用。Netlink 是 Linux 内核提供的一种机制,用于用户空间和内核之间的通信。它的设计基于套接字(socket),用户空间应用程序可以通过它来与内核进行数据交换。Netlink 与传统的 TCP/IP 套接字不同,主要用于内核和用户空间之间的低延迟、高效的通信。Netlink 通过套接字提供了内核空间和用户空间的双向通信渠道,广泛用于网络协议栈管理、设备配置、路由表、网络接口等。它的通信方式与其他套接字类型(如 TCP、UDP)类似,但具有更低的开销和更多的灵活性。Netlink 套接字分为多个类型,每种类型的 Netlink 套接字用于不同的内核功能。常见的 Netlink 类型包括:- NETLINK_ROUTE:与路由相关的操作(例如路由表管理、接口配置)。
- NETLINK_GENERIC:用于一般性的内核信息交换。
- NETLINK_FIREWALL:与防火墙相关的通信(iptables、ip6tables)。
- NETLINK_INET_DIAG:用于获取网络套接字的信息。
- NETLINK_NFLOG:与 Netfilter 日志相关的通信。
在用户空间应用程序中,Netlink 套接字通过 socket() 系统调用创建,与其他网络套接字(如 TCP、UDP)类似。创建 Netlink 套接字时,需要指定通信的类型和协议。#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/socket.h>#include<linux/netlink.h>#define NETLINK_ROUTE 0 // 路由通信intmain(){ int sock_fd; struct sockaddr_nl sa_nl; // 创建 Netlink 套接字 sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock_fd < 0) { perror("Socket creation failed"); exit(EXIT_FAILURE); } // 设置 Netlink 地址结构 memset(&sa_nl, 0, sizeof(sa_nl)); sa_nl.nl_family = AF_NETLINK; // 绑定套接字 if (bind(sock_fd, (struct sockaddr *)&sa_nl, sizeof(sa_nl)) < 0) { perror("Bind failed"); close(sock_fd); exit(EXIT_FAILURE); } printf("Netlink socket created and bound successfully\n"); close(sock_fd); return 0;}
使用 socket() 创建 Netlink 套接字,传入的参数:- AF_NETLINK 表示使用 Netlink 协议族。
- SOCK_RAW 用于裸套接字,表示没有协议头,允许自定义协议。
(2)、绑定套接字: 绑定操作通过 bind() 完成,将 Netlink 套接字与本地地址绑定,以便接收和发送数据。Netlink 消息由一个头部和一个负载部分组成。消息头部定义了消息的类型、大小、标识符等信息。负载部分包含了实际的数据内容。struct nlmsghdr { __u32 nlmsg_len; // 消息的总长度 __u16 nlmsg_type; // 消息类型 __u16 nlmsg_flags; // 消息标志 __u32 nlmsg_seq; // 消息序列号 __u32 nlmsg_pid; // 发送消息的进程 ID};
- nlmsg_len:消息的总长度,包括头部和数据部分。
- nlmsg_type:消息类型,指示该消息是执行某个操作,还是回复某个请求。
- nlmsg_flags:标志,定义消息的特性,例如是否要求回复等。
- nlmsg_pid:发送消息的进程 ID,方便接收方识别消息来源。
Netlink 消息的负载部分依赖于消息的类型。例如,在路由操作中,负载部分通常是路由表条目的信息。每种类型的消息可能会有不同的负载格式。Netlink 的消息可以通过 send() 和 recv() 系统调用进行发送和接收。不同类型的消息有不同的处理方式,常见的如路由消息、网络接口变更等。struct nlmsghdr nlh;struct sockaddr_nl sa_nl;int sock_fd;nlh.nlmsg_len = NLMSG_SPACE(sizeof(struct rtmsg)); // 设置消息长度nlh.nlmsg_type = RTM_GETROUTE; // 获取路由信息nlh.nlmsg_flags = 0;nlh.nlmsg_seq = 0;nlh.nlmsg_pid = getpid();// 发送消息send(sock_fd, &nlh, nlh.nlmsg_len, 0);
char buffer[4096];int ret = recv(sock_fd, buffer, sizeof(buffer), 0);if (ret > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buffer; // 解析消息内容}
6. Netlink 与 Linux 网络栈的结合Netlink 的一个重要应用场景是与 Linux 网络栈进行交互。通过 Netlink,用户可以:- 配置网络接口(RTM_NEWLINK,RTM_DELLINK)。
- 修改防火墙规则(NETLINK_FIREWALL)。
- 获取网络统计信息(NETLINK_INET_DIAG)。
struct nlmsghdr nlh;nlh.nlmsg_type = RTM_GETROUTE; // 获取路由表
发送该消息后,内核将返回路由表信息,通过 recv() 获取该信息进行解析。Netlink 在很多 Linux 网络工具中得到了广泛应用,包括:- iproute2:这是一个命令行工具集,提供了与内核网络栈交互的功能。ip 命令就是基于 Netlink 实现的。
- iptables:Netlink 用于配置防火墙规则。
- NetworkManager:Netlink 用于动态配置网络接口。
Netlink 作为 Linux 内核与用户空间通信的重要机制,以其强大的异步通信能力、灵活的多播支持和良好的扩展性,在网络配置、系统监控、设备管理等众多领域发挥着关键作用。掌握Netlink的原理和使用方法,对于深入理解Linux系统架构和开发系统级应用具有重要意义。