Определите интерфейс виртуальной сети в c

Я пытаюсь определить, на каком интерфейсе работает мое устройство с C. Я отсканировал все интерфейсы с помощью ioctl и выстроил результат следующим образом:

iface 0 ==> lo      IP = 127.0.0.1      FLAGS = 00000049    MAC: 00 00 00 00 00 00 
iface 1 ==> eth0    iface no IP         FLAGS = 00001043    MAC: 40 D6 3C 02 74 10 
iface 2 ==> eth0.1  iface no IP         FLAGS = 00001043    MAC: 40 D6 3C 02 74 10 
iface 3 ==> ra0     iface no IP         FLAGS = 00001043    MAC: 40 D6 3C 02 74 10 
iface 4 ==> br-lan  IP = 192.168.100.1  FLAGS = 00001043    MAC: 40 D6 3C 02 74 11 
iface 5 ==> apcli0  IP = 192.168.1.17   FLAGS = 00001043    MAC: 42 D6 3C 02 74 10 
iface 6 ==> mon0    iface no IP         FLAGS = 00001002    MAC: 40 D6 3C 02 74 10 

Я использовал getifaddrs (), чтобы получить список интерфейсов, затем ioctl (IP, использующий SIOCGIFADDR), (флаги, использующие SIOCGIFFLAGS), enum net_device_flags и (mac, использующий SIOCGIFHWADDR).

Из списка я могу определить петлевые, нерабочие интерфейсы, которые не имеют IP. У меня все еще есть два интерфейса, которые идентичны FLAGS и имеют IP (apcli0 & br-lan). Br-lan - это виртуальный интерфейс.

Можно ли определить, является ли интерфейс виртуальным (с C)?

Вот мой код:

    int i;
    int fd;
    int cnt = 0;
    struct ifaddrs *addrs,*tmp;
    char ibuf[256];
    struct ifreq ifr;

    fd = socket(AF_INET, SOCK_DGRAM, 0);

    getifaddrs(&addrs);

    tmp = addrs;

    while (tmp)
    {
        if ( (tmp->ifa_addr) && (tmp->ifa_addr->sa_family == AF_PACKET)) {
            printf("iface %i ==> %s\n", cnt, tmp->ifa_name);

            // get ip
            ifr.ifr_addr.sa_family = AF_INET;
            strncpy(ifr.ifr_name, tmp->ifa_name, IFNAMSIZ);

            if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
                printf("IP = %s\n", (char *)inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
            } else {
                printf("iface no IP\n");
            }

            // flags
            if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0) {
                printf("FLAGS = %08X\n", ifr.ifr_flags);
            } else {
                printf("iface no flags\n");

            }

            // mac addr
            if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
                memcpy(ibuf, ifr.ifr_hwaddr.sa_data, 6);
                printf("MAC: ");
                for (i=0; i<6; i++) {
                    printf("%02X ", ibuf[i] & 0xFF);
                }
                printf("\n");
            } else {
                printf("iface no mac\n");

            }

            cnt++;
        }
        tmp = tmp->ifa_next;
    }
    freeifaddrs(addrs);

Благодарю.

2 ответа

Я не верю, что это возможно с информацией, возвращенной getifaddrs()потому что нет SIOCGIFFLAGS которые указывают, что устройство является виртуальным. Вы можете найти некоторые виртуальные устройства, например, проверив IFF_LOOPBACK флаг в iface->ifa_flags (loopback - это виртуальный интерфейс), но в структуре нет информации, которая бы конкретно указывала, является ли устройство физическим или виртуальным.

С достаточно свежими ядрами Linux (не обязательно с начальным, но оно должно по крайней мере предоставлять sysfs), я считаю, что это можно определить, изучив /sys/class/net - каталоги здесь будут представлять интерфейсы, о которых сообщают ifconfig а также getifaddrs()и будет либо связана с устройством в /sys/devices/platform (физические устройства) или /sys/devices/virtual (виртуальные устройства). Я не знаю чистого способа сделать это в "обычном C" (с хорошими нормальными вызовами функций, а затем проверяя флаг на физический или виртуальный), но кажется возможным сделать это, изучив структуру каталогов в /sys,

редактировать: выглядит как устройства pci, которые являются физическими устройствами, также могут отображаться под /sys/devices/pci0000:00 или аналогичные, поэтому не все физические устройства будут в /sys/devices/physicalтак что лучше проверить, что /sys/class/net каталоги не связаны с /sys/devices/virtual каталоги...

Следующий фрагмент С++ основан на ответе @sinback.

      #include <iostream>
#include <filesystem>
#include <cstring>
#include <vector>

#include <limits.h>
#include <stdlib.h>
 
namespace fs = std::filesystem;
 
char buf[PATH_MAX];

int main()
{
    std::string path = "/sys/class/net";
    for (const auto & entry : fs::directory_iterator(path))
    {
        auto res = realpath(entry.path().c_str(), buf);

        if (res) {
            auto abs_path = fs::path(buf);
            auto abs_path_splits = std::vector<fs::path>(abs_path.begin(), abs_path.end());
            
            if (abs_path_splits[3].filename() == "virtual") {
                std::cout << "VIRTUAL ";
            } else {
                std::cout << "PHYSICAL ";
            }
            
            std::cout << "DEVICE " << entry.path() << " evaluates to " << buf << std::endl;
        } else {
            std::cerr << " Error (" << errno << "): " << strerror(errno) << std::endl;
            return EXIT_FAILURE;
        }
    }

    return 0;
}
Другие вопросы по тегам