使用 Netlink 协议规范¶
本文档是使用 Netlink 协议规范的快速入门指南。有关规范的更详细描述,请参阅 Netlink 协议规范(YAML 格式)。
简单 CLI¶
内核附带一个简单的 CLI 工具,在开发与 Netlink 相关的代码时应该很有用。该工具用 Python 实现,可以使用 YAML 规范向内核发出 Netlink 请求。仅支持通用 Netlink。
该工具位于 tools/net/ynl/cli.py
。它接受一些参数,最重要的参数是
--spec
- 指向规范文件
--do $name
/--dump $name
- 发出请求$name
--json $attrs
- 为请求提供属性
--subscribe $group
- 接收来自$group
的通知
YAML 规范可以在 Documentation/netlink/specs/
下找到。
使用示例
$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/ethtool.yaml \
--do rings-get \
--json '{"header":{"dev-index": 18}}'
{'header': {'dev-index': 18, 'dev-name': 'eni1np1'},
'rx': 0,
'rx-jumbo': 0,
'rx-jumbo-max': 4096,
'rx-max': 4096,
'rx-mini': 0,
'rx-mini-max': 4096,
'tx': 0,
'tx-max': 4096,
'tx-push': 0}
输入参数被解析为 JSON,而输出仅以 Python 漂亮的格式打印。这是因为某些 Netlink 类型无法直接表示为 JSON。如果输入中需要此类属性,则需要对脚本进行一些修改。
规范和 Netlink 内部结构被分解为一个独立的库 - 重用 cli.py
中的代码应该很容易编写 Python 工具/测试。
生成内核代码¶
tools/net/ynl/ynl-regen.sh
扫描内核树,查找需要更新的自动生成文件。使用此工具是生成/更新自动生成代码的最简单方法。
默认情况下,仅当规范比源文件新时才重新生成代码,要强制重新生成,请使用 -f
。
ynl-regen.sh
在文件内容中搜索 YNL-GEN
(请注意,它仅扫描 git 索引中的文件,即仅由 git 跟踪的文件!)。例如,fou_nl.c
内核源代码包含
/* Documentation/netlink/specs/fou.yaml */
/* YNL-GEN kernel source */
ynl-regen.sh
将找到此标记,并使用基于 fou.yaml 的内核源代码替换该文件。
基于规范生成新文件的最简单方法是将上面的两个标记行添加到文件中,将该文件添加到 git,然后运行重新生成工具。在树中 grep YNL-GEN
以查看其他示例。
代码生成本身由 tools/net/ynl/ynl-gen-c.py
执行,但它需要一些参数,因此直接为每个文件调用它很快就会变得繁琐。
YNL 库¶
tools/net/ynl/lib/
包含一个 C 库(基于 libmnl)的实现,该库与 tools/net/ynl/ynl-gen-c.py
生成的代码集成,以创建易于使用的 netlink 包装器。
YNL 基础知识¶
YNL 库由两部分组成 - 通用代码(函数前缀为 ynl_
)和每个系列的自动生成代码(前缀为系列的名称)。
要创建 YNL 套接字,请调用 ynl_sock_create(),并传递系列结构(系列结构由自动生成的代码导出)。ynl_sock_destroy() 关闭套接字。
YNL 请求¶
发出 YNL 请求的步骤最好用一个例子来解释。此示例中的所有函数和类型都来自自动生成的代码(在本例中为 netdev 系列)
// 0. Request and response pointers
struct netdev_dev_get_req *req;
struct netdev_dev_get_rsp *d;
// 1. Allocate a request
req = netdev_dev_get_req_alloc();
// 2. Set request parameters (as needed)
netdev_dev_get_req_set_ifindex(req, ifindex);
// 3. Issues the request
d = netdev_dev_get(ys, req);
// 4. Free the request arguments
netdev_dev_get_req_free(req);
// 5. Error check (the return value from step 3)
if (!d) {
// 6. Print the YNL-generated error
fprintf(stderr, "YNL: %s\n", ys->err.msg);
return -1;
}
// ... do stuff with the response @d
// 7. Free response
netdev_dev_get_rsp_free(d);
YNL 转储¶
执行转储遵循与请求类似的模式。转储返回一个对象列表,以特殊标记终止,或者在错误时返回 NULL。使用 ynl_dump_foreach()
迭代结果。
YNL 通知¶
YNL 库支持将同一套接字用于通知和请求。如果在处理请求期间收到通知,则会在内部排队,并且可以在稍后检索。
要订阅通知,请使用 ynl_subscribe()
。通知必须从套接字中读取,ynl_socket_get_fd()
返回底层套接字 fd,该 fd 可以插入到适当的异步 IO API 中,例如 poll
或 select
。
可以使用 ynl_ntf_dequeue()
检索通知,并且必须使用 ynl_ntf_free()
释放通知。由于我们事先不知道通知类型,因此通知以 struct ynl_ntf_base_type *
的形式返回,并且用户应根据 cmd
成员将其转换为适当的完整类型。