You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过<ethtool_netlink.h>获取LINKMODES?请求发送报错排查

问题排查与解决:ethtool Netlink接口获取链路模式失败

问题现状

使用ethtool Netlink接口编写代码获取链路速度、双工模式时,运行报错:Failed to receive messages: Invalid input data or parameter,无论是否指定网卡设备名,错误均存在。

核心问题排查

  1. 请求标志位错误:
    代码中使用了NLM_F_DUMP标志,但ETHTOOL_MSG_LINKMODES_GET不支持批量dump操作,必须指定单个设备,且不能使用该标志。

  2. 设备属性嵌套错误:
    ethtool Netlink的请求要求设备名必须嵌套在ETHTOOL_A_HEADER属性容器中,而非直接添加ETHTOOL_A_HEADER_DEV_NAME属性。

  3. 响应属性解析层级错误:
    链路速度属性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ò

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 17:14:54