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 +0100

    ethtool: introduce ethtool netlink interface

    Basic genetlink and init infrastructure for the netlink interface, register
    genetlink family "ethtool". Add CONFIG_ETHTOOL_NETLINK Kconfig option to
    make the build optional. Add initial overall interface description into
    Documentation/networking/ethtool-netlink.rst, further patches will add more
    detailed 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_data
         ethtool-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 protocol
    Netlink message header (type: 0x0014)
        Length: 36
        Family ID: 0x14 (ethtool)
        Flags: 0x0005
        Sequence: 2
        Port ID: 0
    Command: 4
    Family Version: 1
    Reserved
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 protocol
    Netlink message header (type: 0x0014)
        Length: 416
        Family ID: 0x14 (ethtool)
        Flags: 0x0000
        Sequence: 2
        Port ID: 45683
    Command: 4
    Family Version: 1
    Reserved
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 函数的主要逻辑

  1. 根据 netlink headers 的 nlmsg_type 字段获取到注册的 genl_family 协议族结构
  2. 不支持并行调用时,获取互斥锁
  3. 调用 genl_family_rcv_msg 解析消息命令并执行
  4. 在不支持并行调用时,释放互斥锁

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 的功能

  1. 解析并创建 netlink attribute buff
  2. 调用协议族中的 pre_doit 函数
  3. 调用 genl_ops 中的 doit 函数
  4. 调用协议族中的 post_doit 函数
  5. 释放 netlink attribute buff

genl_ops 中的 doit 函数调用在 ethtool netlink genl_ops 中均指向 ethnl_default_doit,此函数标志流程进入下一个层次。

ethnl_default_doit 的功能

  1. 根据 generic netlink 头中的 cmd 字段查找 ethnl_default_requests表,得到一个 struct ethnl_request_op实例。get_wol 消息中对应 ethnl_wol_request_ops实例
  2. 解析 netlink attribute 并执行相关回调
  3. 依次调用 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 函数

  1. 调用网络设备驱动 ethtool_ops 中的 begin 方法
  2. 调用网络设备驱动 ethtool_ops 中的 get_wol 方法(对应 r8169 驱动实现的 rtl8169_get_wol 函数)
  3. 调用网络设备驱动 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 框架跨越了如下几个抽象层次:

  1. socket netlink
  2. generic netlink family
  3. ethtool generic netlink ops
  4. ethtool netlink request ops
  5. driver ethtool_ops

ethtool netlink 消息有如下几个组成部分:

  1. nlmsghdr
  2. genlmsghdr
  3. attribute

消息的不同组成部分在不同的层次中被使用,解析消息需要两层,适配 ethtool_ops 需要一层,这些层次都封装了一种类型功能的变化,有良好的扩展能力。

提问

  1. 如何将 ethtool netlink 嵌入到内核现有的 netlink 框架中?
  2. ethtool netlink 如何找到底层的网络设备进而调用 ethtool_ops 中的方法?
  3. 为啥需要 genl_ops 与 ethnl_request_ops?两者能合并吗?
  4. 与传统 ioctl 接口获取信息,ethtool netlink 框架变化的是什么?不变的又是什么?
  5. 使用 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值