首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux C 獲取本機網卡 IP 與介面狀態:ioctl + SIOCGIFCONF 完整實現

     編輯:關於C語言

前言

在 Linux 系統編程中,獲取本機所有網卡的 IP 地址、介面名稱與運行狀態(UP/DOWN/PROMISC)是網路工具、監控代理、容器網路組件的基礎需求。本文提供一個完整、可編譯的 C++ 類封裝,使用 `ioctl` + `SIOCGIFCONF` / `SIOCGIFFLAGS` / `SIOCGIFADDR` 實現,無第三方依賴。

核心數據結構


#define MAX_INTERFACES 16

enum IfStatus { UNKNOWN, PROMISC, UP, RUNNING, DOWN };

struct IfInfo {
    char name[IFNAMSIZ];      // 介面名稱,如 "eth0"
    char address[INET_ADDRSTRLEN]; // IPv4 地址字串
    IfStatus status;
};

class IfConfig {
public:
    IfInfo interfaces[MAX_INTERFACES];
    int count = 0;
};

核心實現:`getLocalHostAddress`


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int getLocalHostAddress(IfConfig &cfg) {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) { perror("socket"); return -1; }

    struct ifreq ifr[MAX_INTERFACES];
    struct ifconf ifc;
    ifc.ifc_len = sizeof(ifr);
    ifc.ifc_buf = (caddr_t)ifr;

    // 1. 獲取介面列表
    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
        perror("SIOCGIFCONF");
        close(sock);
        return -1;
    }

    int num = ifc.ifc_len / sizeof(struct ifreq);
    for (int i = 0; i < num && cfg.count < MAX_INTERFACES; ++i) {
        // 2. 獲取介面標誌 (UP/BROADCAST/RUNNING/PROMISC...)
        if (ioctl(sock, SIOCGIFFLAGS, &ifr[i]) < 0) continue;

        strncpy(cfg.interfaces[cfg.count].name, ifr[i].ifr_name, IFNAMSIZ-1);

        unsigned short flags = ifr[i].ifr_flags;
        if (flags & IFF_PROMISC) cfg.interfaces[cfg.count].status = PROMISC;
        else if (flags & IFF_UP) cfg.interfaces[cfg.count].status = UP;
        else if (flags & IFF_RUNNING) cfg.interfaces[cfg.count].status = RUNNING;
        else cfg.interfaces[cfg.count].status = DOWN;

        // 3. 獲取 IPv4 地址
        if (ioctl(sock, SIOCGIFADDR, &ifr[i]) == 0) {
            struct sockaddr_in* sin = (struct sockaddr_in*)&ifr[i].ifr_addr;
            inet_ntop(AF_INET, &sin->sin_addr,
                      cfg.interfaces[cfg.count].address, INET_ADDRSTRLEN);
        } else {
            strcpy(cfg.interfaces[cfg.count].address, "0.0.0.0");
        }
        cfg.count++;
    }
    close(sock);
    return cfg.count;
}

主程式測試


int main() {
    IfConfig cfg;
    int n = getLocalHostAddress(cfg);
    for (int i = 0; i < n; ++i) {
        printf("[%d] %-8s  %-15s  %s\n", i,
               cfg.interfaces[i].name,
               cfg.interfaces[i].address,
               (cfg.interfaces[i].status == UP) ? "UP" :
               (cfg.interfaces[i].status == RUNNING) ? "RUNNING" :
               (cfg.interfaces[i].status == PROMISC) ? "PROMISC" : "DOWN");
    }
    return 0;
}

編譯與執行


g++ -std=c++11 -o get_if get_if.cpp
./get_if
[0] lo        127.0.0.1       UP
[1] eth0      192.168.1.100   RUNNING
[2] docker0   172.17.0.1      UP

進階擴展

需求方案
IPv6 地址`AF_INET6` + `SIOCGIFADDR` + `inet_ntop(AF_INET6, ...)`
硬體地址 (MAC)`SIOCGIFHWADDR` + `ifr.ifr_hwaddr.sa_data`
MTU`SIOCGIFMTU`
流量統計讀取 `/proc/net/dev` 或 `rtnetlink` (NETLINK)
動態變更通知`NETLINK_ROUTE` + `RTM_NEWLINK`/`RTM_DELLINK`/`RTM_NEWADDR`
跨平台 (Windows)`GetAdaptersAddresses` (IPHLPAPI) / `WSAIoctl(SIO_GET_INTERFACE_LIST)`

常見坑點

  1. **權限**:讀取介面配置通常需 `CAP_NET_ADMIN` 或 root,容器中需 `--cap-add=NET_ADMIN`。
  2. **緩衝區大小**:`MAX_INTERFACES` 過小會截斷,生產代碼應先 `SIOCGIFCONF` 獲取所需大小,再動態分配。
  3. **介面別名**:`eth0:1` 等邏輯介面同屬一物理介面,`SIOCGIFCONF` 會分別返回,需按名稱去重。
  4. **IPv4/IPv6 混合**:`ifr_addr` 家族由 `sa_family` 指示,務必檢查後再轉型。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved