Logo

Linux 内核

6.16.0-rc4

快速搜索

目录

  • 开发流程
  • 提交补丁
  • 行为准则
  • 维护者手册
  • 所有开发流程文档
  • 核心 API
  • 驱动 API
  • 子系统
  • 锁
  • 许可规则
  • 编写文档
  • 开发工具
  • 测试指南
  • Hacking 指南
  • 追踪
  • 故障注入
  • 实时补丁
  • Rust
  • 管理
  • 构建系统
  • 报告问题
  • 用户空间工具
  • 用户空间 API
    • 系统调用
    • 安全相关接口
    • 设备和 I/O
    • 其他
      • Linux 特定的 ELF 特性
      • Netlink 手册
      • 平台 Profile 选择(例如 /sys/firmware/acpi/platform_profile)
      • VDUSE - “用户空间中的 vDPA 设备”
      • futex2
      • Perf 环形缓冲区
      • NT 同步原语驱动程序
  • 固件
  • 固件和设备树
  • CPU 架构
  • 未排序的文档
  • 翻译

本页

  • 显示源代码

Netlink 简介¶

Netlink 通常被描述为 ioctl() 的替代品。它旨在用一种允许轻松添加或扩展参数的格式,来替代提供给 ioctl() 的固定格式 C 结构。

为了实现这一点,Netlink 使用一个最小的固定格式元数据头,后跟多个 TLV(类型、长度、值)格式的属性。

不幸的是,该协议多年来一直在发展,以一种有机的、无文档的方式发展,这使得很难连贯地解释它。为了使其最具实用意义,本文档首先描述 Netlink 目前的使用方式,并在后面的章节中深入探讨更多“历史”用途。

打开套接字¶

Netlink 通信通过套接字进行,首先需要打开一个套接字

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);

套接字的使用允许以一种自然的方式在两个方向(往返于内核)交换信息。当应用程序 send() 请求时,操作仍然是同步执行的,但需要一个单独的 recv() 系统调用来读取回复。

因此,一个非常简化的 Netlink“调用”流程看起来像

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);

/* format the request */
send(fd, &request, sizeof(request));
n = recv(fd, &response, RSP_BUFFER_SIZE);
/* interpret the response */

Netlink 还为“转储”提供了自然支持,即向用户空间传递某种类型的所有对象(例如,转储所有网络接口)。

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);

/* format the dump request */
send(fd, &request, sizeof(request));
while (1) {
  n = recv(fd, &buffer, RSP_BUFFER_SIZE);
  /* one recv() call can read multiple messages, hence the loop below */
  for (nl_msg in buffer) {
    if (nl_msg.nlmsg_type == NLMSG_DONE)
      goto dump_finished;
    /* process the object */
  }
}
dump_finished:

socket() 调用的前两个参数几乎不需要解释 - 它正在打开一个 Netlink 套接字,所有标头都由用户提供(因此是 NETLINK、RAW)。最后一个参数是 Netlink 中的协议。此字段用于标识套接字将与之通信的子系统。

经典 Netlink 与通用 Netlink¶

Netlink 的初始实现依赖于子系统的静态 ID 分配,并且几乎没有提供支持基础设施。让我们将这些协议统称为 **经典 Netlink**。它们的列表在 `include/uapi/linux/netlink.h` 文件的顶部定义,它们包括 - 通用网络 (NETLINK_ROUTE)、iSCSI (NETLINK_ISCSI) 和审计 (NETLINK_AUDIT) 等。

**通用 Netlink**(于 2005 年推出)允许动态注册子系统(和子系统 ID 分配)、内省,并简化了接口的内核侧实现。

以下部分描述了如何使用通用 Netlink,因为使用通用 Netlink 的子系统数量比旧协议多一个数量级。也没有计划向内核添加更多经典 Netlink 协议。本文档稍后提供了有关与 Linux 内核的核心网络部分(或使用经典 Netlink 的其他 20 个子系统)进行通信的方式与通用 Netlink 的基本信息。

通用 Netlink¶

除了 Netlink 固定元数据标头之外,每个 Netlink 协议都定义了自己的固定元数据标头。(类似于网络标头的堆叠方式 - 以太网 > IP > TCP,我们有 Netlink > 通用 N. > Family。)

Netlink 消息始终以 struct nlmsghdr 开头,后跟特定于协议的标头。对于通用 Netlink,协议标头是 struct genlmsghdr。

在通用 Netlink 的情况下,字段的实际含义如下

struct nlmsghdr {
      __u32   nlmsg_len;      /* Length of message including headers */
      __u16   nlmsg_type;     /* Generic Netlink Family (subsystem) ID */
      __u16   nlmsg_flags;    /* Flags - request or dump */
      __u32   nlmsg_seq;      /* Sequence number */
      __u32   nlmsg_pid;      /* Port ID, set to 0 */
};
struct genlmsghdr {
      __u8    cmd;            /* Command, as defined by the Family */
      __u8    version;        /* Irrelevant, set to 1 */
      __u16   reserved;       /* Reserved, set to 0 */
};
/* TLV attributes follow... */

在经典 Netlink 中,nlmsghdr.nlmsg_type 用于标识消息引用的子系统中的哪个操作(例如,获取有关 netdev 的信息)。通用 Netlink 需要在单个协议中多路复用多个子系统,因此它使用此字段来标识子系统,而 genlmsghdr.cmd 则用于标识操作。(有关如何查找感兴趣的子系统的 Family ID 的信息,请参阅 解析 Family ID。)请注意,此字段的前 16 个值 (0 - 15) 保留用于经典 Netlink 和通用 Netlink 中的控制消息。有关更多详细信息,请参阅 Netlink 消息类型。

Netlink 套接字上有 3 种常见的消息交换类型

  • 执行单个操作 (do);

  • 转储信息 (dump);

  • 获取异步通知 (multicast)。

经典 Netlink 非常灵活,并且可能允许发生其他类型的交换,但在实践中,这些是使用的三种类型。

异步通知由内核发送,并由订阅它们的用户套接字接收。do 和 dump 请求由用户发起。nlmsghdr.nlmsg_flags 应按如下方式设置

  • 对于 do:NLM_F_REQUEST | NLM_F_ACK

  • 对于 dump:NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP

nlmsghdr.nlmsg_seq 应设置为单调递增的值。该值在响应中被回显,并且在实践中并不重要,但是为发送的每条消息将其设置为递增的值被认为是良好的习惯。该字段的目的是将响应与请求进行匹配。异步通知的 nlmsghdr.nlmsg_seq 将为 0。

nlmsghdr.nlmsg_pid 是 Netlink 中地址的等效项。在与内核通信时,此字段可以设置为 0。有关该字段的(不常见)用途,请参阅 nlmsg_pid。

genlmsghdr.version 的预期用途是允许对子系统提供的 API 进行版本控制。迄今为止,没有子系统对此字段进行了重大使用,因此将其设置为 1 似乎是一个安全的选择。

Netlink 消息类型¶

如前所述,nlmsghdr.nlmsg_type 携带特定于协议的值,但是前 16 个标识符是保留的(第一个特定于子系统的消息类型应等于 NLMSG_MIN_TYPE,即 0x10)。

仅定义了 4 个 Netlink 控制消息

  • NLMSG_NOOP - 忽略消息,在实践中未使用;

  • NLMSG_ERROR - 携带操作的返回代码;

  • NLMSG_DONE - 标记转储的结束;

  • NLMSG_OVERRUN - 套接字缓冲区已溢出,迄今为止未使用。

NLMSG_ERROR 和 NLMSG_DONE 具有实际意义。它们携带操作的返回代码。请注意,除非在请求上设置了 `NLM_F_ACK` 标志,否则如果没有错误,Netlink 将不会以 `NLMSG_ERROR` 进行响应。为了避免必须特殊处理此怪癖,建议始终设置 `NLM_F_ACK`。

`NLMSG_ERROR` 的格式由 struct nlmsgerr 描述

----------------------------------------------
| struct nlmsghdr - response header          |
----------------------------------------------
|    int error                               |
----------------------------------------------
| struct nlmsghdr - original request header |
----------------------------------------------
| ** optionally (1) payload of the request   |
----------------------------------------------
| ** optionally (2) extended ACK             |
----------------------------------------------

这里有两个 `struct nlmsghdr` 实例,第一个是响应,第二个是请求。NLMSG_ERROR 携带导致错误的消息的信息。这在尝试将请求与响应进行匹配或重新解析请求以将其转储到日志中时可能很有用。

请求的有效负载不会在报告成功的消息中(`error == 0`)或如果设置了 `NETLINK_CAP_ACK` setsockopt() 时回显。后者很常见,也许建议这样做,因为必须从内核读取每个请求的副本是相当浪费的。请求有效负载的缺失由 `nlmsghdr.nlmsg_flags` 中的 `NLM_F_CAPPED` 指示。

`NLMSG_ERROR` 的第二个可选元素是扩展 ACK 属性。有关更多详细信息,请参阅 `扩展 ACK`。扩展 ACK 的存在由 `nlmsghdr.nlmsg_flags` 中的 `NLM_F_ACK_TLVS` 指示。

NLMSG_DONE 更简单,请求永远不会被回显,但是可能存在扩展 ACK 属性

----------------------------------------------
| struct nlmsghdr - response header          |
----------------------------------------------
|    int error                               |
----------------------------------------------
| ** optionally extended ACK                 |
----------------------------------------------

请注意,某些实现可能会发出自定义 `NLMSG_DONE` 消息来回复 `do` 操作请求。在这种情况下,有效负载是特定于实现的,并且也可能不存在。

解析 Family ID¶

本节解释了如何查找子系统的 Family ID。它也可以作为通用 Netlink 通信的示例。

通用 Netlink 本身是一个通过通用 Netlink API 公开的子系统。为了避免循环依赖,通用 Netlink 具有静态分配的 Family ID(`GENL_ID_CTRL` 等于 `NLMSG_MIN_TYPE`)。通用 Netlink family 实现了一个命令,用于查找有关其他 family 的信息(`CTRL_CMD_GETFAMILY`)。

要获取有关通用 Netlink family(例如名为 `"test1"`)的信息,我们需要在先前打开的通用 Netlink 套接字上发送消息。该消息应以通用 Netlink Family (1) 为目标,成为对 `CTRL_CMD_GETFAMILY` 的 `do` (2) 调用 (3)。此调用的 `dump` 版本将使内核响应有关它知道的 _所有_ family 的信息。最后但并非最不重要的是,必须将所讨论的 family 的名称 (4) 指定为具有适当类型的属性

struct nlmsghdr:
  __u32 nlmsg_len:    32
  __u16 nlmsg_type:   GENL_ID_CTRL               // (1)
  __u16 nlmsg_flags:  NLM_F_REQUEST | NLM_F_ACK  // (2)
  __u32 nlmsg_seq:    1
  __u32 nlmsg_pid:    0

struct genlmsghdr:
  __u8 cmd:           CTRL_CMD_GETFAMILY         // (3)
  __u8 version:       2 /* or 1, doesn't matter */
  __u16 reserved:     0

struct nlattr:                                   // (4)
  __u16 nla_len:      10
  __u16 nla_type:     CTRL_ATTR_FAMILY_NAME
  char data:          test1\0

(padding:)
  char data:          \0\0

Netlink 中的长度字段(`nlmsghdr.nlmsg_len` 和 `nlattr.nla_len`)始终 _包括_ 标头。netlink 中的属性标头必须与从消息开始处的 4 个字节对齐,因此在 `CTRL_ATTR_FAMILY_NAME` 之后添加额外的 `\0\0`。属性长度 _不包括_ 填充。

如果找到了 family,内核将回复两条消息,即包含有关 family 所有信息的响应

/* Message #1 - reply */
struct nlmsghdr:
  __u32 nlmsg_len:    136
  __u16 nlmsg_type:   GENL_ID_CTRL
  __u16 nlmsg_flags:  0
  __u32 nlmsg_seq:    1    /* echoed from our request */
  __u32 nlmsg_pid:    5831 /* The PID of our user space process */

struct genlmsghdr:
  __u8 cmd:           CTRL_CMD_GETFAMILY
  __u8 version:       2
  __u16 reserved:     0

struct nlattr:
  __u16 nla_len:      10
  __u16 nla_type:     CTRL_ATTR_FAMILY_NAME
  char data:          test1\0

(padding:)
  data:               \0\0

struct nlattr:
  __u16 nla_len:      6
  __u16 nla_type:     CTRL_ATTR_FAMILY_ID
  __u16:              123  /* The Family ID we are after */

(padding:)
  char data:          \0\0

struct nlattr:
  __u16 nla_len:      9
  __u16 nla_type:     CTRL_ATTR_FAMILY_VERSION
  __u16:              1

/* ... etc, more attributes will follow. */

以及错误代码(成功),因为在请求上已设置 `NLM_F_ACK`

/* Message #2 - the ACK */
struct nlmsghdr:
  __u32 nlmsg_len:    36
  __u16 nlmsg_type:   NLMSG_ERROR
  __u16 nlmsg_flags:  NLM_F_CAPPED /* There won't be a payload */
  __u32 nlmsg_seq:    1    /* echoed from our request */
  __u32 nlmsg_pid:    5831 /* The PID of our user space process */

int error:            0

struct nlmsghdr: /* Copy of the request header as we sent it */
  __u32 nlmsg_len:    32
  __u16 nlmsg_type:   GENL_ID_CTRL
  __u16 nlmsg_flags:  NLM_F_REQUEST | NLM_F_ACK
  __u32 nlmsg_seq:    1
  __u32 nlmsg_pid:    0

属性(struct nlattr)的顺序无法保证,因此用户必须遍历属性并解析它们。

请注意,通用 Netlink 套接字未与单个 family 关联或绑定。套接字可以用于与许多不同的 family 交换消息,使用 `nlmsghdr.nlmsg_type` 字段在逐条消息的基础上选择接收 family。

扩展 ACK¶

扩展 ACK 控制在 `NLMSG_ERROR` 和 `NLMSG_DONE` 消息中报告附加的错误/警告 TLV。为了保持向后兼容性,必须通过将 `NETLINK_EXT_ACK` setsockopt() 设置为 `1` 来显式启用此功能。

扩展 ack 属性的类型在 `enum nlmsgerr_attrs` 中定义。最常用的属性是 `NLMSGERR_ATTR_MSG`、`NLMSGERR_ATTR_OFFS` 和 `NLMSGERR_ATTR_MISS_*`。

`NLMSGERR_ATTR_MSG` 携带一条英文消息,描述遇到的问题。这些消息比可以通过标准 UNIX 错误代码表达的消息详细得多。

`NLMSGERR_ATTR_OFFS` 指向导致问题的属性。

`NLMSGERR_ATTR_MISS_TYPE` 和 `NLMSGERR_ATTR_MISS_NEST` 告知缺少属性。

扩展 ACK 可以在错误以及成功的情况下报告。后者应被视为警告。

扩展 ACK 极大地提高了 Netlink 的可用性,并且应始终启用、适当地解析并报告给用户。

高级主题¶

转储一致性¶

内核用于存储对象的一些数据结构使得很难提供转储中所有对象的原子快照(而不影响更新它们的快速路径)。

如果转储被中断并且可能不一致(例如,缺少对象),内核可能会在转储中的任何消息(包括 `NLMSG_DONE` 消息)上设置 `NLM_F_DUMP_INTR` 标志。如果用户空间看到设置了该标志,则应重试转储。

内省¶

通过访问在 `解析 Family ID` 中报告的 Family 对象来启用基本内省功能。用户可以查询有关通用 Netlink family 的信息,包括内核支持的操作和内核理解的属性。Family 信息包括内核可以解析的属性的最高 ID,单独的命令(`CTRL_CMD_GETPOLICY`)提供了有关支持的属性的详细信息,包括内核接受的值范围。

在用户空间需要确保内核在发出请求之前支持某项功能的情况下,查询 family 信息很有用。

nlmsg_pid¶

nlmsghdr.nlmsg_pid 是 Netlink 中地址的等效项。它被称为端口 ID,有时也称为进程 ID,因为出于历史原因,如果应用程序未选择(bind() 到)显式端口 ID,则内核将自动为其分配等于其进程 ID 的 ID(如 getpid() 系统调用所报告的)。

与 TCP/IP 网络协议的 bind() 语义类似,零值表示“自动分配”,因此应用程序通常将 `nlmsghdr.nlmsg_pid` 字段初始化为 `0`。

当内核需要发送单播通知时,该字段今天仍然在极少数情况下使用。用户空间应用程序可以使用 bind() 将其套接字与特定的 PID 关联,然后将其 PID 传递给内核。这样,内核就可以到达特定的用户空间进程。

这种通信在 UMH(用户模式助手)之类的场景中得到利用,当内核需要触发用户空间处理或向用户空间询问策略决策时。

多播通知¶

Netlink 的优势之一是能够向用户空间发送事件通知。这是一种单向通信形式(内核 -> 用户),不涉及任何控制消息,例如 `NLMSG_ERROR` 或 `NLMSG_DONE`。

例如,通用 Netlink family 本身定义了一组关于已注册 family 的多播通知。添加新 family 时,已订阅通知的套接字将收到以下消息

struct nlmsghdr:
  __u32 nlmsg_len:    136
  __u16 nlmsg_type:   GENL_ID_CTRL
  __u16 nlmsg_flags:  0
  __u32 nlmsg_seq:    0
  __u32 nlmsg_pid:    0

struct genlmsghdr:
  __u8 cmd:           CTRL_CMD_NEWFAMILY
  __u8 version:       2
  __u16 reserved:     0

struct nlattr:
  __u16 nla_len:      10
  __u16 nla_type:     CTRL_ATTR_FAMILY_NAME
  char data:          test1\0

(padding:)
  data:               \0\0

struct nlattr:
  __u16 nla_len:      6
  __u16 nla_type:     CTRL_ATTR_FAMILY_ID
  __u16:              123  /* The Family ID we are after */

(padding:)
  char data:          \0\0

struct nlattr:
  __u16 nla_len:      9
  __u16 nla_type:     CTRL_ATTR_FAMILY_VERSION
  __u16:              1

/* ... etc, more attributes will follow. */

该通知包含与对 `CTRL_CMD_GETFAMILY` 请求的响应相同的信息。

通知的 Netlink 标头大多为 0 且不相关。`nlmsghdr.nlmsg_seq` 可以为零,也可以是 family 维护的单调递增的通知序列号。

要接收通知,用户套接字必须订阅相关的通知组。与 Family ID 非常相似,给定多播组的 Group ID 是动态的,可以在 Family 信息中找到。`CTRL_ATTR_MCAST_GROUPS` 属性包含具有组的名称(`CTRL_ATTR_MCAST_GRP_NAME`)和 ID(`CTRL_ATTR_MCAST_GRP_ID`)的嵌套。

一旦知道 Group ID,setsockopt() 调用会将套接字添加到组中

unsigned int group_id;

/* .. find the group ID... */

setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
           &group_id, sizeof(group_id));

套接字现在将接收通知。

建议使用单独的套接字来接收通知和向内核发送请求。通知的异步性质意味着它们可能会与响应混合在一起,从而使消息处理变得更加困难。

缓冲区大小调整¶

Netlink 套接字是数据报套接字而不是流套接字,这意味着每条消息都必须通过单个 recv()/recvmsg() 系统调用完整地接收。如果用户提供的缓冲区太短,则消息将被截断,并且在 struct msghdr 中设置 `MSG_TRUNC` 标志(struct msghdr 是 recvmsg() 系统调用的第二个参数,_不是_ Netlink 标头)。

截断时,消息的剩余部分将被丢弃。

Netlink 期望用户缓冲区至少为 8kB 或 CPU 架构的页面大小,以较大者为准。但是,特定的 Netlink family 可能需要更大的缓冲区。建议使用 32kB 缓冲区来最有效地处理转储(更大的缓冲区可以容纳更多转储的对象,因此需要更少的 recvmsg() 调用)。

经典 Netlink¶

经典 Netlink 和通用 Netlink 之间的主要区别在于子系统标识符的动态分配和内省的可用性。从理论上讲,该协议没有显着差异,但是,在实践中,经典 Netlink 尝试了一些在通用 Netlink 中被放弃的概念(实际上,它们通常只在单个子系统的一小部分中找到用途)。本节旨在解释其中的一些概念,其明确目标是让通用 Netlink 用户在阅读 uAPI 标头时有信心忽略它们。

这里的大多数概念和示例都指的是 `NETLINK_ROUTE` family,它涵盖了 Linux 网络堆栈的大部分配置。对该 family 的真实文档,值得单独写一章(或一本书)。

Family¶

Netlink 将子系统称为 family。这是使用套接字和协议 family 概念的残余,协议 family 是 `NETLINK_ROUTE` 中消息多路分解的一部分。

可悲的是,每一层封装都喜欢将它携带的任何东西称为“family”,这使得这个术语非常令人困惑

  1. AF_NETLINK 是一个真正的套接字协议 family

  2. AF_NETLINK 的文档将消息中自身标头(`struct nlmsghdr`)后面的内容称为“Family 标头”

  3. 通用 Netlink 是 AF_NETLINK 的一个 family(struct genlmsghdr 遵循 `struct nlmsghdr`),但它也称其用户为“Family”。

请注意,通用 Netlink Family ID 位于不同的“ID 空间”中,并且与经典 Netlink 协议号重叠(例如,`NETLINK_CRYPTO` 的经典 Netlink 协议 ID 为 21,通用 Netlink 也会很高兴地将其分配给它的一个 family)。

严格检查¶

`NETLINK_GET_STRICT_CHK` 套接字选项启用 `NETLINK_ROUTE` 中的严格输入检查。之所以需要它,是因为历史上内核没有验证它没有处理的结构的字段。这使得以后开始使用这些字段变得不可能,而不会冒应用程序错误初始化或根本不初始化它们的风险。

`NETLINK_GET_STRICT_CHK` 声明应用程序正在正确初始化所有字段。它还选择验证消息是否不包含尾随数据,并请求内核拒绝类型高于内核已知的最大属性类型的属性。

`NETLINK_GET_STRICT_CHK` 不在 `NETLINK_ROUTE` 之外使用。

未知属性¶

历史上,Netlink 忽略了所有未知属性。想法是它可以让应用程序无需探测内核支持的内容。应用程序可以发出更改状态的请求,并检查请求的哪些部分“卡住了”。

对于新的通用 Netlink family 和选择严格检查的 family 来说,情况不再如此。有关执行的验证类型,请参见 enum netlink_validation。

固定元数据和结构¶

经典 Netlink 在消息中大量使用了固定格式的结构。消息通常在 `struct nlmsghdr` 之后有一个包含相当数量字段的结构。也很常见的是将具有多个成员的结构放入属性中,而不将每个成员分解为它自己的属性。

这导致了验证和可扩展性的问题,因此对于新属性,不再鼓励使用二进制结构。

请求类型¶

`NETLINK_ROUTE` 将请求分为 4 种类型 `NEW`、`DEL`、`GET` 和 `SET`。每个对象都可以处理所有或某些请求(对象是 netdev、路由、地址、qdisc 等)。请求类型由消息类型的最低 2 位定义,因此新对象的命令将始终以 4 的步幅分配。

每个对象还将具有所有请求类型共享的自己的固定元数据(例如,用于 netdev 请求的 struct ifinfomsg,用于地址请求的 struct ifaddrmsg,用于 qdisc 请求的 struct tcmsg)。

即使其他协议和通用 Netlink 命令经常在其消息名称中使用相同的动词(`GET`、`SET`),请求类型的概念也没有得到更广泛的采用。

通知回显¶

`NLM_F_ECHO` 请求用于请求的回调通知被排队到请求套接字上。这对于发现请求的影响很有用。

请注意,此功能不是普遍实现的。

其他特定于请求类型的标志¶

经典 Netlink 在 `struct nlmsghdr` 中定义了 nlmsg_flags 上字节中 `GET`、`NEW` 和 `DEL` 请求的各种标志。由于请求类型尚未被概括化,因此特定于请求类型的标志很少使用(并且被认为对于新 family 来说已弃用)。

对于 `GET` - `NLM_F_ROOT` 和 `NLM_F_MATCH` 被合并为 `NLM_F_DUMP`,并且不单独使用。`NLM_F_ATOMIC` 从未使用过。

对于 `DEL` - `NLM_F_NONREC` 仅由 nftables 使用,而 `NLM_F_BULK` 仅由 FDB 的某些操作使用。

用于 `NEW` 的标志在经典 Netlink 中最常用。不幸的是,其含义并不十分清楚。以下描述基于对作者意图的最佳猜测,并且在实践中,所有 family 都以某种方式偏离它。`NLM_F_REPLACE` 要求替换现有对象,如果没有匹配的对象存在,则操作应失败。`NLM_F_EXCL` 具有相反的语义,并且只有在对象已经存在时才成功。`NLM_F_CREATE` 要求如果对象不存在则创建该对象,它可以与 `NLM_F_REPLACE` 和 `NLM_F_EXCL` 结合使用。

主 Netlink uAPI 标头中的注释说明

4.4BSD ADD           NLM_F_CREATE|NLM_F_EXCL
4.4BSD CHANGE        NLM_F_REPLACE

True CHANGE          NLM_F_CREATE|NLM_F_REPLACE
Append               NLM_F_CREATE
Check                NLM_F_EXCL

这似乎表明这些标志早于请求类型。没有 `NLM_F_CREATE` 的 `NLM_F_REPLACE` 最初用于代替 `SET` 命令。没有 `NLM_F_CREATE` 的 `NLM_F_EXCL` 用于检查对象是否存在而不创建它,大概早于 `GET` 命令。

`NLM_F_APPEND` 指示如果一个键可以具有与其关联的多个对象(例如,路由的多个下一跳对象),则应将新对象添加到列表中,而不是替换整个列表。

uAPI 参考¶

struct nlmsghdr¶

Netlink 消息的固定格式元数据标头

定义:

struct nlmsghdr {
    __u32 nlmsg_len;
    __u16 nlmsg_type;
    __u16 nlmsg_flags;
    __u32 nlmsg_seq;
    __u32 nlmsg_pid;
};

成员

nlmsg_len

消息长度(包括标头)

nlmsg_type

消息内容类型

nlmsg_flags

附加标志

nlmsg_seq

序列号

nlmsg_pid

发送进程端口 ID

enum nlmsgerr_attrs¶

nlmsgerr 属性

常量

NLMSGERR_ATTR_UNUSED

未使用

NLMSGERR_ATTR_MSG

错误消息字符串(字符串)

NLMSGERR_ATTR_OFFS

原始消息中无效属性的偏移量,从标头开始计数 (u32)

NLMSGERR_ATTR_COOKIE

任意特定于子系统的 cookie,用于(在成功情况下)标识创建的对象或操作或类似的东西(二进制)

NLMSGERR_ATTR_POLICY

拒绝属性的策略

NLMSGERR_ATTR_MISS_TYPE

缺失的必需属性的类型,如果属性在消息级别缺失,则不会出现 `NLMSGERR_ATTR_MISS_NEST`

NLMSGERR_ATTR_MISS_NEST

属性缺失的嵌套的偏移量

__NLMSGERR_ATTR_MAX

属性数量

NLMSGERR_ATTR_MAX

最高属性编号

enum netlink_attribute_type¶

属性类型

常量

NL_ATTR_TYPE_INVALID

未使用

NL_ATTR_TYPE_FLAG

标志属性(存在/不存在)

NL_ATTR_TYPE_U8

8 位无符号属性

NL_ATTR_TYPE_U16

16 位无符号属性

NL_ATTR_TYPE_U32

32 位无符号属性

NL_ATTR_TYPE_U64

64 位无符号属性

NL_ATTR_TYPE_S8

8 位有符号属性

NL_ATTR_TYPE_S16

16 位有符号属性

NL_ATTR_TYPE_S32

32 位有符号属性

NL_ATTR_TYPE_S64

64 位有符号属性

NL_ATTR_TYPE_BINARY

二进制数据,可以指定最小/最大长度

NL_ATTR_TYPE_STRING

字符串,可以指定最小/最大长度

NL_ATTR_TYPE_NUL_STRING

NUL 终止字符串,可以指定最小/最大长度

NL_ATTR_TYPE_NESTED

嵌套,即此属性的内容由子属性组成。可以指定嵌套策略和内部的最大类型。

NL_ATTR_TYPE_NESTED_ARRAY

嵌套数组,即此属性的内容包含子属性,其类型无关紧要(仅用于分隔数组条目),并且每个此类数组条目再次具有属性,可以指定这些内部属性的策略和相应的最大类型。

NL_ATTR_TYPE_BITFIELD32

struct nla_bitfield32 属性

NL_ATTR_TYPE_SINT

32 位或 64 位有符号属性,对齐到 4B

NL_ATTR_TYPE_UINT

32 位或 64 位无符号属性,对齐到 4B

enum netlink_policy_type_attr¶

策略类型属性

常量

NL_POLICY_TYPE_ATTR_UNSPEC

未使用

NL_POLICY_TYPE_ATTR_TYPE

属性的类型,enum netlink_attribute_type (U32)

NL_POLICY_TYPE_ATTR_MIN_VALUE_S

有符号整数的最小值 (S64)

NL_POLICY_TYPE_ATTR_MAX_VALUE_S

有符号整数的最大值 (S64)

NL_POLICY_TYPE_ATTR_MIN_VALUE_U

无符号整数的最小值 (U64)

NL_POLICY_TYPE_ATTR_MAX_VALUE_U

无符号整数的最大值 (U64)

NL_POLICY_TYPE_ATTR_MIN_LENGTH

二进制属性的最小长度,如果未给定则没有最小值 (U32)

NL_POLICY_TYPE_ATTR_MAX_LENGTH

二进制属性的最大长度,如果未给定则没有最大值 (U32)

NL_POLICY_TYPE_ATTR_POLICY_IDX

嵌套和嵌套数组类型的子策略 (U32)

NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE

嵌套和嵌套数组类型的最大子策略属性,理论上可以小于索引指向的策略的大小,如果在嵌套内限制 (U32)

NL_POLICY_TYPE_ATTR_BITFIELD32_MASK

bitfield32 类型的有效掩码 (U32)

NL_POLICY_TYPE_ATTR_PAD

用于 64 位对齐的填充属性

NL_POLICY_TYPE_ATTR_MASK

无符号整数的有效位掩码 (U64)

__NL_POLICY_TYPE_ATTR_MAX

属性数量

NL_POLICY_TYPE_ATTR_MAX

最高属性编号

©内核开发社区。 | 由 Sphinx 5.3.0 & Alabaster 0.7.16 驱动 | 页面源代码