如何从接口IP地址获取接口索引

4

请问如何从接口IP地址获取接口索引? 例如,如果接口IP地址为192.168.23.25,则它的接口索引是什么。

我想补充一点,我需要在C语言编写的代码中使用它,所以如果有任何带有某些选项的函数可以根据接口IP地址给我接口索引号。


可能是 https://dev59.com/j3RC5IYBdhLWcg3wCMrX 的重复问题。 - Tino
7个回答

4
你应该可以使用getifaddrs()来实现这个功能。它应该会考虑到MarkR关于次要地址的担忧。作为测试,你可以添加如下内容:
ip addr add 192.168.25.23/24 dev eth0

编译和运行手册页上的示例程序应该显示类似于:
lo  address family: 17 (AF_PACKET)
eth0  address family: 17 (AF_PACKET)
lo  address family: 2 (AF_INET)
        address: <127.0.0.1>
eth0  address family: 2 (AF_INET)
        address: <192.168.1.105>
eth0  address family: 2 (AF_INET)
        address: <192.168.25.23>
lo  address family: 10 (AF_INET6)
        address: <::1>
eth0  address family: 10 (AF_INET6)
        address: <fe84::82d6:baaf:fe14:4c22%eth0>

你可以在遍历列表时获取索引,但你还可以额外查看if_nametoindex()if_indextoname()if_nameindex()函数。由于你可以将地址与接口名称关联起来,因此只需根据需要调用这些函数即可。

3
以编程方式,使用if_nametoindex()。我已经在Ubuntu 12.04(内核3.11.0-15-generic)上验证过。
这是示例代码片段,
#include <net/if.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    for (int ix=1; ix<argc; ix++)
    {
        unsigned int rc = if_nametoindex(argv[ix]);
        if (rc) {
            printf("interface [%s] has index : %d\n", argv[ix], rc);
        }
        else {
            perror("if_nametoindex");
        }
    }
}

使用示例:

$ ./if_index eth0
interface [eth0] has index : 2

此外,非编程方法是阅读/proc/net/if_inet6条目。第二列是相应的接口索引。
$ cat /proc/net/if_inet6 
00000000000000000000000000000001 01 80 10 80       lo
fe800000000000000a0027fffe1a2a32 03 40 20 80     eth1
fe800000000000000a0027fffe08b9ca 02 40 20 80     eth0

2
你可以简单地使用IP的json输出,并通过jq进行扫描:
# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
...

使用正确的参数:

# ip -json a s lo | jq '.[] | .ifindex'
1
# ip -json a s enp2s0 | jq '.[] | .ifindex'
2

1
我喜欢这种方法,但我认为在这些论坛中拼写出命令比缩写更好。这样,新手就能更容易地阅读命令并找到文档。因此,这里是新手解码器环(tm):ip -json a s lo 表示 ip -json address show lo,而 ip -json a s enp2s0 表示 ip -json address show enp2s0 - chris
我刚意识到这并没有回答楼主的问题,但我仍然喜欢这种方法。如果我能找到一种方式以这种方式回答楼主的问题,我会发布它。 - chris

2

你不能这样做,你需要查看所有接口,然后循环遍历所有IP地址,直到找到你想要的那一个。我认为这段代码可以实现你的需求。

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

int main(int argc, char *argv[])
{
    in_addr_t ia;
    int id;

    ia = inet_addr(argv[1]);

    id = do_lookup(ia);
}

int do_lookup(in_addr_t ia) {
    char          buf[1024];
    struct ifconf ifc;
    struct ifreq *ifr;
    int           sck;
    int           nInterfaces;
    int           i;

/* Get a socket handle. */
    sck = socket(AF_INET, SOCK_DGRAM, 0);
    if(sck < 0)
    {
        perror("socket");
        return -1;
    }

/* Query available interfaces. */
    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    if(ioctl(sck, SIOCGIFCONF, &ifc) < 0)
    {
        perror("ioctl(SIOCGIFCONF)");
        return -1;
    }

/* Iterate through the list of interfaces. */
    ifr         = ifc.ifc_req;
    nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
    for(i = 0; i < nInterfaces; i++)
    {
        struct ifreq *item = &ifr[i];
        if(((struct sockaddr_in *)&item->ifr_addr)->sin_addr.s_addr == ia) {
            return i;
        }
    }

    return -1;
}

2
上次我检查时,SIOCGIFCONF仅为每个接口提供主地址;因此,具有>1的接口将会错过它们的第二个和随后的地址。 - MarkR
你是正确的。他没有指示第二个地址,而且每个设备有多个地址并不常见。 - Richard June
嘿,Paolo,你的编辑意味着要改变什么?API有变化吗? - Richard June

0
您可以使用以下代码:它将列出 Linux 操作系统上的网络接口。
    #include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>

int main(void)
{
    char          buf[1024];
    struct ifconf ifc;
    struct ifreq *ifr;
    int           sck;
    int           nInterfaces;
    int           i;

/* Get a socket handle. */
    sck = socket(AF_INET, SOCK_DGRAM, 0);
    if(sck < 0)
    {
        perror("socket");
        return 1;
    }

/* Query available interfaces. */
    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    if(ioctl(sck, SIOCGIFCONF, &ifc) < 0)
    {
        perror("ioctl(SIOCGIFCONF)");
        return 1;
    }

/* Iterate through the list of interfaces. */
    ifr         = ifc.ifc_req;
    nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
    for(i = 0; i < nInterfaces; i++)
    {
        struct ifreq *item = &ifr[i];

    /* Show the device name and IP address */
        printf("%s: IP %s",
               item->ifr_name,
               inet_ntoa(((struct sockaddr_in *)&item->ifr_addr)->sin_addr));

    /* Get the MAC address */
        if(ioctl(sck, SIOCGIFHWADDR, item) < 0)
        {
            perror("ioctl(SIOCGIFHWADDR)");
            return 1;
        }

    /* Get the broadcast address (added by Eric) */
        if(ioctl(sck, SIOCGIFBRDADDR, item) >= 0)
            printf(", BROADCAST %s", inet_ntoa(((struct sockaddr_in *)&item->ifr_broadaddr)->sin_addr));
        printf("\n");
    }

        return 0;
}

我从这里得到了它。


0
SIOCGIFCONF对使用其他IP地址添加的副本IP地址不起作用。
ip addr add 192.168.25.23/24 dev eth1

如果你真的需要这样做,那么看看“ip addr sh”使用了什么 - 可能是一个netlink套接字操作(其中涉及非常奇怪的宏)。

0

注意:我刚刚注意到原帖要求使用C语言解决。非常抱歉。然而,由于搜索引擎将我明确包含bash的搜索短语带到了这里(而且我已经输入了所有这些内容),希望大家不介意我在这里留下它。如果有时间,我会稍后将其转换为gist,并尝试在此处删除它。

我有一个相对简单的解决方案,适用于shell脚本。我喜欢@Oliver的建议,但它没有达到获取给定IP地址接口索引的原始目标。我稍微调整了他的答案。假设我想知道当前绑定到192.168.1.96的接口索引是哪个。这种方法可以工作:

chris@anglesey:~$ ip -json address show \
  | jq '.[] | select(.addr_info[].local == "192.168.1.96") | .ifindex'

输出:

60

你可以通过取消选择 .ifindex 元素来检查它是否正确,从而获取网卡的所有 JSON 数据。
chris@anglesey:~$ ip -json address show   | jq '.[] | select(.addr_info[].local == "192.168.1.96") '
{
  "ifindex": 60,
  "ifname": "enx808abdbef5eb",
  "flags": [
    "BROADCAST",
    "MULTICAST",
    "UP",
    "LOWER_UP"
  ],
  "mtu": 1500,
  "qdisc": "fq_codel",
  "operstate": "UP",
  "group": "default",
  "txqlen": 1000,
  "link_type": "ether",
  "address": "80:8a:bd:be:f5:eb",
  "broadcast": "ff:ff:ff:ff:ff:ff",
  "addr_info": [
    {
      "family": "inet",
      "local": "192.168.1.96",
      "prefixlen": 24,
      "broadcast": "192.168.1.255",
      "scope": "global",
      "dynamic": true,
      "noprefixroute": true,
      "label": "enx808abdbef5eb",
      "valid_life_time": 71302,
      "preferred_life_time": 71302
    },
    {
      "family": "inet6",
      "local": "fe80::767a:4f36:1fb0:cd0b",
      "prefixlen": 64,
      "scope": "link",
      "noprefixroute": true,
      "valid_life_time": 4294967295,
      "preferred_life_time": 4294967295
    }
  ]
}

通过更改命令中给定的IP地址,此方法将自动处理辅助地址和IPv6地址。

此外,如果您只知道要查找的地址的子字符串,则可以进行微小调整以查找该地址。虽然不能像按子网过滤那样有用,但有时可能会有所帮助。

chris@anglesey:~$ ip -json addr show | jq '.[] | select(.addr_info[].local | contains("192.168.1")) | .ifindex'

输出:

60

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