2017-02-17 4 views
1

J'apprends la bibliothèque netlink version 3 et je veux savoir comment obtenir l'adresse IPv4 d'une interface réseau spécifiée. Je peux obtenir l'adresse mac et même requery le nom de l'interface à partir d'une structure de données de lien, mais je ne peux pas comprendre comment obtenir l'adresse IP en utilisant les bibliothèques libnl et libnl-route. J'ai trouvé du code pour obtenir l'adresse IP en utilisant la libnl-cli lib mais c'est pour faire tomber les résultats dans un descripteur de fichier (pensez à stdout). J'ai envoyé un mail à la liste de diffusion pour cette bibliothèque mais je n'ai pas eu de réponse.Comment obtenir l'adresse ipv4 d'une interface en utilisant libnl3 (netlink version 3) sur Linux?

Voici mon code: https://gist.github.com/netskink/4f554ed6657954b17ab255ad5bc6d1f0

Voici mes résultats:

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

Ive vu le mécanisme pour récupérer l'adresse IP à l'aide ioctl, mais depuis NetLink lib peut renvoyer l'adresse IP à l'aide du cli sublibrary Je pense que cela peut être fait mais je n'arrive pas à trouver un moyen.

+0

https: // github.com/thom311/libnl/blob/maître/src/nl-addr-list.c – larsks

+0

Ce code utilise la sous-bibliothèque cli. Ce n'est pas ce qui m'intéresse. Voir ma question originale. – netskink

Répondre

3

L'interface peut avoir plusieurs adresses (adresses ipv4 et ipv6 - l'exemple de code m'a donné un ipv4 et un ipv6), donc il n'y a pas de telle fonction qui retourne une adresse pour l'interface. Si seulement vous aviez une adresse locale spécifique, vous auriez pu appeler rtnl_addr_get. Au lieu de cela, vous pouvez itérer des adresses.

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

Vous pouvez itérer des adresses du cache et voir si elles contiennent l'adresse nécessaire (en regardant ifindex). S'il vous plaît jeter un oeil à https://www.infradead.org/~tgr/libnl/doc/api/cache_8c_source.html pour les fonctions utiles (il y a une fonction de filtre).

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

Et pour vérifier la version IP, utilisez rtnl_addr_get_family.

+0

Merci. Je vais essayer d'utiliser votre solution demain. Merci beaucoup. – netskink

+0

Cela fonctionne en effet. Merci beaucoup. J'ai marqué votre solution comme réponse et j'ai voté pour la visibilité. J'apprécie beaucoup votre aide. – netskink

1

À partir de la réponse de l'utilisateur2518959.

rtnl_addr_alloc_cache et rtnl_link_alloc_cache renvoient tous deux un objet/structure nl_cache. Même si ces deux résultats sont du même type, ils ont des routines différentes qui peuvent être utilisées sur chacun.

La commande nl_cache retournée par rtnl_addr_alloc_cache peut être utilisée pour obtenir l'objet/structure rtnl_addr. Qui sont à leur tour peuvent être utilisés pour appeler rtnl_addr_get_local pour obtenir l'adresse ipv4 ou ipv6. Par contre, le nl_cache retourné par rtnl_link_alloc_cache peut être utilisé pour obtenir le nom de l'interface (eth0, enp6s0, ...) et l'adresse mac. Les routines sont respectivement rtnl_link_get_by_name et rtnl_link_get_addr.

Dans les deux cas, le lien commun entre les deux est la routine rtnl_addr_get_index et rtnl_link_get_index qui renvoient un index d'interface qui peut être utilisé pour relier chaque entrée de chaque cache. c'est à dire. l'interface 1 de la version addr de nl_cache et l'interface 1 du lien nl_cache sont la même interface. L'un donne l'adresse IP et l'autre donne l'adresse mac et le nom. Enfin, un tunnel aura une adresse IP mais pas de mac donc il n'aura pas de nom de lien ou d'adresse MAC.

Voici un code qui montre l'approche user25185959 et une autre méthode qui montre la relation de manière explicite. User2518959 a transmis le numéro d'interface dans le rappel pour filtrer les interfaces. Peut-être la source de `nl-addr-list` est-elle intéressante?

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