Вопрос или проблема
Я написал некоторый код на C, в котором есть элементарная функция лицензирования программного обеспечения, где исполняемый файл будет работать только если MAC-адрес системы, который я внес в черный список в своем коде на C. Я хочу, чтобы это работало только в Linux.
Я получал доступ к /sys/class/net/eth0/address
, который содержит что-то вроде a0:b1:c2:d3:e4:f5
всё в нижнем регистре в одной строке, что облегчает получение MAC-адреса. Это работает только если у вас есть eth0
, потому что я захардкодил fopen( "/sys/class/net/eth0/address", "r" );
Проблема, которую я осознаю, это eth0 часть. С учетом того, что BiosDevName и/или Предсказуемое именование сетевых интерфейсов теперь являются стандартом [в RHEL/CentOS 7], я вижу что-то вроде eno1
вместо eth0
как первого сетевого устройства с MAC-адресом. И это может сильно варьироваться от системы к системе. Так что я имею дело с /sys/class/net/<unknown>/address
.
Каков самый портативный и надежный, и не слишком сложный способ получить MAC-адреса системы Linux? Часто серверы, с которыми я работаю, имеют четырехпортовые сетевые карты Intel, но мне действительно нужен только первый. И мой код не интересуется, каковы имена сетевых интерфейсов, мне просто нужно знать один или несколько присутствующих MAC-адресов.
Если вам просто нужно получить MAC-адреса для системы и сопоставить их с интерфейсом, мы можем сделать это с помощью Bash и передать данные в программу на C. Если мы посмотрим в /sys/class/net/*/
, мы увидим, что имя интерфейса содержится в файле uevent
, в то время как MAC-адрес находится в файле address
; так что мы можем объединить эти два файла для каждого интерфейса. Примечание: мой bash не самый красивый, возможно, есть лучший способ сделать это.
[user@headdesk eth0]$ cat /sys/class/net/*/uevent /sys/class/net/*/address | grep -v IFINDEX | column | sed 's/INTERFACE=//'
eth0 fa:16:3e:ac:48:74
lo 00:00:00:00:00:00
Ответ или решение
Получение MAC-адреса системы: eth0 против Предсказуемых имен сетевых интерфейсов
Вопрос определения MAC-адреса для системы Linux может стать более сложным с учетом изменений в системе именования сетевых интерфейсов, таких как BiosDevName и Предсказуемые имена сетевых интерфейсов, которые введены в дистрибутивах RHEL/CentOS 7 и новее. На практике это означает, что сетевые интерфейсы больше не имеют фиксированных имен, таких как eth0
. Вместо этого, имена интерфейсов могут иметь вид, например, eno1
, что усложняет задачу, если ваш код жестко привязан к конкретному интерфейсу.
Решение проблемы
Чтобы сделать ваш код более универсальным и независимым от имен сетевых интерфейсов, можно использовать подход, который позволит вам динамически находить необходимые MAC-адреса на системе.
-
Считывание из файловой системы: Самый простой и надежный способ — это считывать MAC-адреса из файловой системы
/sys/class/net/
. Каждый сетевой интерфейс имеет папку, где находится файлaddress
, содержащий MAC-адрес, и файлuevent
, который содержит информацию об интерфейсе. -
Использование Bash для сбора информации: Как упомянуто, вы можете использовать Bash, чтобы извлечь информацию о MAC-адресах всех интерфейсов, а затем использовать полученные данные в вашем C-коде. Пример команды, которая выводит пары интерфейс-MAC:
for iface in /sys/class/net/*; do
mac=$(cat "$iface/address")
name=$(basename "$iface")
echo "$name: $mac"
done
Это скрипт перебирает все интерфейсы в каталоге /sys/class/net/
, считывает их MAC-адреса и выводит их вместе с именами интерфейсов.
- Код на C: Получив данные из скрипта, вы можете передавать их в ваш C-код. При этом устраните зависимость от фиксированного имени интерфейса и обеспечьте универсальность приложения.
Примеры кода на C
Если вы все же хотите пойти по пути получения MAC-адресов непосредственно в вашем C-коде, вам нужно будет читать файлы напрямую, как показано ниже:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
void get_mac_addresses() {
struct dirent *entry;
DIR *dir = opendir("/sys/class/net/");
if (dir == NULL) {
perror("opendir");
return;
}
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_LNK) { // Это значит, что это символическая ссылка
char iface_path[256];
snprintf(iface_path, sizeof(iface_path), "/sys/class/net/%s/address", entry->d_name);
FILE *file = fopen(iface_path, "r");
if (file) {
char mac[18];
if (fgets(mac, sizeof(mac), file) != NULL) {
// Удаление символа новой строки
mac[strcspn(mac, "\n")] = 0;
printf("%s: %s\n", entry->d_name, mac);
}
fclose(file);
}
}
}
closedir(dir);
}
int main() {
get_mac_addresses();
return 0;
}
В данном коде мы открываем директорию, которая содержит сетевые интерфейсы, считываем имя каждого интерфейса и получаем MAC-адрес из соответствующего файла.
Заключение
С переходом на предсказуемые имена интерфейсов, использование жестко закодированного значения eth0
становится неэффективным. Приведенные выше методы позволяют динамически извлекать MAC-адреса, независимо от имен интерфейсов, что делает ваш код более адаптивным и совместимым с различными системами Linux. Выбрав правильный подход, вы сможете избежать жесткой зависимости от определенного интерфейса и сделать ваше приложение более универсальным.