Есть ли способ запросить определенный интерфейс с помощью netlink?

Насколько я понимаю, отправка запроса RTM_GETLINK сбрасывает весь интерфейс в системе. Меня интересует только конкретный интерфейс. Есть ли способ, которым я могу настроить свой запрос, чтобы предоставить мне всю информацию о конкретном интерфейсе? Я знаю название интересующего меня интерфейса.


person Denil Vira    schedule 06.04.2017    source источник
comment
Сначала скажи нам имя.   -  person Manav Dubey    schedule 07.04.2017
comment
Это может быть сетевая карта, такая как eth0, или интерфейс vlan, например. эт0.100.   -  person Denil Vira    schedule 07.04.2017


Ответы (1)


Я знаю, что это старый вопрос, но информация в Интернете, по крайней мере, та, что я нашел, о том, как архивировать фильтрацию ядра с помощью netlink, не так хороша, как хотелось бы.

Вот многообещающая статья объясняя тему и возможные фильтры, я не проверял их все, так что никаких гарантий нет. К сожалению, в коде отсутствуют еще несколько практических примеров...

Я нашел больше информации здесь, хотя меня смутило то, что этот источник говорит о флаге сокета NETLINK_DUMP_STRICT_CHK, который я не смог найти в своих заголовках netlink. Кажется, они изменили его на NETLINK_GET_STRICT_CHK в ядре 4.20.

В любом случае, после небольшого исследования и отслеживания некоторых команд 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.

person teawolf    schedule 20.06.2020