如何在centos中的c中从IP获取以太网适配器名称
how to get Ethernet adapter name from IP in c in centos
有两个以太网适配器,所以我有两个不同的 IP 地址。现在我想用相应的 ip 找到适配器的名称。比如,我有 IP 为 192.168.10.1 的英特尔卡。如何在没有任何第三方安装的情况下使用 C 或 C++ 在 centos(linux) 中检索此适配器名称?
我需要找到制造商名称(不是 eth0 等)。该制造商列表位于“/usr/share/hwdata/pci.ids”中,但我无法将该名称与 IP 地址对应起来。我可以使用 'lscpu | grep "Ethernet"' 获取适配器名称列表。但是问题又出现了,用 ip 地址映射名称。
我假设适配器名称是指 eth0/eth1/etc。而不是 Manufacturer/Model。如果是这样,一种可能的解决方案(有点复杂但有效)是执行 ifconfig 系统调用并将其通过管道传输到文本文件。从那里您可以执行文本文件搜索以查找 IP 地址,然后从那里因为输出是恒定的您可以只使用 IP 的起始位置作为获取适配器名称的基础。
标准libc中有getifaddrs
函数。我修改了手册页中的示例。
您无法从内核获取名称,但它在 /sys
文件系统中提供了 PCI ID。您可以使用 libpci
将这些数字解析为文件名。当前代码不支持 USB 设备和子设备编号。
#define _GNU_SOURCE /* To get defns of NI_MAXSERV and NI_MAXHOST */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_link.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <pci/pci.h>
/* PCI IDs are contained in /sys filesystem. */
unsigned long read_sysfs_uint(const char* ifa_name, const char* info) {
char path[PATH_MAX];
char buf[12];
int fd;
snprintf(path, PATH_MAX, "/sys/class/net/%s/device/%s", ifa_name, info);
fd = open(path, O_RDONLY);
if(fd == -1)
return 0;
if(read(fd, buf, 12) == -1) {
close(fd);
return 0;
}
close(fd);
return strtoul(buf, NULL, 16);
}
/* Try to get PCI IDs and get PCI device name for it.
XXX: doesn't check for subsystem's numbers */
void print_pci_ids(const char* ifa_name) {
int vendor = (int) read_sysfs_uint(ifa_name, "vendor");
int device = (int) read_sysfs_uint(ifa_name, "device");
int subsystem_vendor = (int) read_sysfs_uint(ifa_name, "subsystem_vendor");
int subsystem_device = (int) read_sysfs_uint(ifa_name, "subsystem_device");
struct pci_access *pacc = pci_alloc();
char namebuf[256];
printf("PCI IDs: %x %x %x %x\n", vendor, device, subsystem_device, subsystem_vendor);
pci_init(pacc);
if(pci_lookup_name(pacc, namebuf, 256,
PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
vendor, device)) {
printf("PCI Name: %s\n", namebuf);
}
pci_cleanup(pacc);
}
int main(int argc, char *argv[])
{
struct ifaddrs *ifaddr, *ifa;
struct in_addr* ifa_inaddr;
struct in_addr addr;
int family, s, n;
if(argc != 2) {
fprintf(stderr, "Usage: getifaddr <IP>\n");
return EXIT_FAILURE;
}
if (inet_aton(argv[1], &addr) == 0) {
perror("inet_aton");
return EXIT_FAILURE;
}
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
return EXIT_FAILURE;
}
/* Walk through linked list, maintaining head pointer so we
can free list later */
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (ifa->ifa_addr == NULL)
continue;
/* We seek only for IPv4 addresses */
if(ifa->ifa_addr->sa_family != AF_INET)
continue;
ifa_inaddr = &(((struct sockaddr_in*) ifa->ifa_addr)->sin_addr);
if(memcmp(ifa_inaddr, &addr, sizeof(struct in_addr)) == 0) {
printf("Interface: %s\n", ifa->ifa_name);
print_pci_ids(ifa->ifa_name);
}
}
freeifaddrs(ifaddr);
return EXIT_SUCCESS;
}
用libpci
编译(需要安装相应的开发包):
$ gcc getifname.c -lpci -o ./getifname
以下是其用法示例:
$ ./getifname
Usage: getifaddr <IP>
$ ./getifname dlks
inet_aton: Success
$ ./getifname 127.0.0.1
Interface: lo
PCI IDs: 0 0 0 0
PCI Name: Device 0000:0000
$ ./getifname 192.168.13.144
Interface: wlan0
PCI IDs: 8086 88e 4060 8086
PCI Name: Intel Corporation Centrino Advanced-N 6235
这实际上有点棘手,因为 linux 没有像 windows 那样的通用通用驱动程序堆栈 API - 基本上可以归结为 3 个选项:
- 读取内核导出的特殊文件:
- 调用
lspci
并解析其输出:http://prefetch.net/articles/linuxpci.html
- 复制
lspci
的功能并实际编写您自己的应用程序,如您所见,您将需要多个内核数据结构,例如 pcimap_entry
等等,但它应该是直截了当的,因为您可以从字面上理解虹吸旧内核大师的知识:https://github.com/gittup/pciutils/blob/gittup/ls-kernel.c
有两个以太网适配器,所以我有两个不同的 IP 地址。现在我想用相应的 ip 找到适配器的名称。比如,我有 IP 为 192.168.10.1 的英特尔卡。如何在没有任何第三方安装的情况下使用 C 或 C++ 在 centos(linux) 中检索此适配器名称?
我需要找到制造商名称(不是 eth0 等)。该制造商列表位于“/usr/share/hwdata/pci.ids”中,但我无法将该名称与 IP 地址对应起来。我可以使用 'lscpu | grep "Ethernet"' 获取适配器名称列表。但是问题又出现了,用 ip 地址映射名称。
我假设适配器名称是指 eth0/eth1/etc。而不是 Manufacturer/Model。如果是这样,一种可能的解决方案(有点复杂但有效)是执行 ifconfig 系统调用并将其通过管道传输到文本文件。从那里您可以执行文本文件搜索以查找 IP 地址,然后从那里因为输出是恒定的您可以只使用 IP 的起始位置作为获取适配器名称的基础。
标准libc中有getifaddrs
函数。我修改了手册页中的示例。
您无法从内核获取名称,但它在 /sys
文件系统中提供了 PCI ID。您可以使用 libpci
将这些数字解析为文件名。当前代码不支持 USB 设备和子设备编号。
#define _GNU_SOURCE /* To get defns of NI_MAXSERV and NI_MAXHOST */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_link.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <pci/pci.h>
/* PCI IDs are contained in /sys filesystem. */
unsigned long read_sysfs_uint(const char* ifa_name, const char* info) {
char path[PATH_MAX];
char buf[12];
int fd;
snprintf(path, PATH_MAX, "/sys/class/net/%s/device/%s", ifa_name, info);
fd = open(path, O_RDONLY);
if(fd == -1)
return 0;
if(read(fd, buf, 12) == -1) {
close(fd);
return 0;
}
close(fd);
return strtoul(buf, NULL, 16);
}
/* Try to get PCI IDs and get PCI device name for it.
XXX: doesn't check for subsystem's numbers */
void print_pci_ids(const char* ifa_name) {
int vendor = (int) read_sysfs_uint(ifa_name, "vendor");
int device = (int) read_sysfs_uint(ifa_name, "device");
int subsystem_vendor = (int) read_sysfs_uint(ifa_name, "subsystem_vendor");
int subsystem_device = (int) read_sysfs_uint(ifa_name, "subsystem_device");
struct pci_access *pacc = pci_alloc();
char namebuf[256];
printf("PCI IDs: %x %x %x %x\n", vendor, device, subsystem_device, subsystem_vendor);
pci_init(pacc);
if(pci_lookup_name(pacc, namebuf, 256,
PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
vendor, device)) {
printf("PCI Name: %s\n", namebuf);
}
pci_cleanup(pacc);
}
int main(int argc, char *argv[])
{
struct ifaddrs *ifaddr, *ifa;
struct in_addr* ifa_inaddr;
struct in_addr addr;
int family, s, n;
if(argc != 2) {
fprintf(stderr, "Usage: getifaddr <IP>\n");
return EXIT_FAILURE;
}
if (inet_aton(argv[1], &addr) == 0) {
perror("inet_aton");
return EXIT_FAILURE;
}
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
return EXIT_FAILURE;
}
/* Walk through linked list, maintaining head pointer so we
can free list later */
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (ifa->ifa_addr == NULL)
continue;
/* We seek only for IPv4 addresses */
if(ifa->ifa_addr->sa_family != AF_INET)
continue;
ifa_inaddr = &(((struct sockaddr_in*) ifa->ifa_addr)->sin_addr);
if(memcmp(ifa_inaddr, &addr, sizeof(struct in_addr)) == 0) {
printf("Interface: %s\n", ifa->ifa_name);
print_pci_ids(ifa->ifa_name);
}
}
freeifaddrs(ifaddr);
return EXIT_SUCCESS;
}
用libpci
编译(需要安装相应的开发包):
$ gcc getifname.c -lpci -o ./getifname
以下是其用法示例:
$ ./getifname
Usage: getifaddr <IP>
$ ./getifname dlks
inet_aton: Success
$ ./getifname 127.0.0.1
Interface: lo
PCI IDs: 0 0 0 0
PCI Name: Device 0000:0000
$ ./getifname 192.168.13.144
Interface: wlan0
PCI IDs: 8086 88e 4060 8086
PCI Name: Intel Corporation Centrino Advanced-N 6235
这实际上有点棘手,因为 linux 没有像 windows 那样的通用通用驱动程序堆栈 API - 基本上可以归结为 3 个选项:
- 读取内核导出的特殊文件:
- 调用
lspci
并解析其输出:http://prefetch.net/articles/linuxpci.html - 复制
lspci
的功能并实际编写您自己的应用程序,如您所见,您将需要多个内核数据结构,例如pcimap_entry
等等,但它应该是直截了当的,因为您可以从字面上理解虹吸旧内核大师的知识:https://github.com/gittup/pciutils/blob/gittup/ls-kernel.c