公司动态

libnl简单解析

📅 2026/8/14 6:29:43
libnl简单解析
最近在调试hostapd和驱动进行交互由于Hostapd和内核之间的交互使用的是nl80211nl80211使用netlink协议想使用netlink协议就需要使用libnl库、使用里面的函数所以这里简单解析一下libnl。可以说是阅读和解析官方文档的一个笔记。libnl‌与nl80211‌区别‌libnl‌主要用于简化开发者与内核网络栈之间的交互提供丰富的API接口支持多种Netlink消息类型提高开发效率和降低错误率。‌nl80211‌位于用户空间与内核空间之间通过Netlink协议进行信息交互主要用于802.11设备的配置和管理生成事件信息。简单解析libnl是有比较标准的库说明文件的Documentation Overview - libnl Suite总述Netlink Library (libnl)套接字处理、发送和接收、消息构造和解析等libnlRouting Family Library (libnl-route)Netfilter Library (libnl-nf)Generic Netlink Library (libnl-genl)Routing Family Library (libnl-route)地址、链路、邻居、路由、流量控制、邻居表等Netfilter Library (libnl-nf)连接跟踪日志记录排队Generic Netlink Library (libnl-genl)控制器API家族和命令注册Core library核心库包含了通信所需的基础知识 通过netlink套接字。它处理的是连接和断开 套接字数据的发送和接收构造和解析 消息提供可定制的接收状态机以及 提供了一个抽象的数据类型框架简化了实现 基于对象的netlink协议其中对象被添加、删除或 使用基于netlink的协议进行了修改。调试信息使用调试打开讲道理可以看到hostapd和内核的交互。一个开关是NLDBG对应关系如下 如果环境变量NLDBG0则打印调试信息到stderrLevelDescription0Debugging disabled (default)1Warnings, important events and notifications2More or less important debugging messages3Repetitive events causing a flood of debugging messages4Even less important messages另一个开关是NLCB设置环境变量NLCBdebugwill 导致使用调试消息处理程序该处理程序将打印 Netlink消息以人类可读的格式交换到tostderr。这两个开关不是编译时候加入的而是启动应用程序时指定为环境变量被链接的libnl库自然就会打印$ NLDBG2 ./myprogram $ NLCBdebug ./myprogramNetlink协议基础netlink协议是一种基于套接字的IPC机制用于 用户空间进程与内核之间或内核与内核之间的通信 用户空间进程自身。netlink协议是基于BSD的 套接字和使用AF_NETLINK地址族。每个netlink 协议使用自己的协议号(例如NETLINK_ROUTENETLINK_NETFILTER等)。它的寻址模式是基于32位的 端口号以前称为PID唯一标识 每一个通信对象。寻址netlink地址端口由32位整数组成。端口0 (0) 是为内核保留的指向每个 Netlink协议族。其他端口号通常指用户空间 拥有的套接字尽管这不是强制的。上图说明了3个应用程序和内核端两个暴露内核socket。它展示了常见的netlink使用 例:User Space to Kernel 从用户空间到内核最常见的netlink使用形式是用户空间应用程序 向内核发送请求并处理响应 错误信息或成功通知。User Space to User Space 用户空间到用户空间Netlink也可以用作用户之间通信的IPC机制 直接应用于空间。通信不限于两个对等点 任意数量的端可以相互通信和组播 功能允许通过一条消息到达多个节点。为了让两个套接字彼此可见两个套接字必须都是可见的 为同一个netlink协议族创建。User space listening to kernel notifications 用户空间监听内核通知这种形式的netlink通信通常出现在需要对某些内核事件起作用的用户空间守护进程中。这样的守护进程通常会维护一个订阅多播组的netlink套接字内核使用该套接字将特定事件通知感兴趣的用户空间各方。与直接寻址相比使用多播更可取因为它可以灵活地随时交换用户空间组件而不会引起内核的注意。消息格式netlink协议通常基于消息由 Netlink消息头 struct nlmsghdr 加上附加的有效负载 到它。有效载荷可以由任意数据组成但通常包含 一个固定大小的特定协议头后跟一个流 属性。Netlink message header (struct nlmsghdr)消息的总长度以字节为单位包括netlink消息头。消息类型指定了消息携带的有效载荷的类型。 netlink协议定义了几种标准消息类型。 每个协议族可以定义其他消息类型。看到 附加信息的消息类型。消息标志可用于修改消息类型的行为。 有关标准消息标志的列表请参阅消息标志部分。序列号是可选的可以用于引用 前一个消息例如错误消息可以引用原始消息 请求导致错误。端口号指定消息要发送到的对等体。如果没有指定消息将被传递到同一协议族的第一个匹配的内核端套接字。消息类型requests, notifications, and repliesNetlink是不同的。请求是设置了NLM_F_REQUEST标志的消息用于请求接收方的操作。请求通常从用户空间进程发送到内核。虽然没有严格强制执行但请求应该携带一个序列号每个请求都要递增。根据请求的性质接收方可以用另一个netlink消息回复请求。应答的序列号必须与其所涉及的请求的序列号相匹配。通知是非正式性质的不需要回复因此序号通常设置为0。消息的类型主要由消息头中设置的16位消息类型来标识。定义了以下标准消息类型NLMSG_NOOP-无操作消息必须丢弃NLMSG_ERROR-错误信息或ACK参见错误信息 分别ackNLMSG_DONE-多部分序列结束参见多部分消息NLMSG_OVERRUN-溢出通知错误通常使用自己的消息类型来实现RPC‌RPC模式Remote Procedure Call模式。 假设要实现的netlink协议的目标是allow 配置一个特定的网络设备因此你想要 提供对各种配置选项的读/写访问。典型的 “netlink方式”可以定义两种消息类型MSG_SETCFG、MSG_GETCFG:#define MSG_SETCFG 0x11 #定义MSG_SETCFG 0x11 #define MSG_GETCFG 0x12 #定义MSG_GETCFG 0x12发送一个MSG_GETCFG请求消息通常会触发一个包含当前配置的消息类型MSG_SETCFG的回复。在面向对象的术语中可以将其描述为“内核在用户空间中设置配置的本地副本”。可以通过发送MSG_SETCFG来更改配置该配置将以ACK参见ACKs或错误消息参见错误消息进行响应。Hostapd里面出现的-22可能就是这个命令返回来的可选地内核可以发送配置更改通知允许用户空间侦听更改而不是频繁轮询。通知通常重用现有的消息类型并依赖于使用单独套接字的应用程序来区分请求和通知但您也可以指定单独的消息类型。多部分消息虽然理论上一个netlink消息的大小可以达到4gb。套接字缓冲区很可能不够大无法容纳如此大小的消息。因此通常将消息限制为一个页面大小PAGE_SIZE并使用多部分机制将大数据块拆分为几个消息。多部分消息设置了NLM_F_MULTI标志并且期望接收方继续接收和解析直到接收到特殊的消息类型NLMSG_DONE。多部分消息不像碎片ip数据包那样必须不能重新组装即使如果协议希望这样做的话这样做是完全合法的。通常多部分消息用于发送对象列表或树每个多部分消息只是携带多个对象允许每个消息独立解析。Netlink Sockets为了使用netlink协议需要一个netlink套接字。每个套接字为消息的发送和接收定义了一个独立的上下文。应用程序可以使用多个套接字例如一个套接字发送请求和接收应答另一个套接字订阅多播组来接收通知。套接字结构struct nl_socknetlink套接字和包括实际文件描述符在内的所有相关属性都由struct nl_sock表示。#include netlink/socket.h struct nl_sock *nl_socket_alloc(void) void nl_socket_free(struct nl_sock *sk) # include netlink / socket.h Struct nl_sock *nl_socket_allocvoid Void nl_socket_freestruct nl_sock *sk应用程序必须为它希望使用的每个netlink套接字分配一个nl_sock结构体的实例。Sequence Numbersl