目录

  • 前言
  • 环境信息与准备工作
    • 环境信息
    • 准备工作
  • 一些基础知识
    • netlink 消息的一些标志
  • 从系统调用角度观测
    • netlink 套接字的创建与 bind
    • 使用 nlctrl 类型 netlink 请求消息获取 ethtool netlink 的协议号
    • 从内核接收返回的消息,得到 ethtool netlink 的协议号
    • 获取内核返回的 ack 消息
    • 发送 ethtool 查询消息获取接口信息
    • 获取内核回复的 netlink 消息得到获取的结果
  • 获取内核返回的 ack 消息
  • 问题联想
  • 参考链接

前言

在 ethtool 的工作原理 这篇文章中我描述了 ethtool 使用 ioctl 获取信息的流程,最近却发现新版本内核与 ethtool 命令已经支持通过 netlink 来交互,又刷新了认知,本文通过使用现有的一些工具来探索此交互的细节。

环境信息与准备工作

环境信息

linux 系统版本:debian11
内核版本:5.15.0
ethtool 命令版本:5.9
测试网卡驱动:r8169

准备工作

  1. 安装 tcpdump、wireshark、ethtool 命令
  2. 加载 nlmon 驱动并配置
    配置方式可参考 tcpdump 抓取 netlink 报文 这篇博客

一些基础知识

netlink 消息的一些标志

NLM_F_REQUEST Must be set on all request messages.
NLM_F_ACK Request for an acknowledgment on success.

NLM_F_ACK 标志请求接收者回复 ack 消息。netlink 中的 ack 消息的含义是一个 NLMSG_ERROR 报文且 error 字段设置为 0。

netlink 消息头的结构:

           struct nlmsghdr {__u32 nlmsg_len;    /* Length of message including header */__u16 nlmsg_type;   /* Type of message content */__u16 nlmsg_flags;  /* Additional flags */__u32 nlmsg_seq;    /* Sequence number */__u32 nlmsg_pid;    /* Sender port ID */};

netlink 消息地址的结构:

          struct sockaddr_nl {sa_family_t     nl_family;  /* AF_NETLINK */unsigned short  nl_pad;     /* Zero */pid_t           nl_pid;     /* Port ID */__u32           nl_groups;  /* Multicast groups mask */};

一些重要的描述信息:

       nl_pid  is the unicast address of netlink socket.  It's always 0 if the destination is in the kernel.  For a user-space process, nl_pid is usually the PID of theprocess owning the destination socket.  However, nl_pid identifies a netlink socket, not a process.  If a process owns several netlink sockets, then  nl_pid  canbe  equal to the process ID only for at most one socket.  There are two ways to assign nl_pid to a netlink socket.  If the application sets nl_pid before callingbind(2), then it is up to the application to make sure that nl_pid is unique.  If the application sets it to 0, the kernel takes care of assigning it.  The  ker‐nel assigns the process ID to the first netlink socket the process opens and assigns a unique nl_pid to every netlink socket that the process subsequently creates.

nl_pid 是 netlink 套接字的单播地址,当目标地址是内核时其值为 0。对于一个用户态进程来说,nl_pid 通常是某个 netlink 套接字拥有者的进程 pid。需要注意的是 nl_pid 是标识一个 netlink 套接字而不是一个进程。如果一个进程同时拥有多个 netlink 套接字,此时最多只有一个 netlink socket 的 nl_pid 会与此进程的 pid 相同。

有两种方法可以将 nl_pid 绑定到一个 netlink 套接字上:

  1. 应用在调用 bind 前设置了 nl_pid 时(非 0 值),由应用自己保证 nl_pid 一一映射到单个 netlink socket
  2. 应用调用 bind 时没有设定 nl_pid、将 nl_pid 设置为 0,内核负责分配多个 netlink socket 的 nl_pid。内核会将第一个 netlink socket 的 nl_pid 设置为进程的 pid,其它的 netlink socket 单独生成唯一的 nl_pid。

第二种方法比较方便,可是由于 nl_pid 是由内核分配的,而 bind 系统调用并不会返回内核分配的 nl_pid,故而需要在 bind 后单独查询 nl_pid(可以调用 getsockname 获取)。

备注:本节信息摘自自 man 7 netlink

从系统调用角度观测

在一个终端中执行 sudo tcpdump -v -i nlmon0 -w netlink.pcap命令开始抓包,在另外一个终端中执行 sudo strace ethtool enp1s0 2>/tmp/strace.log触发 netlink 消息并使用 strace 跟踪系统调用。

注意事项:

最终抓到的 netlink 消息中不只包含 ethtool 使用的 netlink 消息,还包含其它的 netlink 消息(route 等)

netlink 套接字的创建与 bind

执行的系统调用:

socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) = 3
setsockopt(3, SOL_NETLINK, NETLINK_EXT_ACK, [1], 4) = 0
bind(3, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 0
getsockname(3, {sa_family=AF_NETLINK, nl_pid=45683, nl_groups=00000000}, [12]) = 0

实现的功能:

  1. 创建了一个 NETLINK_GENERIC 类型的 netlink
  2. 设置此套接字的 NETLINK_EXT_ACK 属性,支持 ACK 功能
  3. 使用 bind 绑定端口,使用上文描述的第二种设置 nl_pid 的方法(由内核将进程的 pid 赋值给第一个 netlink socket 的 nl_pid 字段)
  4. 使用 getsockname 获取地址信息,目标是获取 nl_pid,获取到的值为 45683 与进程的 pid 相同

使用 nlctrl 类型 netlink 请求消息获取 ethtool netlink 的协议号

执行的系统调用:

sendto(3, {{len=32, type=nlctrl, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1, pid=0}, "\x03\x01\x00\x00\x0c\x00\x02\x00\x65\x74\x68\x74\x6f\x6f\x6c\x00"}, 32, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 32

实现的功能:

向内核发送获取 ethtool 协议号的 netlink 消息,目的地址的 nl_pid 为 0 表示目的地址为内核。

wireshark 解析的报文内容:

Frame 49: 48 bytes on wire (384 bits), 48 bytes captured (384 bits)
Linux netlink (cooked header)Link-layer address type: Netlink (824)Family: Generic (0x0010)
Linux Generic Netlink protocolNetlink message header (type: 0x0010)Command: CTRL_CMD_GETFAMILY (3)Family Version: 1ReservedAttribute: CTRL_ATTR_FAMILY_NAME: ethtool

netlink 消息头中设定 type 为 0x10 表示 nl_ctrl 类型,Command 为 CTRL_CMD_GETFAMILY 表示获取协议号的命令,Attribute 中设置待获取的 netlink 协议号的名称为 ethtool

这一步获取到的 ethtool netlink 的协议号会在 ethtool 向内核发送 ethtool netlink 命令的时候填充到【消息头的 type 字段】中。

注意事项:

wireshark 打印的捕获报文大小为 48-bytes,这是消息的长度(32bytes)+ 消息头的长度 16bytes(sizeof(struct nlmsghdr)) 的结果。

从内核接收返回的消息,得到 ethtool netlink 的协议号

执行的系统调用:

recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{len=756, type=nlctrl, flags=0, seq=1, pid=45683}, "\x01\x02\x00\x00\x0c\x00\x02\x00\x65\x74\x68\x74\x6f\x6f\x6c\x00\x06\x00\x01\x00\x14\x00\x00\x00\x08\x00\x03\x00\x01\x00\x00\x00"...}, iov_len=65536}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 756

可以看到接收到的消息中的目的地址的 nl_pid 为 45683,表明这是一条内核发往用户态的点对点消息。

wireshark 解析的报文内容:

Frame 50: 772 bytes on wire (6176 bits), 772 bytes captured (6176 bits)
Linux netlink (cooked header)Link-layer address type: Netlink (824)Family: Generic (0x0010)
Linux Generic Netlink protocolNetlink message header (type: 0x0010)Command: CTRL_CMD_NEWFAMILY (1)Family Version: 2ReservedAttribute: CTRL_ATTR_FAMILY_NAME: ethtoolAttribute: CTRL_ATTR_FAMILY_ID: 0x14Attribute: CTRL_ATTR_VERSION: 1Attribute: CTRL_ATTR_HDRSIZE: 0Attribute: CTRL_ATTR_MAXATTR: 0Attribute: CTRL_ATTR_OPSAttribute: CTRL_ATTR_MCAST_GROUPS

返回消息中的 Attribute 字段保存获取到的数据,CTRL_ATTR_FAMILY_ID 的值为 0x14 表示 ethtool netlink 的协议号为 0x14。

获取内核返回的 ack 消息

执行的系统调用:

recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{len=36, type=NLMSG_ERROR, flags=NLM_F_CAPPED, seq=1, pid=45683}, {error=0, msg={len=32, type=nlctrl, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1, pid=0}}}, iov_len=65536}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 36

基础知识中提到——netlink 中的 ack 消息的含义是一个 NLMSG_ERROR 报文且 error 字段设置为 0。上述系统调用中,接收到的 netlink 消息符合 ack 消息的特征,同时 ack 消息中也带了原消息的 netlink 头部,用以区分 ack 的目标。

wireshark 解析的报文内容:

Frame 51: 52 bytes on wire (416 bits), 52 bytes captured (416 bits)
Linux netlink (cooked header)Link-layer address type: Netlink (824)Family: Generic (0x0010)
Netlink messageNetlink message header (type: Error)Length: 36Message type: Error (0x0002)Flags: 0x0100Sequence: 1Port ID: 45683Error code: Success (0)Netlink message header (type: 0x0010)Length: 32Message type: Protocol-specific (0x0010)Flags: 0x0005Sequence: 1Port ID: 0

发送 ethtool 查询消息获取接口信息

sendto(3, {{len=36, type=ethtool, flags=NLM_F_REQUEST|NLM_F_ACK, seq=2, pid=0}, "\x04\x01\x00\x00\x10\x00\x01\x80\x0b\x00\x02\x00\x65\x6e\x70\x31\x73\x30\x00\x00"}, 36, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 36

消息的 type 修改为了 ethool,seq 递增为 2,目标地址的 nl_pid 为 0 表明这时一个发往内核的消息。

wireshark 解析的报文内容:

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]

消息头中,type 字段为 0x14,这就是 ethtool netlink 的内核协议号,Data 中封装了请求消息的内容。

获取内核回复的 netlink 消息得到获取的结果

sendto(3, {{len=36, type=ethtool, flags=NLM_F_REQUEST|NLM_F_ACK, seq=2, pid=0}, "\x04\x01\x00\x00\x10\x00\x01\x80\x0b\x00\x02\x00\x65\x6e\x70\x31\x73\x30\x00\x00"}, 36, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 36

wireshark 解析的报文内容:

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]

wireshark 解析的 Data 部分内容:

0000   00 04 03 38 00 00 00 00 00 00 00 00 00 00 00 10   ...8............
.........................................................................
0030   0b 00 02 00 65 6e 70 31 73 30 00 00 05 00 02 00   ....enp1s0......
.........................................................................
0060   11 00 02 00 31 30 62 61 73 65 54 2f 48 61 6c 66   ....10baseT/Half
0070   00 00 00 00 04 00 03 00 24 00 01 80 08 00 01 00   ........$.......
0080   01 00 00 00 11 00 02 00 31 30 62 61 73 65 54 2f   ........10baseT/
0090   46 75 6c 6c 00 00 00 00 04 00 03 00 24 00 01 80   Full........$...

Data 中表示获取到的 ethtool dump 数据,能够明显识别出来它包含支持的链路模式(10M 半、全双工)。

获取内核返回的 ack 消息

recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{len=36, type=NLMSG_ERROR, flags=NLM_F_CAPPED, seq=2, pid=45683}, {error=0, msg={len=36, type=ethtool, flags=NLM_F_REQUEST|NLM_F_ACK, seq=2, pid=0}}}, iov_len=65536}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 36
sendto(3, {{len=36, type=ethtool, flags=NLM_F_REQUEST|NLM_F_ACK, seq=3, pid=0}, "\x02\x01\x00\x00\x10\x00\x01\x80\x0b\x00\x02\x00\x65\x6e\x70\x31\x73\x30\x00\x00"}, 36, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 36

wireshark 解析的报文内容:

Frame 54: 52 bytes on wire (416 bits), 52 bytes captured (416 bits)
Linux netlink (cooked header)Link-layer address type: Netlink (824)Family: Generic (0x0010)
Netlink messageNetlink message header (type: Error)Length: 36Message type: Error (0x0002)Flags: 0x0100Sequence: 2Port ID: 45683Error code: Success (0)Netlink message header (type: 0x0014)Length: 36Message type: Protocol-specific (0x0014)Flags: 0x0005Sequence: 2Port ID: 0

同样,第一个 Netlink message header 标识 ack 消息,第二个 Netlink message header 标识 ack 的目标。

问题联想

  1. 为啥需要获取 ethtool netlink 的协议号?
    Netlink message header 头中的 type 为数字,发送消息时需要填充,其值为内核维护,内核使用它来映射到对应的协议族 ops。
  2. ack 为啥不是相互的?用户态为啥不回复 ack?
    上面的交互过程有些奇怪,内核收到消息后先发送执行结果消息然后再发送 ack,且用户态收到消息后不会发送 ack,与 tcp ack 的过程完全不同。
    我觉得主要的问题是 tcp ack 完全隐藏在协议栈内部,netlink 却涉及内核与用户态的交互,用户态也需要回复 ack 时,每个使用的应用都要编码相关功能。同时用户态明确知道自己需要获取的数据的格式的规范,可以自己做合法性校验。

参考链接

https://www.kernel.org/doc/html/latest/networking/ethtool-netlink.html
https://lwn.net/Articles/783633/

使用 strace、tcpdump、nlmon、wireshark 探索 ethtool netlink 框架的原理相关推荐

  1. 使用 nlmon 驱动抓取 netlink 报文的原理

    前言 在 如何抓取 netlink 报文 这篇博客中,我描述了使用 nlmon 驱动创建虚拟 tap 口抓取 netlink 报文的过程,在这篇文章中,我探讨下这一过程背后的原理. nlmon 驱动 ...

  2. ethtool netlink 框架原理浅析

    2019 年 11 月我写了 ethtool 的工作原理 这篇博客,描述了 ethtool 使用 ioctl 获取信息的方式.谁曾想在 2019 年 12 月的时候内核已经开始支持 ethtool n ...

  3. 38 | 案例篇:怎么使用 tcpdump 和 Wireshark 分析网络流量?

    通常,需要暴露到公网的服务,都会绑定一个域名,既方便了人们记忆,也避免了后台服务 IP 地址的变更影响到用户. 不过要注意,DNS 解析受到各种网络状况的影响,性能可能不稳定.比如公网延迟增大,缓存过 ...

  4. tcpdump 抓二层包_可能是我见过的最简单易懂且实用的 TCPDump 和 Wireshark 抓包及分析教程!( 强烈建议收藏 )...

    公众号关注 「奇妙的 Linux 世界」设为「星标」,每天带你玩转 Linux ! 本文将展示如何使用 tcpdump 抓包,以及如何用 tcpdump 和 wireshark 分析网络流量.文中的例 ...

  5. tcpdump 和Wireshark抓包

    1 起因# 前段时间,一直在调线上的一个问题:线上应用接受POST请求,请求body中的参数获取不全,存在丢失的状况.这个问题是偶发性的,大概发生的几率为5%-10%左右,这个概率已经相当高了.在排查 ...

  6. tcpdump与Wireshark抓包分析

    1 起因# 前段时间,一直在调线上的一个问题:线上应用接受POST请求,请求body中的参数获取不全,存在丢失的状况.这个问题是偶发性的,大概发生的几率为5%-10%左右,这个概率已经相当高了.在排查 ...

  7. tcpdump与wireshark

    简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的 ...

  8. CoreCLR源码探索(八) JIT的工作原理(详解篇)

    在上一篇 我们对CoreCLR中的JIT有了一个基础的了解,这一篇我们将更详细分析JIT的实现. JIT的实现代码主要在https://github.com/dotnet/coreclr/tree/m ...

  9. 深入探索android热修复技术原理_打卡活动:技术书籍书单

    不知不觉,码个蛋打卡活动都已经60多天了,好多小伙伴都已经可以领取第一波奖励:技术书籍了,上周好多小伙伴私信码仔,问都有哪些技术书籍,码仔今天就给大家列了个书单供大家选择: 01 Android软件安 ...

最新文章

  1. 阿里云如何将服务器添加至跳板机,如何将服务器添加至jumpsever
  2. QT窗体控件自适应大小
  3. D3引擎用正则运算的方式,实现智能设备APP消息推送
  4. java语言程序设计基础篇14.6答案_《Java语言程序设计-基础篇》答案-第15章
  5. java上三角数组_二维数组的三角填充 两种java实现的方法
  6. 【janino】janino 加载自定义函数
  7. linux内核编译步骤
  8. 杜比服务器系统安装,小编教你给Win10系统安装杜比音效驱动的方法
  9. codeblocks12.11汉化方法(汉化包的使用)
  10. 两相四线混合式步进电机用双H桥驱动电路
  11. 计算机网络常见面试题总结
  12. Java开发工程师的工作内容包含哪些?
  13. [转载]Geronimo 叛逆者: 使用 Spring 框架轻松解决数据访问和配置问题
  14. mysql聚簇索引和非聚簇索引的区别_聚簇索引与非聚簇索引的区别
  15. Android 沉浸式体验
  16. 批量去除图片的黑色背景,并且统一修改图片尺寸
  17. 阅读笔记 - 20220401
  18. 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例55
  19. ue4 后期处理景深_【UE4设计师】2-3后期处理效果——使用景深设置电影拍摄
  20. xml文件报错:URL is not registered

热门文章

  1. 售前售前售前售前售前
  2. 120年奥运历史数据分析
  3. WEB安全之DIV CSS基础(二):文字和文本的属性、列表样式和伪类超链接
  4. html转pdf 图片跨域问题解决
  5. n阶方阵求逆c语言报告,n阶方阵求逆
  6. mysql的四表联查_数据库四表联查
  7. 电脑的组成与底层原理(CPU)
  8. 【图书管理系统】附源码+教程
  9. Java - char型变量中能不能存贮一个中文汉字,为什么?
  10. TCP/IP 事件选择模型