Linux 下檢測網卡鏈路狀態(UP/DOWN、插拔、連接),傳統 ioctl(SIOCGIFFLAGS) 需手動構造 ifreq、遍歷接口。現代 getifaddrs() 直接返回鏈表,代碼量減半,可移植性更強。本文給出生產級實現。
#includeint getifaddrs(struct ifaddrs **ifap); void freeifaddrs(struct ifaddrs *ifap); struct ifaddrs { struct ifaddrs *ifa_next; char *ifa_name; // 接口名(eth0, lo...) unsigned int ifa_flags; // IFF_UP, IFF_RUNNING, IFF_LOOPBACK... struct sockaddr *ifa_addr; // 地址 struct sockaddr *ifa_netmask; // 掩碼 struct sockaddr *ifa_broadaddr; // 廣播 struct sockaddr *ifa_dstaddr; // 目標地址 void *ifa_data; // 驅動私有數據 };
IFF_UP:管理狀態 UP(ip link set up)。IFF_RUNNING:物理鏈路載波檢測(網線插好/無線關聯)。IFF_LOWER_UP(內核 2.6.31+):物理層面 UP,更準確。#include#include #include #include #include #include int check_link_status(const char *ifname) { struct ifaddrs *ifa, *iflist; if (getifaddrs(&iflist) < 0) { perror("getifaddrs"); return -1; } for (ifa = iflist; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_INET) continue; // 僅看 IPv4 接口 if (strcmp(ifa->ifa_name, ifname) != 0) continue; unsigned flags = ifa->ifa_flags; if (!(flags & IFF_UP)) { printf("DEVICE_DOWN\n"); freeifaddrs(iflist); return 1; } if (!(flags & IFF_RUNNING)) { printf("DEVICE_UNPLUGGED\n"); freeifaddrs(iflist); return 2; } printf("DEVICE_LINKED\n"); freeifaddrs(iflist); return 3; } fprintf(stderr, "DEVICE_NOT_FOUND: %s\n", ifname); freeifaddrs(iflist); return 0; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "用法: %s <接口名>\n", argv[0]); return 1; } int ret = check_link_status(argv[1]); fprintf(stderr, "狀態碼: %d\n", ret); return ret == 3 ? 0 : 1; }
gcc check_link.c -o check_link ./check_link eth0 # 輸出示例: # DEVICE_LINKED # 狀態碼: 3
| 維度 | ioctl + SIOCGIFFLAGS | getifaddrs |
|---|---|---|
| 代碼行數 | ~40 | ~25 |
| 多地址支持 | 需多次 ioctl | 單次遍歷鏈表 |
| IPv6 支持 | 額外處理 | 原生支持 |
| 可移植性 | Linux/BSD 差異大 | POSIX.1-2001 標準 |
| 性能 | 系統調用多 | 單次系統調用 |
CAP_NET_ADMIN 讀取接口旗標。IFF_LOOPBACK。IFF_LOWER_UP,用 IFF_RUNNING 替代。ifaddrs.h,需回退 ioctl 方案。