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

时间:2017-02-17 21:26:07

标签: linux netlink

我正在学习netlink库版本3,我想知道如何获取指定网络接口的ipv4地址。我可以从链接数据结构中获取mac地址甚至重新查询接口名称,但我无法弄清楚如何使用libnl和libnl-route libs获取ip地址。我确实找到了一些使用libnl-cli lib来获取ip地址的代码,但这是用于将结果转储到文件描述符(想想stdout)。我已将邮件发送到此库的邮件列表,但我没有收到回复。

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

以下是我的结果:

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

我已经看到了使用ioctls检索ip地址的机制,但由于netlink lib可以使用cli子库返回ip地址,我认为它可以完成,但我无法找到方法。

2 个答案:

答案 0 :(得分:3)

接口可以有多个地址(ipv4和ipv6地址 - 代码示例给了我一个ipv4和一个ipv6),所以没有这样的函数返回一个接口地址。如果只有你有特定的本地地址,你可以调用rtnl_addr_get。相反,你可以迭代地址。

#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);
}

您可以从缓存中迭代地址,看看它们是否包含所需的地址(查看ifindex)。请查看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);

要检查ip版本,请使用rtnl_addr_get_family。

答案 1 :(得分:1)

建立在user2518959的答案之上。

rtnl_addr_alloc_cache和rtnl_link_alloc_cache都返回一个nl_cache对象/结构。即使这两个结果属于同一类型,它们也有不同的例程,可以在每个结果上使用。

从rtnl_addr_alloc_cache返回的nl_cache可用于获取rtnl_addr对象/结构。反过来可以用来调用rtnl_addr_get_local来获取ipv4或ipv6地址。

相反,从rtnl_link_alloc_cache返回的nl_cache可用于获取接口名称(eth0,enp6s0,...)和mac地址。例程分别是rtnl_link_get_by_name和rtnl_link_get_addr。

在任何一种情况下,两者之间的公共链接是例程rtnl_addr_get_index和rtnl_link_get_index,它返回一个接口索引,该索引可用于关联来自每个高速缓存的条目。即。来自nl_cache的addr版本的接口1和来自链接nl_cache的接口1是相同的接口。一个给出ip地址,另一个给出mac地址和名称。

最后,隧道将有一个IP地址,但没有mac,因此它没有链接名称或mac地址。

这是一些代码,它显示了user25185959方法和另一种显示关系的替代方法。 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);
}