Определите интерфейс виртуальной сети в 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;
}