在Linux中,我该如何以编程方式找到特定网络设备配置的IP地址/子网掩码/网关?

10
我想编写一段代码,检查每个网络设备(例如eth0、lo、主设备)的统计数据和配置数据。我可以在/sys/class/net/...中找到大部分配置数据和所有统计数据,但是我无法找到任何C/C++ API或procfs/sysfs中列出的inet地址、子网掩码和网关。
我尝试了以下一些替代方案: - 解析ifconfig/route等工具的输出:我不想每次进行检查时都启动一个子进程。 - 解析/etc/sysconfig/network-scripts/:只会给我启动配置而不是当前状态。
由于这段代码是为我们公司的产品设计的,需要经过彻底的外部库检查(这意味着添加任何外部库将花费我很长时间),因此我更喜欢依赖于Linux本机API而非外部库的解决方案。
谢谢!

2
由于没有任何答案覆盖如何获取网关(s)。在此处查找答案:http://stackoverflow.com/a/28322370/694576 - alk
3个回答

19

你可以使用ifreq结构体和ioctl()调用来获取所有接口信息:

这里是man页面Ifreq manpage

/* local interface info */
    typedef struct{
        char *iface;
        struct ether_addr hwa;
        struct in_addr ipa;
        struct in_addr bcast;
        struct in_addr nmask;
        u_short mtu;
    } ifcfg_t; 
    /*
     * Grabs local network interface information and stores in a ifcfg_t 
     * defined in network.h, returns 0 on success -1 on failure
    */
    int get_local_info(int rsock, ifcfg_t *ifcfg)
    {
        struct ifreq ifr;

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
        if((ioctl(rsock, SIOCGIFHWADDR, &ifr)) == -1){
            perror("ioctl():");
            return -1;
        }
        memcpy(&(ifcfg->hwa), &ifr.ifr_hwaddr.sa_data, 6);

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
        if((ioctl(rsock, SIOCGIFADDR, &ifr)) == -1){
            perror("ioctl():");
            return -1;
        }
        memcpy(&ifcfg->ipa, &(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr, 4);

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
        if((ioctl(rsock, SIOCGIFBRDADDR, &ifr)) == -1){
            perror("ioctl():");
            return -1;
        }
        memcpy(&ifcfg->bcast, &(*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr, 4);

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
        if((ioctl(rsock, SIOCGIFNETMASK, &ifr)) == -1){
            perror("ioctl():");
            return -1;
        }
        memcpy(&ifcfg->nmask.s_addr, &(*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr, 4);

        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
        if((ioctl(rsock, SIOCGIFMTU, &ifr)) == -1){
            perror("ioctl():");
            return -1;
        }
        ifcfg->mtu = ifr.ifr_mtu;

        return 0;
    }

快速编辑,此函数要求在调用之前已经分配了接口,像这样:

strcpy(if_cfg->iface, iface)

确保你先分配了内存,然后这样调用

if((get_local_info(sock, if_cfg)) != 0){
    printf("Unable to get network device info\n");
    return -1;
}

6
现在,使用getifaddrs()调用(http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html)是首选方法来实现这一点(例如,它支持IPv6地址)。 - caf

10

通过在随机的Linux系统上使用strace运行netstat,可以看到以下一系列调用:

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
access("/proc/net/if_inet6", R_OK)      = 0
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 5
[snip]
open("/proc/net/dev", O_RDONLY)         = 6
fstat64(6, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f91000
read(6, "Inter-|   Receive               "..., 1024) = 575
read(6, "", 1024)                       = 0
close(6)                                = 0
munmap(0xb7f91000, 4096)                = 0
ioctl(4, SIOCGIFCONF, {64, {{"lo", {AF_INET, inet_addr("127.0.0.1")}}, {"eth0", {AF_INET, inet_addr("192.168.0.
8")}}}}) = 0
ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(5, SIOCGIFHWADDR, {ifr_name="eth0", ifr_hwaddr=00:11:09:ca:d1:55}) = 0
ioctl(5, SIOCGIFMETRIC, {ifr_name="eth0", ifr_metric=0}) = 0
ioctl(5, SIOCGIFMTU, {ifr_name="eth0", ifr_mtu=1500}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0x4000, irq=10, dma=0, port=0
}}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0x4000, irq=10, dma=0, port=0
}}) = 0
ioctl(5, SIOCGIFTXQLEN, {ifr_name="eth0", ifr_qlen=1000}) = 0
ioctl(4, SIOCGIFADDR, {ifr_name="eth0", ifr_addr={AF_INET, inet_addr("192.168.0.8")}}) = 0
ioctl(4, SIOCGIFDSTADDR, {ifr_name="eth0", ifr_dstaddr={AF_INET, inet_addr("192.168.0.8")}}) = 0
ioctl(4, SIOCGIFBRDADDR, {ifr_name="eth0", ifr_broadaddr={AF_INET, inet_addr("192.168.0.255")}}) = 0
ioctl(4, SIOCGIFNETMASK, {ifr_name="eth0", ifr_netmask={AF_INET, inet_addr("255.255.255.0")}}) = 0

所以,"秘密"似乎是创建一个 socket,然后进行一系列的 ioctl() 调用以访问当前的信息。


3

请查看/usr/include/ifaddrs.h。这里有一个GNU特定的API。

int getifaddrs (struct ifaddrs **ifap);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接