ethtool netlink 框架原理浅析
2019 年 11 月我写了 ethtool 的工作原理 这篇博客,描述了 ethtool 使用 ioctl 获取信息的方式。谁曾想在 2019 年 12 月的时候内核已经开始支持 ethtool netlink 框架。
相关的 commit 如下:
commit 2b4a8990b7df55875745a80a609a1ceaaf51f322
Author: Michal Kubecek <mkubecek@suse.cz>
Date: Fri Dec 27 15:55:18 2019 +0100ethtool: introduce ethtool netlink interfaceBasic genetlink and init infrastructure for the netlink interface, registergenetlink family "ethtool". Add CONFIG_ETHTOOL_NETLINK Kconfig option tomake the build optional. Add initial overall interface description intoDocumentation/networking/ethtool-netlink.rst, further patches will add moredetailed information.Signed-off-by: Michal Kubecek <mkubecek@suse.cz>Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>Reviewed-by: Andrew Lunn <andrew@lunn.ch>Signed-off-by: David S. Miller <davem@davemloft.net>
我最近才从同事那里了解到这一变化,不由觉得自己看待问题少了些变化的角度。
基础知识
在 使用 strace、tcpdump、nlmon、wireshark 探索 ethtool netlink 框架的原理 这篇博客中,我通过 strace 等几个相关的强力工具描述了 ethtool 命令使用 ethtool netlink 获取信息的原理,本文探讨下内核部分的实现原理。
ethtool 通过 netlink 获取信息时内核的执行流
在 debian11 系统中,我使用 ftrace 跟踪执行 ethtool 命令时 r8169 驱动中的 get_wol 调用,得到如下内核调用栈信息:
ethtool-8117 [000] ..... 17227.018504: rtl8169_get_wol <-wol_prepare_dataethtool-8117 [000] ..... 17227.018534: <stack trace>=> 0xffffffffc0c94083=> rtl8169_get_wol=> wol_prepare_data=> ethnl_default_doit=> genl_family_rcv_msg_doit=> genl_rcv_msg=> netlink_rcv_skb=> genl_rcv=> netlink_unicast=> netlink_sendmsg=> sock_sendmsg=> __sys_sendto=> __x64_sys_sendto=> do_syscall_64=> entry_SYSCALL_64_after_hwframe
调用层次图示:
我将参考上图,自上而下描述内核中的实现原理。
用户态使用 ethtool netlink 与内核交互的消息实例
用户态发送给内核的消息:
Frame 52: 52 bytes on wire (416 bits), 52 bytes captured (416 bits)
Linux netlink (cooked header)Link-layer address type: Netlink (824)Family: Generic (0x0010)
Linux Generic Netlink protocolNetlink message header (type: 0x0014)Length: 36Family ID: 0x14 (ethtool)Flags: 0x0005Sequence: 2Port ID: 0Command: 4Family Version: 1Reserved
Data (32 bytes)Data: 14000500020000000000000004010000100001800b000200656e703173300000[Length: 32]
内核返回给用户态的消息:
Frame 53: 432 bytes on wire (3456 bits), 432 bytes captured (3456 bits)
Linux netlink (cooked header)Link-layer address type: Netlink (824)Family: Generic (0x0010)
Linux Generic Netlink protocolNetlink message header (type: 0x0014)Length: 416Family ID: 0x14 (ethtool)Flags: 0x0000Sequence: 2Port ID: 45683Command: 4Family Version: 1Reserved
Data (412 bytes)Data: 140000000200000073b20000040100001800018008000100020000000b000200656e7031…[Length: 412]
摘自 使用 strace、tcpdump、nlmon、wireshark 探索 ethtool netlink 框架的原理。
generic netlink 与 ethtool netlink
generic netlink 框架及内部协议族
框架代码位于内核源码树 net/netlink/genetlink.c
文件中,此框架为每个 namespace 的 net 结构创建一个名为 genl_sock 的 struct sock
结构,并将实例化的 netlink sock 结构的 netlink_rcv 收包函数设置为 genl_rcv
。
当用户态通过 generic netlink socket 发送 netlink 消息时,内核中调用到 netlink_unicast 接口时会调用注册的 netlink_rcv 函数接收消息,在 generic netlink 框架中就对应 genl_rcv
函数。
同时 generic netlink 框架提供一套接口,允许用户注册、解除注册以struct genl_family
结构标识的 generic netlink 内部协议族。每个协议族通过 name 区分,内核会为每个协议族分配一个唯一的 family id。
上文用户态发送给内核的 netlink 消息中的 Family ID 为 0x14,0x14 就是 ethtool netlink 的协议号。
genl_rcv 函数的主要逻辑
调用 netlink_rcv_skb 函数依次解析 netlink 报文的头部、校验关键字段合法性、调用 genl_rcv_msg 函数、判断是否需要回复 ack、需要回复则回复
genl_rcv_msg 函数的主要逻辑
- 根据 netlink headers 的 nlmsg_type 字段获取到注册的 genl_family 协议族结构
- 不支持并行调用时,获取互斥锁
- 调用 genl_family_rcv_msg 解析消息命令并执行
- 在不支持并行调用时,释放互斥锁
genl_family_rcv_msg 的主要逻辑
根据 netlink headers 获取消息的 payload 并解析为一个 generic netlink 消息头,然后通过消息头中的 cmd 字段从内部协议族中获取到一个 struct genl_ops 实例。get_wol 消息对应如下实现:
{.cmd = ETHTOOL_MSG_WOL_GET,.flags = GENL_UNS_ADMIN_PERM,.doit = ethnl_default_doit,.start = ethnl_default_start,.dumpit = ethnl_default_dumpit,.done = ethnl_default_done,.policy = ethnl_wol_get_policy,.maxattr = ARRAY_SIZE(ethnl_wol_get_policy) - 1,},
获取到 genl_ops 后,首先检查权限,权限检查通过后根据 netlink 头的 nlmsg_flags 配置,调用 generic netlink 的 dumpit 函数、doit 函数。
对于 get_wol 消息,流程转向 genl_family_rcv_msg_doit
。
genl_family_rcv_msg_doit 的功能
- 解析并创建 netlink attribute buff
- 调用协议族中的 pre_doit 函数
- 调用 genl_ops 中的 doit 函数
- 调用协议族中的 post_doit 函数
- 释放 netlink attribute buff
genl_ops 中的 doit 函数调用在 ethtool netlink genl_ops 中均指向 ethnl_default_doit
,此函数标志流程进入下一个层次。
ethnl_default_doit 的功能
- 根据 generic netlink 头中的 cmd 字段查找
ethnl_default_requests
表,得到一个struct ethnl_request_op
实例。get_wol 消息中对应ethnl_wol_request_ops
实例 - 解析 netlink attribute 并执行相关回调
- 依次调用 struct ethnl_request_op 结构中的 prepare_data、reply_size、fill_reply 方法获取数据并填充到回复消息中(回复消息直接使用源消息空间构造),然后调用
genlmsg_reply
命令回复消息
ethnl_wol_request_ops
的 prepare_data 函数为 wol_prepare_data
,此函数将流程推向网络设备驱动 ethtool_ops
层。
wol_prepare_data 函数
- 调用网络设备驱动 ethtool_ops 中的 begin 方法
- 调用网络设备驱动 ethtool_ops 中的 get_wol 方法(对应 r8169 驱动实现的 rtl8169_get_wol 函数)
- 调用网络设备驱动 ethtool_ops 中的 complete 方法
此函数封装对网络设备驱动 ethtool_ops 实例方法的调用过程。
相关内核代码的结构
.
├── bitset.c
├── bitset.h
├── cabletest.c
├── channels.c
├── coalesce.c
├── common.c
├── common.h
├── debug.c
├── eee.c
├── features.c
├── ioctl.c
├── linkinfo.c
├── linkmodes.c
├── linkstate.c
├── Makefile
├── netlink.c
├── netlink.h
├── pause.c
├── privflags.c
├── rings.c
├── strset.c
├── tsinfo.c
├── tunnels.c
└── wol.c
netlink.c 为框架内容,ioctl.c 为 ioctl 方式实现,其它的每种类型 ethtool cmd 命令的解析与执行单独创建一个源文件实现。
分层设计
ethtool netlink 框架跨越了如下几个抽象层次:
- socket netlink
- generic netlink family
- ethtool generic netlink ops
- ethtool netlink request ops
- driver ethtool_ops
ethtool netlink 消息有如下几个组成部分:
- nlmsghdr
- genlmsghdr
- attribute
消息的不同组成部分在不同的层次中被使用,解析消息需要两层,适配 ethtool_ops 需要一层,这些层次都封装了一种类型功能的变化,有良好的扩展能力。
提问
- 如何将 ethtool netlink 嵌入到内核现有的 netlink 框架中?
- ethtool netlink 如何找到底层的网络设备进而调用 ethtool_ops 中的方法?
- 为啥需要 genl_ops 与 ethnl_request_ops?两者能合并吗?
- 与传统 ioctl 接口获取信息,ethtool netlink 框架变化的是什么?不变的又是什么?
- 使用 ethtool netlink 框架的优势
参考链接
https://www.kernel.org/doc/html/latest/networking/ethtool-netlink.html
https://lwn.net/Articles/783633/
https://blog.csdn.net/Longyu_wlz/article/details/126216625
ethtool netlink 框架原理浅析相关推荐
- 使用 strace、tcpdump、nlmon、wireshark 探索 ethtool netlink 框架的原理
目录 前言 环境信息与准备工作 环境信息 准备工作 一些基础知识 netlink 消息的一些标志 从系统调用角度观测 netlink 套接字的创建与 bind 使用 nlctrl 类型 netlink ...
- vue.js框架原理浅析
vue.js是一个非常优秀的前端开发框架,不是我说的,大家都知道. 首先我现在的能力,独立阅读源码还是有很大压力的,所幸vue写的很规范,通过方法名基本可以略知一二,里面的原理不懂的地方多方面查找资料 ...
- vue.js框架原理浅析 1
vue.js是一个非常优秀的前端开发框架,不是我说的,大家都知道. 首先我现在的能力,独立阅读源码还是有很大压力的,所幸vue写的很规范,通过方法名基本可以略知一二,里面的原理不懂的地方多方面查找资料 ...
- 20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG01
20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG01 编译环境:Ubuntu18.04.6 64位 内存:48GB [BEGIN] 2022/12/24 8:28 ...
- 20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG02
20230103编译ToyBrick的TB-RK3588X的Andorid12的LOG02 编译环境:Ubuntu18.04.6 64位 内存:48GB CC drivers/android ...
- 服务器虚拟交换机到网卡不通,浅析虚拟化环境网卡绑定模式
作者:郭国华,新炬网络高级技术专家. 随着云计算的发展,虚拟化技术已普遍应用于各类型企业,同时虚拟化为服务器带来密集的网络吞吐压力已成为影响云计算发展的最大性能瓶颈之一,为了最大化提升主机网络性能,对 ...
- 沉淀,再出发:docker的原理浅析
沉淀,再出发:docker的原理浅析 一.前言 在我们使用docker的时候,很多情况下我们对于一些概念的理解是停留在名称和用法的地步,如果更进一步理解了docker的本质,我们的技术一定会有质的进步 ...
- Android ueventd浅析
0.备模型中任何设备有事件需要上报时,会触发 uevent提供的接口,uevent模块准备好上报事件的格式后,可以通过两个途径上报到用户空间: 通过 kmod 模块,直接调动用户空间的可执行文件 通过 ...
- Linux下通过NetLink获取网口信息
原本是打算通过ioctl这个api获取网口是否插线状态,结果设备网口和驱动适配有问题 查出来的结果跟实际网口对不上 即使用ifconfig也是对不上 (ifconfig也是用的ioctl) 然后发现e ...
最新文章
- 2020 年,AI 芯片内存哪家强?
- Jenkins - 构建Allure Report
- 利用多线程解决多业务不同定时区间歇触发问题的一种方法
- rman datafile恢复(归档模式)
- NodeJS开发c++扩展模块
- 基于 OpenFire 的TVBox管理平台开发笔记
- 基于spring注解AOP的异常处理
- dbgrideh标题排序
- 【elasticsearch】 flink es sink Connection refused
- R语言编程基础(1)
- 【Java】 剑指offer(65) 不用加减乘除做加法
- Spring:连接池连接数据库报错Unknown system variable ‘tx_isolation‘
- wps linux 64 下载,WPS For Linux 10.1.0.6757社区版发布下载
- 松下服务器报警13参数修改,新版松下伺服发生故障报警代码一览及对策.doc
- Maven2的新特性
- 拉宾-卡普算法详解以及示例[rabin-karp]
- 2017最新申请苹果开发者账号,申请appstore开发者账号,申请app开发者账号,申请苹果企业公司开发者账号
- 深入浅出matplotlib(101):研究最有名的滤波函数:sinc函数
- android每个程序的内存大小,如何获取Android手机全部内存和可用内存尺寸
- PFC的使用与探索(一)