如何不依赖ip命令,通过系统内部API获取默认网络接口名称及MAC地址?
不依赖外部命令,用系统API获取默认接口及MAC地址
当然可以!完全不需要依赖ip这类外部工具,我们可以直接借助Linux系统原生的API来实现这两个功能——获取默认网络接口,以及读取该接口的MAC地址。下面我会把你的代码改造为纯系统API实现,同时保持功能一致。
核心思路
- 获取默认网络接口:替换原来调用
ip route的方式,改用RTNetlink(路由网络链接)接口来查询内核的路由表,找到默认路由对应的网络接口。这是Linux系统中查询网络配置的标准原生方式,完全不依赖外部命令。 - 获取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; }
代码说明
默认接口获取:
- 使用
NETLINK_ROUTE类型的套接字与内核通信,发送路由查询请求 - 解析返回的路由消息,找到目的前缀长度为0的默认路由
- 通过路由属性
RTA_OIF获取接口索引,再用SIOCGIFNAMEioctl调用将索引转换为接口名称 - 代码中默认查询IPv4的默认路由,若需要支持IPv6,只需将
rtm->rtm_family改为AF_INET6即可
- 使用
MAC地址获取:
- 保留了你原来的实现,通过
getifaddrs遍历系统接口,匹配目标接口后从struct sockaddr_ll中提取MAC地址,这部分是纯系统API调用,无需依赖外部工具
- 保留了你原来的实现,通过
测试输出
运行改造后的代码,你会得到和之前完全一致的输出:
enp4s0 MAC: b4:2e:99:f4:5e:d5
内容的提问来源于stack exchange,提问作者Nikolay Borodin
相关产品推荐
相关产品推荐

