我正在学习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地址,我认为它可以完成,但我无法找到方法。
答案 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);
}