如何通过<ethtool_netlink.h>获取LINKMODES?请求发送报错排查
问题排查与解决:ethtool Netlink接口获取链路模式失败
问题现状
使用ethtool Netlink接口编写代码获取链路速度、双工模式时,运行报错:Failed to receive messages: Invalid input data or parameter,无论是否指定网卡设备名,错误均存在。
核心问题排查
请求标志位错误:
代码中使用了NLM_F_DUMP标志,但ETHTOOL_MSG_LINKMODES_GET不支持批量dump操作,必须指定单个设备,且不能使用该标志。设备属性嵌套错误:
ethtool Netlink的请求要求设备名必须嵌套在ETHTOOL_A_HEADER属性容器中,而非直接添加ETHTOOL_A_HEADER_DEV_NAME属性。响应属性解析层级错误:
链路速度属性ETHTOOL_A_LINKMODES_SPEED并非直接顶级属性,而是嵌套在ETHTOOL_A_LINKMODES_CURRENT子容器内。
修正后的代码
#include <errno.h> #include <linux/ethtool_netlink.h> #include <netlink/genl/ctrl.h> #include <netlink/genl/genl.h> #include <netlink/netlink.h> #include <stdio.h> static int parse_cb(struct nl_msg* msg, void* arg __attribute__((unused))) { int err = 0; struct genlmsghdr* genlhdr = nlmsg_data(nlmsg_hdr(msg)); struct nlattr* tb[ETHTOOL_A_LINKMODES_MAX + 1]; struct nlattr* current_tb[ETHTOOL_A_LINKMODES_CURRENT_MAX + 1]; // 解析顶层属性 err = nla_parse(tb, ETHTOOL_A_LINKMODES_MAX, genlmsg_attrdata(genlhdr, 0), genlmsg_attrlen(genlhdr, 0), NULL); if (0 != err) { nl_perror(err, "Failed parsing top-level attributes"); return NL_SKIP; } // 检查并解析当前链路模式子容器 if (!tb[ETHTOOL_A_LINKMODES_CURRENT]) { fprintf(stderr, "Current link modes attribute not found\n"); return NL_SKIP; } err = nla_parse_nested(current_tb, ETHTOOL_A_LINKMODES_CURRENT_MAX, tb[ETHTOOL_A_LINKMODES_CURRENT], NULL); if (0 != err) { nl_perror(err, "Failed parsing current link modes"); return NL_SKIP; } // 提取速度属性 if (!current_tb[ETHTOOL_A_LINKMODES_CURRENT_SPEED]) { fprintf(stderr, "Speed attribute not found\n"); return NL_SKIP; } printf("Link speed: %d Mbps\n", nla_get_u32(current_tb[ETHTOOL_A_LINKMODES_CURRENT_SPEED])); // 可选:提取双工模式 if (current_tb[ETHTOOL_A_LINKMODES_CURRENT_DUPLEX]) { uint8_t duplex = nla_get_u8(current_tb[ETHTOOL_A_LINKMODES_CURRENT_DUPLEX]); printf("Duplex mode: %s\n", duplex == ETHTOOL_LINK_MODE_DUPLEX_FULL ? "Full" : "Half"); } return NL_OK; } static int send_request(struct nl_sock* sk, int fam, const char* dev_name) { void* hdr = NULL; int err = 0; struct nl_msg* msg = NULL; struct nlattr* header = NULL; msg = nlmsg_alloc(); if (NULL == msg) { fprintf(stderr, "Failed to create message\n"); return -ENOMEM; } // 移除NLM_F_DUMP,使用默认的NLM_F_REQUEST hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, fam, 0, 0, ETHTOOL_MSG_LINKMODES_GET, ETHTOOL_GENL_VERSION); if (NULL == hdr) { fprintf(stderr, "Failed to put message\n"); nlmsg_free(msg); return -EMSGSIZE; } // 创建ETHTOOL_A_HEADER容器并添加设备名 header = nla_nest_start(msg, ETHTOOL_A_HEADER); if (!header) { fprintf(stderr, "Failed to start header nest\n"); nlmsg_free(msg); return -EMSGSIZE; } err = nla_put_string(msg, ETHTOOL_A_HEADER_DEV_NAME, dev_name); if (err < 0) { fprintf(stderr, "Failed to put device name\n"); nla_nest_cancel(msg, header); nlmsg_free(msg); return -err; } nla_nest_end(msg, header); err = nl_send_auto(sk, msg); err = err >= 0 ? 0 : err; nlmsg_free(msg); return err; } int main(int argc, char* argv[]) { struct nl_sock* sock = NULL; int err = 0; int fam = 0; const char* dev_name = "enp0s0"; // 默认设备,可通过命令行参数传入 if (argc >= 2) { dev_name = argv[1]; } sock = nl_socket_alloc(); if (NULL == sock) { fprintf(stderr, "Failed to allocate unicast nl_sock\n"); return -1; } err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL); if (0 != err) { nl_perror(err, "Failed to add custom cb"); } err = genl_connect(sock); if (0 != err) { nl_perror(err, "Failed to connect socket"); goto free_socket; } fam = genl_ctrl_resolve(sock, ETHTOOL_GENL_NAME); if (fam < 0) { nl_perror(fam, "failed to resolve generic netlink family"); goto free_socket; } err = send_request(sock, fam, dev_name); if (0 != err) { nl_perror(err, "failed to send message"); goto free_socket; } err = nl_recvmsgs_default(sock); if (err < 0 && err != -NLE_INTR && err != -NLE_AGAIN) { nl_perror(err, "Failed to receive messages"); } free_socket: nl_close(sock); nl_socket_free(sock); return err; }
编译与运行说明
编译时需链接libnl-genl库:
gcc -o ethtool_link ethtool_link.c -lnl-genl-3 -lnl-3
运行时指定目标网卡(默认enp0s0):
./ethtool_link eth0
内容的提问来源于stack exchange,提问作者Nicolò
相关产品推荐
相关产品推荐

