根据我的理解,发送RTM_GETLINK
请求会转储系统上的所有接口。我只对特定的界面感兴趣。有没有办法可以设置我的请求,以便向我提供有关特定接口的所有信息?我知道我感兴趣的界面的名称。
答案 0 :(得分:0)
我知道这是一个古老的问题,但是有关如何使用netlink归档内核过滤的在线信息(至少是我发现的信息)并不如人们所愿。
这里有一个很有前途的paper来解释主题和可能的过滤器,我没有测试所有的过滤器,因此没有任何保证。不幸的是,它在代码中缺少一些更实际的示例...
我发现了更多信息here,尽管令我感到困惑的是,该消息来源正在谈论NETLINK_DUMP_STRICT_CHK套接字标志,该标志在我的netlink标头中找不到。似乎他们在内核4.20中将其更改为NETLINK_GET_STRICT_CHK。
无论如何,经过一些研究并寻找了一些iproute2命令之后,我弄清楚了如何应用内核过滤器(至少对于RTM_GETLINK和RTM_GETADDR而言,但其他应该以相同的方式工作)。
查询一个接口的链接信息的示例代码(需要在INTERFACE_NAME中指定!):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#define INTERFACE_NAME "wlp59s0"
int main(int argc, char ** argv) {
// We do not specify nl_groups, as we do not want to be part of a kernel multi
struct sockaddr_nl src_addr = {AF_NETLINK, 0, (__u32) getpid(), 0};
struct sockaddr_nl dest_addr = {AF_NETLINK};
int optval = 1;
struct {
struct nlmsghdr nh;
struct ifinfomsg ifi;
} request = {};
char buffer[4096];
struct iovec iov = {};
struct msghdr msg = {};
struct nlmsghdr *nh;
struct ifinfomsg *ifi;
struct rtattr *attr;
ssize_t n, attr_len;
// Open a netlink socket
int request_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (request_fd == -1) {
fprintf(stderr, "Netlink socket create failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Enable kernel filtering
if (setsockopt(request_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &optval, sizeof(optval)) < 0) {
fprintf(stderr, "Netlink set socket option \"NETLINK_GET_STRICT_CHK\" failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Bind the file descriptor with the option specified in src_addr
if (bind(request_fd, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0) {
fprintf(stderr, "Netlink socket bind failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Prepare address request for the kernel
// Message length
request.nh.nlmsg_len = NLMSG_LENGTH(sizeof(request.ifi));
// We are interested in link information
request.nh.nlmsg_type = RTM_GETLINK;
// Request and dump filtered flags
request.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP_FILTERED;
// No address family specified
request.ifi.ifi_family = AF_NETLINK;
// The filter option we pass to netlink. More filters are possible...
request.ifi.ifi_index = (int) if_nametoindex(INTERFACE_NAME);
// Place the request in iovec
iov.iov_base = &request;
iov.iov_len = request.nh.nlmsg_len;
// Place iovec struct in message
msg.msg_name = &dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// Send the request message
if (sendmsg(request_fd, (struct msghdr *) &msg, 0) < 0) {
fprintf(stderr, "Error sending interface request to netlink: %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Prepare iovec for the response
memset(&iov, 0, sizeof(iov));
iov.iov_base = buffer;
iov.iov_len = sizeof(buffer);
// Receive the response from netlink
n = recvmsg(request_fd, &msg, 0);
if (n < 0) {
fprintf(stderr, "Error receiving message from netlink: %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Loop over the netlink header contained in the message (should only be one, since we requested one specific interface)
for (nh = (struct nlmsghdr *) buffer; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
// type of the response we are looking for
if (nh->nlmsg_type == RTM_NEWLINK) {
// Get and print ifinfomsg struct
ifi = (struct ifinfomsg *) NLMSG_DATA(nh);
printf("Family: %u\n", ifi->ifi_family);
printf("Device type: %u\n", ifi->ifi_type);
printf("Index: %u\n", ifi->ifi_index);
printf("Flags: %u\n", ifi->ifi_flags);
printf("Change mask: %u\n", ifi->ifi_change);
attr_len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
// Iterate over following routing attributes (we do only care for the MAC address)
for (attr = IFLA_RTA(ifi); RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) {
if (attr->rta_type == IFLA_ADDRESS) {
char name[IFNAMSIZ];
char buf[64];
unsigned char *ptr = (unsigned char *) RTA_DATA(attr);
snprintf(buf, 64, " %02x:%02x:%02x:%02x:%02x:%02x",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
// Convert the interface index back to the name, we expect the value defined in INTERFACE_NAME!
printf("%s hs the MAC address: %s\n",if_indextoname(ifi->ifi_index, name), buf);
}
}
}
}
return EXIT_SUCCESS;
}
只需将INTERFACE_NAME更改为您需要的内容,其余的就可以使用。
请注意,该代码不会等待NLMSG_DONE,因为我们不希望收到多部分消息。如果应用的过滤器可能与多个接口匹配,则需要循环读取套接字并检查NLMSG_DONE消息类型。