简体   繁体   English

如何在Linux上使用libnl3(netlink版本3)获取接口的ipv4地址?

[英]How to get ipv4 address of an interface using libnl3 (netlink version 3) on linux?

I'm learning the netlink library version 3 and I want to know how to get the ipv4 address of a specified network interface. 我正在学习netlink库版本3,我想知道如何获取指定网络接口的ipv4地址。 I can get the mac address and even requery the interface name from a link data structure, but I can not figure out how to get the ip address using the libnl and libnl-route libs. 我可以获取mac地址,甚至可以从链接数据结构中重新查询接口名称,但是我不知道如何使用libnl和libnl-route库获取IP地址。 I did find some code to get the ip address using the libnl-cli lib but that is for dumping the results to a file descriptor (think stdout). 我确实找到了一些使用libnl-cli lib获取ip地址的代码,但这是将结果转储到文件描述符中的方式(请考虑stdout)。 I have sent mail to the mailing list for this library but I have not gotten a response. 我已将邮件发送到该库的邮件列表,但没有得到答复。

Here is my code: https://gist.github.com/netskink/4f554ed6657954b17ab255ad5bc6d1f0 这是我的代码: https : //gist.github.com/netskink/4f554ed6657954b17ab255ad5bc6d1f0

Here are my results: 这是我的结果:

./stats 
Returned link name is enp6s0
Returned link addr is a0:36:9f:66:93:13

Ive seen the mechanism to retrieve the ip address using ioctls, but since netlink lib can return the ip address using the cli sublibrary I figure it can be done but I can not figure out a way. 我已经看到了使用ioctls检索ip地址的机制,但是由于netlink lib可以使用cli子库返回ip地址,我想可以做到,但是我找不到办法。

Interface can have multiple addresses (ipv4 and ipv6 addresses - code sample gave me one ipv4 and one ipv6), so there is no such function that returns one address for interface. 接口可以有多个地址(ipv4和ipv6地址-代码示例给了我一个ipv4和一个ipv6),因此没有这样的函数为接口返回一个地址。 If only you had specific local address, you could have called rtnl_addr_get. 如果只有特定的本地地址,则可以调用rtnl_addr_get。 Instead you can iterate addresses. 相反,您可以迭代地址。

#include <libnl3/netlink/cache.h>

void addr_cb(struct nl_object *o, void *data)
{
    int ifindex = (int)(intptr_t)data;
    struct rtnl_addr *addr = (rtnl_addr *)o;
    if (NULL == addr) {
        /* error */
        printf("addr is NULL %d\n", errno);
        return;
    }

    int cur_ifindex = rtnl_addr_get_ifindex(addr);
    if(cur_ifindex != ifindex)
        return;

    const struct nl_addr *local = rtnl_addr_get_local(addr);
    if (NULL == local) {
        /* error */
        printf("rtnl_addr_get failed\n");
        return;
    }

    char addr_str[ADDR_STR_BUF_SIZE];
    const char *addr_s = nl_addr2str(local, addr_str, sizeof(addr_str));
    if (NULL == addr_s) {
        /* error */
        printf("nl_addr2str failed\n");
        return;
    }
    fprintf(stdout, "\naddr is: %s\n", addr_s);
}

You can iterate addresses from cache and see if they contain needed address (looking at ifindex). 您可以从缓存中迭代地址,并查看它们是否包含所需的地址(查看ifindex)。 Please take a look at https://www.infradead.org/~tgr/libnl/doc/api/cache_8c_source.html for useful functions (there is some filter function). 请查看https://www.infradead.org/~tgr/libnl/doc/api/cache_8c_source.html了解有用的功能(有一些过滤器功能)。

int ifindex = rtnl_link_get_ifindex(p_rtnl_link);
printf("ifindex: %d\n", ifindex);

bool empty = nl_cache_is_empty(addr_cache);
printf("empty: %d\n", empty);

nl_cache_foreach(addr_cache,
        addr_cb, (void*)(intptr_t)ifindex);

And to check ip version use rtnl_addr_get_family. 并使用rtnl_addr_get_family检查IP版本。

Building upon user2518959's answer. 以user2518959的答案为基础。

The rtnl_addr_alloc_cache and rtnl_link_alloc_cache both return a nl_cache object/structure. rtnl_addr_alloc_cache和rtnl_link_alloc_cache都返回nl_cache对象/结构。 Even those these two results are of the same type, they have different routines which can be used on each. 即使这两个结果属于同一类型,它们也可以使用不同的例程。

The nl_cache returned from rtnl_addr_alloc_cache can be used to get rtnl_addr object/structures. 从rtnl_addr_alloc_cache返回的nl_cache可用于获取rtnl_addr对象/结构。 Which are in turn can be used to call rtnl_addr_get_local to get the ipv4 or ipv6 address. 依次调用rtnl_addr_get_local可以获取ipv4或ipv6地址。

In contrast, the nl_cache returned from rtnl_link_alloc_cache can be used to get the interface name (eth0, enp6s0, ...) and the mac address. 相反,从rtnl_link_alloc_cache返回的nl_cache可用于获取接口名称(eth0,enp6s0,...)和mac地址。 The routines are rtnl_link_get_by_name and rtnl_link_get_addr respectively. 例程分别是rtnl_link_get_by_name和rtnl_link_get_addr。

In either case, the common link between the two is routine rtnl_addr_get_index and rtnl_link_get_index which return an interface index which can be used to relate either entry from each cache. 无论哪种情况,两者之间的公共链接是例程rtnl_addr_get_index和rtnl_link_get_index,它们返回一个接口索引,该接口索引可用于关联每个高速缓存中的任一条目。 ie. 即。 interface 1 from the addr version of nl_cache and interface 1 from the link nl_cache are the same interface. 来自nl_cache的addr版本的interface 1和来自链接nl_cache的interface 1是相同的接口。 One gives the ip address and the other gives the mac address and name. 一个给出IP地址,另一个给出MAC地址和名称。

Lastly, a tunnel will have an ip address but no mac so it will not have a link name or mac address. 最后,隧道将具有IP地址,但没有mac,因此它将没有链接名称或mac地址。

Here is some code which shows user25185959 approach and an alternate method which shows the relationship explictly. 这是一些代码,显示了user25185959的方法,另一种方法显示了这种关系。 User2518959 passed the interface number into the callback to filter out interfaces. User2518959将接口号传递到回调中以过滤出接口。

#include <libnl3/netlink/netlink.h>
#include <libnl3/netlink/route/link.h>
#include <libnl3/netlink/route/addr.h>
#include <libnl3/netlink/cache.h>
#include <libnl3/netlink/route/addr.h>

#include <errno.h>



/*
gcc ipchange.c -o ipchange $(pkg-config --cflags --libs libnl-3.0 libnl-route-3.0 libnl-cli-3.0)
*/

#include <stdbool.h>

#define ADDR_STR_BUF_SIZE 80

void addr_cb(struct nl_object *p_nl_object, void *data) {

    int ifindex = (int) (intptr_t) data;  // this is the link index passed as a parm
    struct rtnl_addr *p_rtnl_addr;
    p_rtnl_addr = (struct rtnl_addr *) p_nl_object;
    int result;

    if (NULL == p_rtnl_addr) {
        /* error */
        printf("addr is NULL %d\n", errno);
        return;
    }

    // This routine is not mentioned in the doxygen help.  
    // It is listed under Attributes, but no descriptive text.
    // this routine just returns p_rtnl_addr->a_ifindex
    int cur_ifindex = rtnl_addr_get_ifindex(p_rtnl_addr);
    if(cur_ifindex != ifindex) {
        // skip interaces where the index differs.
        return;
    }

    // Adding this to see if I can filter on ipv4 addr
    // this routine just returns p_rtnl_addr->a_family
    // this is not the one to use
    // ./linux/netfilter.h:    NFPROTO_IPV6   = 10,
    // ./linux/netfilter.h:    NFPROTO_IPV4   =  2,
    // this is the one to use
    // x86_64-linux-gnu/bits/socket.h
    // defines AF_INET6 = PF_INET6 = 10
    // defines AF_INET  = PF_INET  = 2
    result = rtnl_addr_get_family(p_rtnl_addr);
    // printf( "family is %d\n",result);
    if (AF_INET6 == result) {
    // early exit, I don't care about IPV6
    return;
    }

    // This routine just returns p_rtnl_addr->a_local
    const struct nl_addr *p_nl_addr_local = rtnl_addr_get_local(p_rtnl_addr);
    if (NULL == p_nl_addr_local) {
        /* error */
        printf("rtnl_addr_get failed\n");
        return;
    }

    char addr_str[ADDR_STR_BUF_SIZE];
    const char *addr_s = nl_addr2str(p_nl_addr_local, addr_str, sizeof(addr_str));
    if (NULL == addr_s) {
        /* error */
        printf("nl_addr2str failed\n");
        return;
    }
    fprintf(stdout, "\naddr is: %s\n", addr_s);

}

int main(int argc, char **argv, char **envp) {

    int err;

    struct nl_sock *p_nl_sock;
    struct nl_cache *link_cache;
    struct nl_cache *addr_cache;

    struct rtnl_addr *p_rtnl_addr;
    struct nl_addr *p_nl_addr;
    struct nl_link *p_nl_link;

    struct rtnl_link *p_rtnl_link;


    char addr_str[ADDR_STR_BUF_SIZE];

    char *pchLinkName;
    char *pchLinkAddr;
    char *pchIPAddr;
    char *interface;
    interface = "enp6s0";
    pchLinkAddr = malloc(40);
    pchIPAddr = malloc(40);
    strcpy(pchLinkAddr,"11:22:33:44:55:66");
    strcpy(pchIPAddr,"123.456.789.abc");





    p_nl_sock = nl_socket_alloc();
    if (!p_nl_sock) {
        fprintf(stderr, "Could not allocate netlink socket.\n");
        exit(ENOMEM);
    }



    // Connect to socket
    if(err = nl_connect(p_nl_sock, NETLINK_ROUTE)) {
        fprintf(stderr, "netlink error: %s\n", nl_geterror(err));
        p_nl_sock = NULL;
        exit(err);
    }


    // Either choice, the result below is a mac address
    err = rtnl_link_alloc_cache(p_nl_sock, AF_UNSPEC, &link_cache);
    //err = rtnl_link_alloc_cache(p_nl_sock, AF_INET, &link_cache);
    //err = rtnl_link_alloc_cache(p_nl_sock, IFA_LOCAL, &link_cache);
    if (0 != err) {
        /* error */
    printf("rtnl_link_alloc_cache failed: %s\n", nl_geterror(err));
    return(EXIT_FAILURE);
    }

    err = rtnl_addr_alloc_cache(p_nl_sock, &addr_cache);
    if (0 != err) {
        /* error */
    printf("rtnl_addr_alloc_cache failed: %s\n", nl_geterror(err));
    return(EXIT_FAILURE);
    }



    p_rtnl_link = rtnl_link_get_by_name(link_cache, "enp6s0");
    if (NULL == p_rtnl_link) {
        /* error */
    printf("rtnl_link_get_by_name failed\n");
    return(EXIT_FAILURE);
    }


    pchLinkName = rtnl_link_get_name(p_rtnl_link);
    if (NULL == pchLinkName) {
        /* error */
    printf("rtnl_link_get_name failed\n");
    return(EXIT_FAILURE);
    }
    printf("Returned link name is %s\n",pchLinkName);


    ////////////////////////////////// mac address  
    p_nl_addr = rtnl_link_get_addr(p_rtnl_link);
    if (NULL == p_nl_addr) {
        /* error */
    printf("rtnl_link_get_addr failed\n");
    return(EXIT_FAILURE);
    }


    pchLinkAddr = nl_addr2str(p_nl_addr, pchLinkAddr, 40);
    if (NULL == pchLinkAddr) {
        /* error */
    printf("rtnl_link_get_name failed\n");
    return(EXIT_FAILURE);
    }
    printf("Returned link addr is %s\n",pchLinkAddr);



    ////////////////////////////////// ip address  
    // How to get ip address for a specified interface?








    //
    // The way she showed me.
    //


    // Return interface index of link object
    int ifindex = rtnl_link_get_ifindex(p_rtnl_link);
    printf("ifindex: %d\n", ifindex);

    // She gave me this but its not necessary
    // Returns true if the cache is empty. True if the cache is empty.
    // bool empty = nl_cache_is_empty(addr_cache);
    // printf("empty: %d\n", empty);

    // Call a callback on each element of the cache.  The
    // arg is passed on the callback function.
    // addr_cache is the cache to iterate on
    // addr_cb is the callback function
    // ifindex is the argument passed to the callback function
    // 
    nl_cache_foreach(addr_cache, addr_cb, (void*)(intptr_t)ifindex);



    // This shows that the link index returned from rtnl_addr_get_index
    // and rtnl_link_get_index are equivalent when using the rtnl_addr
    // and rtnl_link from the two respective caches.


   // Another way...
   // This will iterate through the cache of ip's
   printf("Getting the list of interfaces by ip addr cache\n");
   int count = nl_cache_nitems(addr_cache);
   printf("addr_cache has %d items\n",count);
   struct nl_object *p_nl_object;
   p_nl_object = nl_cache_get_first(addr_cache); 
   p_rtnl_addr = (struct rtnl_addr *) p_nl_object;
   for (int i=0; i<count; i++) {
    // This routine just returns p_rtnl_addr->a_local
    const struct nl_addr *p_nl_addr_local = rtnl_addr_get_local(p_rtnl_addr);
    if (NULL == p_nl_addr_local) {
        /* error */
        printf("rtnl_addr_get failed\n");
        return(EXIT_FAILURE);
    }



    int cur_ifindex = rtnl_addr_get_ifindex(p_rtnl_addr);
    printf("This is index %d\n",cur_ifindex);

    const char *addr_s = nl_addr2str(p_nl_addr_local, addr_str, sizeof(addr_str));
    if (NULL == addr_s) {
        /* error */
        printf("nl_addr2str failed\n");
        return(EXIT_FAILURE);
    }
    fprintf(stdout, "\naddr is: %s\n", addr_s);

    //       
    printf("%d\n",i);
    p_nl_object = nl_cache_get_next(p_nl_object); 
        p_rtnl_addr = (struct rtnl_addr *) p_nl_object;

    // Just for grins


    }



   // Another way...
   // This will iterate through the cache of LLC
   printf("Getting the list of interfaces by mac cache\n");
   count = nl_cache_nitems(link_cache);
   printf("addr_cache has %d items\n",count);
   p_nl_object = nl_cache_get_first(link_cache); 
   p_rtnl_link = (struct rtnl_link *) p_nl_object;
   for (int i=0; i<count; i++) {
    // This routine just returns p_rtnl_addr->a_local
    const struct nl_addr *p_nl_addr_mac = rtnl_link_get_addr(p_rtnl_link);
    if (NULL == p_nl_addr_mac) {
        /* error */
        printf("rtnl_addr_get failed\n");
        return(EXIT_FAILURE);
    }

    int cur_ifindex = rtnl_link_get_ifindex(p_rtnl_link);
    printf("This is index %d\n",cur_ifindex);

    const char *addr_s = nl_addr2str(p_nl_addr_mac, addr_str, sizeof(addr_str));
    if (NULL == addr_s) {
        /* error */
        printf("nl_addr2str failed\n");
        return(EXIT_FAILURE);
    }
    fprintf(stdout, "\naddr is: %s\n", addr_s);

    //       
    printf("%d\n",i);
    p_nl_object = nl_cache_get_next(p_nl_object); 
        p_rtnl_link = (struct rtnl_link *) p_nl_object;

    }


    return(EXIT_SUCCESS);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM