2017-02-17 14 views
1

netlink 라이브러리 버전 3을 배우고 있으며 지정된 네트워크 인터페이스의 ipv4 주소를 얻는 방법을 알고 싶습니다. 나는 MAC 주소를 얻을 수 있으며 심지어 링크 데이터 구조에서 인터페이스 이름을 다시 쿼리 할 수 ​​있지만 libnl 및 libnl-route libs를 사용하여 IP 주소를 얻는 방법을 알 수는 없습니다. libnl-cli lib를 사용하여 IP 주소를 얻는 코드를 찾았지만 결과를 파일 설명자에 덤핑하는 것입니다 (stdout으로 생각하십시오). 이 라이브러리의 메일 링리스트에 메일을 보내 왔지만 응답을받지 못했습니다. 여기 https://gist.github.com/netskink/4f554ed6657954b17ab255ad5bc6d1f0리눅스에서 libnl3 (netlink 버전 3)을 사용하여 인터페이스의 ipv4 주소를 얻는 방법은 무엇입니까?

내 결과입니다 : 여기

내 코드입니다

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

필자의 ioctl을 사용하여 IP 주소를 검색 할 수있는 메커니즘을 볼 수 있지만, 넷 링크 LIB은을 사용하여 IP 주소를 반환 할 수 있기 때문에 cli 서브 라이브러리 나는 그것이 할 수 있다고 생각하지만 길을 이해할 수 없다.

+0

아마도 'nl-addr-list'의 소스가 관심의 대상일까요? https : // github.com/thom311/libnl/blob/master/src/nl-addr-list.c – larsks

+0

그 코드는 cli 서브 라이브러리를 사용합니다. 그건 내가 관심있는 것이 아니다. 원래의 질문을 보라. – netskink

답변

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를 사용하십시오.

+0

감사합니다. 내일 귀하의 솔루션을 사용하려고 시도 할 것입니다. 많은 감사합니다. – netskink

+0

실제로 작동합니다. 많은 감사합니다. 귀하의 솔루션을 대답으로 표시하고 가시성을 위해 투표했습니다. 도와 주셔서 감사합니다. – netskink

1

사용자 작성시 2518959의 답변입니다.

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