C 是否有一种使用netlink查询特定接口的方法?

C 是否有一种使用netlink查询特定接口的方法?,c,linux,network-programming,netlink,C,Linux,Network Programming,Netlink,据我所知,发送RTM\u GETLINK请求会转储系统上的所有接口。我只对特定的界面感兴趣。有没有一种方法可以设置我的请求,以向我提供有关特定接口的所有信息?我知道我感兴趣的接口的名称 我知道这是一个老问题,但网上关于如何使用netlink归档内核过滤的信息,至少是我发现的信息,并没有人们希望的那么好 这是一个很有前途的解释主题和可能的过滤器,我没有测试所有的,所以没有任何保证。不幸的是,它缺少一些更实用的代码示例 我找到了更多的信息,尽管这让我感到困惑,因为这个消息源谈论的是NETLINK\u

据我所知,发送
RTM\u GETLINK
请求会转储系统上的所有接口。我只对特定的界面感兴趣。有没有一种方法可以设置我的请求,以向我提供有关特定接口的所有信息?我知道我感兴趣的接口的名称

我知道这是一个老问题,但网上关于如何使用netlink归档内核过滤的信息,至少是我发现的信息,并没有人们希望的那么好

这是一个很有前途的解释主题和可能的过滤器,我没有测试所有的,所以没有任何保证。不幸的是,它缺少一些更实用的代码示例

我找到了更多的信息,尽管这让我感到困惑,因为这个消息源谈论的是NETLINK\u DUMP\u STRICT\u CHK套接字标志,我在NETLINK头中找不到它。似乎他们在内核4.20中将其更改为NETLINK\u GET\u STRICT\u CHK

无论如何,在进行了一点研究并浏览了一些命令之后,我发现了如何应用内核过滤器(至少对于RTM_GETLINK和RTM_GETADDR,但另一个应该以同样的方式工作)

查询一个接口的链接信息的示例代码(需要在接口名称中指定!):

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义接口名称“wlp59s0”
int main(int argc,字符**argv){
//我们不指定nl_组,因为我们不想成为多内核系统的一部分
结构sockaddr_nl src_addr={AF_NETLINK,0,(u u32)getpid(),0};
结构sockaddr_nl dest_addr={AF_NETLINK};
int optval=1;
结构{
结构nlmsghdr nh;
结构IFINFOMG ifi;
}请求={};
字符缓冲区[4096];
结构iovec iov={};
结构msghdr msg={};
结构nlmsghdr*nh;
结构IFINFOMG*ifi;
结构rtattr*attr;
西泽·特伦;
//打开netlink套接字
int request_fd=套接字(AF_网络链路、SOCK_原始、网络链路_路由);
如果(请求_fd==-1){
fprintf(stderr,“Netlink套接字创建失败:%s\n”,strerror(errno));
返回退出失败;
}
//启用内核筛选
if(setsockopt(request_fd,SOL_NETLINK,NETLINK_GET_STRICT_CHK,&optval,sizeof(optval))<0){
fprintf(标准,“Netlink设置套接字选项\”Netlink_GET_STRICT_CHK \“失败:%s\n”,strerror(errno));
返回退出失败;
}
//使用src_addr中指定的选项绑定文件描述符
if(bind(request_fd,(struct sockaddr*)&src_addr,sizeof(src_addr))<0){
fprintf(stderr,“Netlink套接字绑定失败:%s\n”,strerror(errno));
返回退出失败;
}
//准备内核的地址请求
//消息长度
request.nh.nlmsg_len=nlmsg_LENGTH(sizeof(request.ifi));
//我们对链接信息感兴趣
request.nh.nlmsg_type=RTM_GETLINK;
//请求和转储筛选标志
request.nh.nlmsg_flags=NLM_F_request | NLM_F_DUMP_FILTERED;
//没有指定地址族
request.ifi.ifi_family=AF_NETLINK;
//我们传递给netlink的筛选器选项。可能有更多筛选器。。。
request.ifi.ifi_index=(int)if_NAME索引(INTERFACE_NAME);
//将请求放入iovec中
iov.iov_base=&request;
iov.iov_len=request.nh.nlmsg_len;
//在消息中放置iovec结构
msg.msg_name=&dest_addr;
msg.msg_namelen=sizeof(dest_addr);
msg.msg_iov=&iov;
msg.msg_iovlen=1;
//发送请求消息
if(sendmsg(请求_fd,(结构msghdr*)&msg,0)<0){
fprintf(stderr,“向netlink发送接口请求时出错:%s\n”,strerror(errno));
返回退出失败;
}
//为响应准备iovec
memset(&iov,0,sizeof(iov));
iov.iov_base=缓冲区;
iov.iov_len=sizeof(缓冲区);
//从netlink接收响应
n=recvmsg(请求\u fd和消息,0);
if(n<0){
fprintf(stderr,“从网络链接接收消息时出错:%s\n”,strerror(errno));
返回退出失败;
}
//在消息中包含的netlink头上循环(应该只有一个,因为我们请求了一个特定接口)
对于(nh=(struct nlmsghdr*)缓冲区;NLMSG_OK(nh,n);nh=NLMSG_NEXT(nh,n)){
//我们正在寻找的响应类型
如果(nh->nlmsg_type==RTM_NEWLINK){
//获取并打印ifinfomsg结构
ifi=(结构ifinfomsg*)NLMSG_数据(nh);
printf(“系列:%u\n”,ifi->ifi\u系列);
printf(“设备类型:%u\n”,ifi->ifi\u类型);
printf(“索引:%u\n”,ifi->ifi\u索引);
printf(“标志:%u\n”,ifi->ifi\u标志);
printf(“更改掩码:%u\n”,ifi->ifi\u更改);
attr_len=nh->nlmsg_len-nlmsg_长度(sizeof(*ifi));
//迭代以下路由属性(我们只关心MAC地址)
对于(attr=IFLA\u RTA(ifi);RTA\u OK(attr,attr\u len);attr=RTA\u NEXT(attr,attr\u len)){
如果(属性->rta\U类型==IFLA\U地址){
字符名[IFNAMSIZ];
char-buf[64];
无符号字符*ptr=(无符号字符*)RTA_数据(attr);
snprintf(buf,64,“%02x:%02x:%02x:%02x:%02x:%02x:%02x”,
ptr[0]、ptr[1]、ptr[2]、ptr[3]、ptr[4]、ptr[5]);
//将接口索引转换回名称,我们期望接口名称中定义的值!
printf(“%s hs MAC地址:%s\n”,如果索引名(ifi->ifi\U索引,名称),则为buf);
}
}
}
}
返回退出成功;
}
只需将INTERFACE_NAME更改为您需要的任何名称,其余的就可以了

请注意,代码不会等待NLMSG_完成,因为我们不希望出现多部分消息。如果应用的筛选器可能匹配多个接口,则需要读取循环中的套接字并检查NLMSG_
#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;
}