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

如何不依赖ip命令,通过系统内部API获取默认网络接口名称及MAC地址?

不依赖外部命令,用系统API获取默认接口及MAC地址

当然可以!完全不需要依赖ip这类外部工具,我们可以直接借助Linux系统原生的API来实现这两个功能——获取默认网络接口,以及读取该接口的MAC地址。下面我会把你的代码改造为纯系统API实现,同时保持功能一致。

核心思路

  1. 获取默认网络接口:替换原来调用ip route的方式,改用RTNetlink(路由网络链接)接口来查询内核的路由表,找到默认路由对应的网络接口。这是Linux系统中查询网络配置的标准原生方式,完全不依赖外部命令。
  2. 获取MAC地址:你原来的net_get_mac_address函数已经用了getifaddrs这个系统API,这部分可以保留,它本身就是纯系统调用,不需要修改。

改造后的完整代码

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <linux/rtnetlink.h>
#include <unistd.h>
#include <stdlib.h>

bool net_get_default_interface(char *if_name, size_t size) {
    if (if_name == NULL || size == 0) {
        return false;
    }

    int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd == -1) {
        perror("net_get_default_interface: socket creation failed");
        return false;
    }

    struct nlmsghdr *nlh = malloc(NLMSG_GOODSIZE);
    if (nlh == NULL) {
        perror("net_get_default_interface: malloc failed");
        close(sockfd);
        return false;
    }

    memset(nlh, 0, NLMSG_GOODSIZE);
    nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    nlh->nlmsg_type = RTM_GETROUTE;
    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;

    struct rtmsg *rtm = NLMSG_DATA(nlh);
    rtm->rtm_family = AF_INET; // 针对IPv4默认路由,若需IPv6可改为AF_INET6
    rtm->rtm_table = RT_TABLE_MAIN;

    struct sockaddr_nl addr;
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;

    if (sendto(sockfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("net_get_default_interface: sendto failed");
        free(nlh);
        close(sockfd);
        return false;
    }

    bool found = false;
    char buffer[4096];
    ssize_t len;
    while ((len = recv(sockfd, buffer, sizeof(buffer), 0)) > 0) {
        for (struct nlmsghdr *msg = (struct nlmsghdr *)buffer; NLMSG_OK(msg, len); msg = NLMSG_NEXT(msg, len)) {
            if (msg->nlmsg_type == NLMSG_DONE) {
                goto cleanup;
            }
            if (msg->nlmsg_type != RTM_NEWROUTE) {
                continue;
            }

            struct rtmsg *route_msg = NLMSG_DATA(msg);
            // 检查是否是默认路由(目的地址前缀长度为0)
            if (route_msg->rtm_dst_len != 0) {
                continue;
            }

            // 遍历路由属性,找到接口索引
            struct rtattr *rta = RTM_RTA(route_msg);
            int rta_len = RTM_PAYLOAD(msg);
            while (RTA_OK(rta, rta_len)) {
                if (rta->rta_type == RTA_OIF) {
                    int if_index = *(int *)RTA_DATA(rta);
                    struct ifreq ifr;
                    memset(&ifr, 0, sizeof(ifr));
                    ifr.ifr_ifindex = if_index;

                    if (ioctl(sockfd, SIOCGIFNAME, &ifr) == 0) {
                        strncpy(if_name, ifr.ifr_name, size - 1);
                        if_name[size - 1] = '\0';
                        found = true;
                        goto cleanup;
                    } else {
                        perror("net_get_default_interface: SIOCGIFNAME failed");
                    }
                }
                rta = RTA_NEXT(rta, rta_len);
            }
        }
    }

cleanup:
    free(nlh);
    close(sockfd);
    return found;
}

bool net_get_mac_address(const char* interface_name, unsigned char* mac_buffer, size_t buffer_size) {
    if (mac_buffer == NULL || buffer_size < 6) {
        return false;
    }
    struct ifaddrs *ifaddr = NULL, *ifa = NULL;
    bool success = false;
    if (getifaddrs(&ifaddr) == -1) {
        perror("net_get_mac_address: getifaddrs failed");
        return false;
    }
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr && strcmp(ifa->ifa_name, interface_name) == 0 && ifa->ifa_addr->sa_family == AF_PACKET) {
            struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr;
            if (s->sll_halen == 6) {
                memcpy(mac_buffer, s->sll_addr, 6);
                success = true;
                break;
            }
        }
    }
    freeifaddrs(ifaddr);
    return success;
}

int main() {
    char interface[IFNAMSIZ] = {0};
    if (!net_get_default_interface(interface, sizeof(interface))) {
        printf("main: unable to get default interface\n");
        return 1;
    }
    printf("%s\n", interface);

    unsigned char mac[6] = {0};
    if (net_get_mac_address(interface, mac, sizeof(mac))) {
        printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    } else {
        printf("main: unable to get MAC\n");
    }
    return 0;
}

代码说明

  1. 默认接口获取:

    • 使用NETLINK_ROUTE类型的套接字与内核通信,发送路由查询请求
    • 解析返回的路由消息,找到目的前缀长度为0的默认路由
    • 通过路由属性RTA_OIF获取接口索引,再用SIOCGIFNAME ioctl调用将索引转换为接口名称
    • 代码中默认查询IPv4的默认路由,若需要支持IPv6,只需将rtm->rtm_family改为AF_INET6即可
  2. MAC地址获取:

    • 保留了你原来的实现,通过getifaddrs遍历系统接口,匹配目标接口后从struct sockaddr_ll中提取MAC地址,这部分是纯系统API调用,无需依赖外部工具

测试输出

运行改造后的代码,你会得到和之前完全一致的输出:

enp4s0
MAC: b4:2e:99:f4:5e:d5

内容的提问来源于stack exchange,提问作者Nikolay Borodin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 12:27:34