文章目录
- 一、前言
- 二、通过`ethtool`源码编译可执行文件
- 三、`ethtool`入口函数
- 四、解析命令行参数`parse_cmdline`
-
- 1.函数框架
- 2.第一个参数处理 (case 1)
-
- 2.1.遍历选项表
- 2.2.选项匹配检查
- [2.3. 特殊条件处理](#2.3. 特殊条件处理)
- 2.4.默认情况
- 五、开始操作`doit`
-
- 1.函数框架
- 2.网络接口初始化
-
- [2.1.初始化 `ifreq` 结构](#2.1.初始化
ifreq
结构) - 2.2.创建控制socket
- 2.3.查询网卡信息
- [2.1.初始化 `ifreq` 结构](#2.1.初始化
- 六、获取网络设备的各项设置`do_gset`
-
- 1.函数概述
- 2.函数执行流程
-
- [2.1. 初始化阶段](#2.1. 初始化阶段)
- [2.2. 获取设备基本设置 (ETHTOOL_GSET)](#2.2. 获取设备基本设置 (ETHTOOL_GSET))
- [2.3. 获取Wake-on-LAN设置 (ETHTOOL_GWOL)](#2.3. 获取Wake-on-LAN设置 (ETHTOOL_GWOL))
- [2.4. 获取消息级别 (ETHTOOL_GMSGLVL)](#2.4. 获取消息级别 (ETHTOOL_GMSGLVL))
- [2.5. 获取链路状态 (ETHTOOL_GLINK)](#2.5. 获取链路状态 (ETHTOOL_GLINK))
- 七、`sys_ioctl`系统调用
-
- 1.代码执行流程
-
- [1.1. 初始错误设置](#1.1. 初始错误设置)
- [1.2. 文件类型检查和处理](#1.2. 文件类型检查和处理)
- [2.3. 设备文件检查和处理](#2.3. 设备文件检查和处理)
- 八、`sock_ioctl`处理套接字相关的`ioctl`请求
- 九、`inet_ioctl`处理IPv4套接字的`ioctl`请求
- [十、`dev_ioctl` 处理网络设备相关的`ioctl`命令](#十、
dev_ioctl
处理网络设备相关的ioctl
命令) -
- 1.函数概述
- 2.SIOCETHTOOL分支执行流程
-
- [2.1. 用户空间数据拷贝](#2.1. 用户空间数据拷贝)
- [2.2. 驱动模块加载](#2.2. 驱动模块加载)
- [2.3. 网络设备锁保护](#2.3. 网络设备锁保护)
- [2.4. 核心`ethtool`处理](#2.4. 核心
ethtool
处理) - [2.5. 结果返回用户空间](#2.5. 结果返回用户空间)
- 十一、`dev_ethtool`处理所有`ethtool`子命令的分发和执行
-
- 1.函数概述
- 2.完整函数逻辑
-
- [2.1. 网络设备查找](#2.1. 网络设备查找)
- 2.2.用户空间数据指针
- [2.3. ETHTOOL_GSET命令详解](#2.3. ETHTOOL_GSET命令详解)
- 3.重要安全机制
-
- [3.1. 权限验证](#3.1. 权限验证)
- [3.2. 用户空间指针安全](#3.2. 用户空间指针安全)
- [3.3. 驱动支持检查](#3.3. 驱动支持检查)
- 十二、`ethtool_get_settings`负责处理`ETHTOOL_GSET`命令
- 十三、`ethtool`获取`eth0`设备信息内核空间完整调用链
-
一、前言
假设输入命令sudo ethtool eth0
,那么ethtool
是怎么工作的?在分析之前先编译一个可执行文件,方便我们调试
二、通过ethtool
源码编译可执行文件
1.准备工作
-
下载源码:
-
安装依赖:
ethtool
的编译依赖于一些开发工具和库,如automake
、autoconf
。在基于Ubuntu的系统上,可以使用以下命令安装这些依赖:
bash
sudo apt-get install automake autoconf
注意查看文件autogen.sh
里的描述,看下载的版本是否支持
2.编译步骤
- 生成配置文件 :
- 进入源码目录,执行以下命令生成配置文件:
bash
./autogen.sh
-
如果遇到
automake
工具未安装的错误,请确保已安装automake
-
配置编译选项:
- 执行
configure
脚本,配置编译信息并生成Makefile
文件。对于本地编译,可以直接使用以下命令:
- 执行
bash
./configure
- 编译源码 :
- 执行
make
命令,开始编译,需要指定linux
的头文件位置
- 执行
bash
make CFLAGS="-g -O0 -I/usr/src/linux-2.6.10/include"
- 编译完成后,可以在当前目录下看到生成的
ethtool
可执行文件
3.验证编译结果
运行测试:
- 可以直接运行
ethtool
命令来测试其功能。例如:
bash
sudo ./ethtool -h
- 这将显示
ethtool
的帮助信息,验证其是否正常运行
三、ethtool
入口函数
c
int main(int argc, char **argp, char **envp)
{
parse_cmdline(argc, argp);
return doit();
}
四、解析命令行参数parse_cmdline
c
static void parse_cmdline(int argc, char **argp)
{
int i, k;
for (i = 1; i < argc; i++) {
switch (i) {
case 1:
for (k = 0; args[k].srt; k++)
if (!strcmp(argp[i], args[k].srt) ||
!strcmp(argp[i], args[k].lng)) {
mode = args[k].Mode;
break;
}
if (mode == MODE_HELP ||
(!args[k].srt && argp[i][0] == '-'))
show_usage(0);
else
devname = argp[i];
break;
...
}
1.函数框架
c
static void parse_cmdline(int argc, char **argp)
{
int i, k;
for (i = 1; i < argc; i++) {
switch (i) {
case 1: // 第一个参数处理
// ...
case 2: // 第二个参数处理
// ...
case 3: // 第三个参数处理
// ...
default: // 其他参数处理
// ...
}
}
// 后处理逻辑
}
2.第一个参数处理 (case 1)
c
case 1:
for (k = 0; args[k].srt; k++)
if (!strcmp(argp[i], args[k].srt) ||
!strcmp(argp[i], args[k].lng)) {
mode = args[k].Mode;
break;
}
if (mode == MODE_HELP ||
(!args[k].srt && argp[i][0] == '-'))
show_usage(0);
else
devname = argp[i];
break;
2.1.遍历选项表
c
for (k = 0; args[k].srt; k++)
-
遍历
args[]
数组,直到遇到srt
为 NULL 的结束标记 -
args[]
包含了所有支持的命令行选项,每一个参数是如下结构体
c
static struct option {
char *srt, *lng;
int Mode;
char *help;
char *opthelp;
} args
2.2.选项匹配检查
c
if (!strcmp(argp[i], args[k].srt) ||
!strcmp(argp[i], args[k].lng)) {
mode = args[k].Mode;
break;
}
!strcmp(argp[i], args[k].srt)
: 匹配短选项(如-s
)!strcmp(argp[i], args[k].lng)
: 匹配长选项(如--change
)mode = args[k].Mode
: 设置对应的操作模式break
: 找到匹配后立即退出循环
2.3. 特殊条件处理
c
if (mode == MODE_HELP ||
(!args[k].srt && argp[i][0] == '-'))
show_usage(0);
两种情况下显示帮助信息:
- 模式是帮助 :
mode == MODE_HELP
- 未知选项 :
!args[k].srt
(遍历完都没找到匹配)且argp[i][0] == '-'
(以破折号开头)
2.4.默认情况
c
else
devname = argp[i];
- 如果不是选项,就认为是设备名 (如
eth0
)
五、开始操作doit
c
static int doit(void)
{
struct ifreq ifr;
int fd;
/* Setup our control structures. */
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, devname);
/* Open control socket. */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("Cannot get control socket");
return 70;
}
/* all of these are expected to populate ifr->ifr_data as needed */
if (mode == MODE_GDRV) {
return do_gdrv(fd, &ifr);
} else if (mode == MODE_GSET) {
return do_gset(fd, &ifr);
} ...
}
1.函数框架
c
static int doit(void)
{
struct ifreq ifr;
int fd;
// 初始化网络接口请求结构
// 创建控制socket
// 根据模式分发到具体处理函数
}
2.网络接口初始化
2.1.初始化 ifreq
结构
c
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, devname);
memset
: 清空结构体,避免未初始化内存strcpy
: 设置网络设备名(如eth0
)ifreq
结构体: 用于内核与用户空间传递网络接口信息
2.2.创建控制socket
c
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("Cannot get control socket");
return 70;
}
AF_INET
: IPv4 协议族SOCK_DGRAM
: 数据报socket(UDP)- 返回值70: Unix惯例,表示系统配置错误
- 用途 : 通过这个socket发送
ioctl
命令到内核
2.3.查询网卡信息
bash
sudo ./ethtool eth0
c
// 流程:
mode = MODE_GSET (默认模式)
→ 调用 do_gset(fd, &ifr)
→ 发送 SIOCETHTOOL ioctl 获取设置
→ 显示速度、双工、自动协商等信息
六、获取网络设备的各项设置do_gset
c
static int do_gset(int fd, struct ifreq *ifr)
{
int err;
struct ethtool_cmd ecmd;
struct ethtool_wolinfo wolinfo;
struct ethtool_value edata;
int allfail = 1;
fprintf(stdout, "Settings for %s:\n", devname);
ecmd.cmd = ETHTOOL_GSET;
ifr->ifr_data = (caddr_t)&ecmd;
err = send_ioctl(fd, ifr);
if (err == 0) {
err = dump_ecmd(&ecmd);
if (err)
return err;
allfail = 0;
} else if (errno != EOPNOTSUPP) {
perror("Cannot get device settings");
}
wolinfo.cmd = ETHTOOL_GWOL;
ifr->ifr_data = (caddr_t)&wolinfo;
err = send_ioctl(fd, ifr);
if (err == 0) {
err = dump_wol(&wolinfo);
if (err)
return err;
allfail = 0;
} else if (errno != EOPNOTSUPP) {
perror("Cannot get wake-on-lan settings");
}
edata.cmd = ETHTOOL_GMSGLVL;
ifr->ifr_data = (caddr_t)&edata;
err = send_ioctl(fd, ifr);
if (err == 0) {
fprintf(stdout, " Current message level: 0x%08x (%d)\n"
" ",
edata.data, edata.data);
print_flags(cmdline_msglvl, ARRAY_SIZE(cmdline_msglvl),
edata.data);
fprintf(stdout, "\n");
allfail = 0;
} else if (errno != EOPNOTSUPP) {
perror("Cannot get message level");
}
edata.cmd = ETHTOOL_GLINK;
ifr->ifr_data = (caddr_t)&edata;
err = send_ioctl(fd, ifr);
if (err == 0) {
fprintf(stdout, " Link detected: %s\n",
edata.data ? "yes":"no");
allfail = 0;
} else if (errno != EOPNOTSUPP) {
perror("Cannot get link status");
}
if (allfail) {
fprintf(stdout, "No data available\n");
return 75;
}
return 0;
}
1.函数概述
c
static int do_gset(int fd, struct ifreq *ifr)
- 目的:获取网络设备的多种配置信息
- 参数 :
fd
:网络设备文件描述符ifr
:指向接口请求结构的指针
- 返回值:成功返回0,失败返回错误码
2.函数执行流程
2.1. 初始化阶段
c
int err;
struct ethtool_cmd ecmd;
struct ethtool_wolinfo wolinfo;
struct ethtool_value edata;
int allfail = 1; // 初始假设所有操作都会失败
fprintf(stdout, "Settings for %s:\n", devname);
2.2. 获取设备基本设置 (ETHTOOL_GSET)
c
ecmd.cmd = ETHTOOL_GSET;
ifr->ifr_data = (caddr_t)&ecmd;
err = send_ioctl(fd, ifr);
- 功能:获取网卡速度、双工模式、自适应等基本设置
- 处理逻辑 :
- 成功:调用
dump_ecmd()
输出详细信息,标记allfail = 0
- 失败但非不支持:报错"Cannot get device settings"
- 不支持操作:静默跳过
- 成功:调用
2.3. 获取Wake-on-LAN设置 (ETHTOOL_GWOL)
c
wolinfo.cmd = ETHTOOL_GWOL;
ifr->ifr_data = (caddr_t)&wolinfo;
err = send_ioctl(fd, ifr);
- 功能:获取网络唤醒配置
- 处理逻辑 :同上,成功时调用
dump_wol()
输出WOL设置
2.4. 获取消息级别 (ETHTOOL_GMSGLVL)
c
edata.cmd = ETHTOOL_GMSGLVL;
ifr->ifr_data = (caddr_t)&edata;
err = send_ioctl(fd, ifr);
-
功能:获取驱动调试消息级别
-
成功输出示例:
Current message level: 0x00000007 (7) drv probe link
2.5. 获取链路状态 (ETHTOOL_GLINK)
c
edata.cmd = ETHTOOL_GLINK;
ifr->ifr_data = (caddr_t)&edata;
err = send_ioctl(fd, ifr);
- 功能:检测网络链路状态(是否连接)
- 输出:"Link detected: yes" 或 "Link detected: no"
七、sys_ioctl
系统调用
c
asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
unsigned int flag;
int on, error = -EBADF;
...
switch (cmd) {
...
default:
error = -ENOTTY;
if (S_ISREG(filp->f_dentry->d_inode->i_mode))
error = file_ioctl(filp, cmd, arg);
else if (filp->f_op && filp->f_op->ioctl)
error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
}
...
}
1.代码执行流程
1.1. 初始错误设置
c
error = -ENOTTY;
-ENOTTY
: "对于设备不适当的ioctl
"错误- 这是默认的错误返回值,表示该命令不被支持
1.2. 文件类型检查和处理
c
if (S_ISREG(filp->f_dentry->d_inode->i_mode))
error = file_ioctl(filp, cmd, arg);
条件 : S_ISREG(...)
检查文件是否是普通文件(regular file)
- 处理 : 调用
file_ioctl(filp, cmd, arg)
- 含义 : 对于普通文件(如文本文件、二进制文件等),使用通用的文件
ioctl
处理函数
2.3. 设备文件检查和处理
c
else if (filp->f_op && filp->f_op->ioctl)
error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
条件:
filp->f_op
存在(文件操作表不为空)filp->f_op->ioctl
存在(设备提供了ioctl
处理方法)
处理 : 调用设备特定的ioctl
处理函数
- 参数 :
inode, file, cmd, arg
- 含义 : 将
ioctl
请求转发给具体的设备驱动程序处理
八、sock_ioctl
处理套接字相关的ioctl
请求
c
static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct socket *sock;
void __user *argp = (void __user *)arg;
int pid, err;
...
switch (cmd) {
...
default:
err = sock->ops->ioctl(sock, cmd, arg);
break;
}
...
}
1.函数概述
c
static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
- 作用 : 处理套接字文件的
ioctl
系统调用 - 参数 :
inode
: 文件的inode
结构file
: 文件结构指针cmd
:ioctl
命令arg
: 命令参数(用户空间指针)
2.默认分支处理
c
default:
err = sock->ops->ioctl(sock, cmd, arg);
break;
执行逻辑:
- 获取套接字操作表 :
sock->ops
指向特定协议族的操作函数表 - 调用协议特定的
ioctl
:sock->ops->ioctl(sock, cmd, arg)
- 返回执行结果 : 错误码通过
err
变量返回
九、inet_ioctl
处理IPv4套接字的ioctl
请求
c
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err = 0;
switch (cmd) {
...
default:
if (!sk->sk_prot->ioctl ||
(err = sk->sk_prot->ioctl(sk, cmd, arg)) ==
-ENOIOCTLCMD)
err = dev_ioctl(cmd, (void __user *)arg);
break;
}
return err;
}
1.函数概述
c
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
- 作用 : 处理IPv4协议族的套接字
ioctl
命令 - 参数 :
sock
: 套接字结构cmd
:ioctl
命令arg
: 命令参数
2.默认分支执行逻辑
c
default:
if (!sk->sk_prot->ioctl ||
(err = sk->sk_prot->ioctl(sk, cmd, arg)) == -ENOIOCTLCMD)
err = dev_ioctl(cmd, (void __user *)arg);
break;
2.1.执行流程图
默认分支开始
↓
检查sk->sk_prot->ioctl是否存在?
↓
不存在 或 存在但返回-ENOIOCTLCMD → 调用dev_ioctl()
↓
存在且处理成功 → 返回协议处理结果
↓
返回错误码
3.详细逻辑分解
条件1: 检查协议是否支持ioctl
c
if (!sk->sk_prot->ioctl || ... )
- 含义 : 如果传输层协议没有实现
ioctl
操作 - 示例 : 某些简单的协议可能没有特定的
ioctl
处理
条件2: 协议处理返回特定错误
c
(err = sk->sk_prot->ioctl(sk, cmd, arg)) == -ENOIOCTLCMD
-ENOIOCTLCMD
: "没有这样的ioctl
命令"错误码- 含义: 协议层表示不认识这个命令,需要继续向下传递
最终处理:
c
err = dev_ioctl(cmd, (void __user *)arg);
dev_ioctl
: 网络设备相关的ioctl
处理函数- 含义: 将命令传递给网络设备层处理
十、dev_ioctl
处理网络设备相关的ioctl
命令
c
// net/core/dev.c
int dev_ioctl(unsigned int cmd, void __user *arg)
{
struct ifreq ifr;
int ret;
char *colon;
...
switch (cmd) {
...
case SIOCETHTOOL:
dev_load(ifr.ifr_name);
rtnl_lock();
ret = dev_ethtool(&ifr);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
...
}
}
1.函数概述
c
int dev_ioctl(unsigned int cmd, void __user *arg)
- 作用 : 处理网络设备相关的
ioctl
命令 - 参数 :
cmd
:ioctl
命令arg
: 用户空间参数指针
2.SIOCETHTOOL分支执行流程
2.1. 用户空间数据拷贝
c
/* 在函数开头部分 */
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
- 作用 : 从用户空间安全地拷贝
ifreq
结构到内核空间
2.2. 驱动模块加载
c
dev_load(ifr.ifr_name);
- 作用: 确保网络设备驱动模块已加载
- 逻辑: 如果设备不存在,尝试自动加载对应的内核模块
- 示例: 对于"eth0"接口,可能加载对应的网络驱动模块
2.3. 网络设备锁保护
c
rtnl_lock();
// ... 关键操作
rtnl_unlock();
- RTNL: Run-Time Network Lock(运行时网络锁)
- 作用: 保护网络设备配置的并发访问,防止竞态条件
- 重要性: 网络设备操作必须是原子的
2.4. 核心ethtool
处理
c
ret = dev_ethtool(&ifr);
dev_ethtool
: 实际处理所有ethtool
命令的函数
2.5. 结果返回用户空间
c
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
ret = -EFAULT;
}
- 条件 : 只有操作成功(
ret == 0
)时才拷贝数据回用户空间 copy_to_user
: 安全地将内核数据拷贝到用户空间colon
处理: 恢复接口名中的冒号(如果有的话)
十一、dev_ethtool
处理所有ethtool
子命令的分发和执行
c
int dev_ethtool(struct ifreq *ifr)
{
struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
void __user *useraddr = ifr->ifr_data;
u32 ethcmd;
int rc;
...
switch (ethcmd) {
case ETHTOOL_GSET:
rc = ethtool_get_settings(dev, useraddr);
break;
...
1.函数概述
c
int dev_ethtool(struct ifreq *ifr)
- 作用 : 处理所有
ethtool
子命令的分发和执行
2.完整函数逻辑
2.1. 网络设备查找
c
struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
- 作用: 根据接口名查找网络设备结构
2.2.用户空间数据指针
c
void __user *useraddr = ifr->ifr_data;
-
作用 : 指向
ethtool
命令数据的用户空间指针 -
数据流:
用户空间: struct ifreq.ifr_data → 指向ethtool_cmd等结构 ↓ 内核空间: useraddr → 通过copy_from_user/copy_to_user访问
2.3. ETHTOOL_GSET命令详解
c
case ETHTOOL_GSET:
rc = ethtool_get_settings(dev, useraddr);
break;
3.重要安全机制
3.1. 权限验证
c
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- 需要
CAP_NET_ADMIN
能力(通常需要root权限)
3.2. 用户空间指针安全
c
if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
3.3. 驱动支持检查
c
if (!dev->ethtool_ops || !dev->ethtool_ops->get_settings)
return -EOPNOTSUPP;
十二、ethtool_get_settings
负责处理ETHTOOL_GSET
命令
c
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
{
struct ethtool_cmd cmd = { ETHTOOL_GSET };
int err;
if (!dev->ethtool_ops->get_settings)
return -EOPNOTSUPP;
err = dev->ethtool_ops->get_settings(dev, &cmd);
if (err < 0)
return err;
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
return -EFAULT;
return 0;
}
1.函数概述
c
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
- 作用: 获取网络设备的物理层设置(速度、双工模式、自适应等)
- 调用路径 :
dev_ethtool()
→ethtool_get_settings()
- 返回值: 成功返回0,失败返回错误码
2.函数执行流程
2.1.完整执行流程图
ethtool_get_settings开始
↓
初始化ethtool_cmd结构,设置cmd=ETHTOOL_GSET
↓
检查驱动是否支持get_settings操作
↓
调用驱动特定的get_settings方法
↓
将结果拷贝回用户空间
↓
返回执行状态
3.代码详细解析
3.1. 初始化ethtool
命令结构
c
struct ethtool_cmd cmd = { ETHTOOL_GSET };
- 作用 : 创建并初始化
ethtool_cmd
结构体 ETHTOOL_GSET
: 命令常量
3.2. 驱动支持检查
c
if (!dev->ethtool_ops->get_settings)
return -EOPNOTSUPP;
- 作用 : 验证网络设备驱动是否实现了
get_settings
方法 -EOPNOTSUPP
: "操作不支持"错误码
3.3. 调用驱动特定的get_settings方法
c
err = dev->ethtool_ops->get_settings(dev, &cmd);
if (err < 0)
return err;
- 作用 : 执行具体的硬件访问操作,填充
ethtool_cmd
结构 - 参数 :
dev
: 网络设备指针&cmd
: 要填充的ethtool
命令结构指针
- 返回值处理: 如果驱动返回错误,直接传播给调用者
3.4. 结果返回用户空间
c
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
return -EFAULT;
return 0;
- 作用: 将填充好的设备设置信息返回给用户空间
copy_to_user
: 安全地将内核数据拷贝到用户空间-EFAULT
: "错误的地址"错误码(值为-14),表示用户空间指针无效
十三、ethtool
获取eth0
设备信息内核空间完整调用链
ioctl
→ sys_ioctl
→sock_ioctl
→ inet_ioctl
→ dev_ioctl
→ dev_ethtool
→ethtool_get_settings
text
用户空间: ioctl(SIOCETHTOOL, &ifr)
↓
系统调用: sys_ioctl()
↓
套接字层: sock_ioctl()
↓
网络层: inet_ioctl()
↓
设备层: dev_ioctl(SIOCETHTOOL)
↓
ethtool层: dev_ethtool(&ifr)
↓
命令分发: switch(ethcmd) → case ETHTOOL_GSET
↓
具体处理: ethtool_get_settings(dev, useraddr)
↓
驱动调用: dev->ethtool_ops->get_settings(dev, &ecmd)
↓
硬件访问: 读取PHY寄存器等硬件操作