получение MAC-адреса системы: eth0 против ПредсказуемыеИменаСетевыхИнтерфейсов

Вопрос или проблема

Я написал некоторый код на 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-адреса на системе.

  1. Считывание из файловой системы: Самый простой и надежный способ — это считывать MAC-адреса из файловой системы /sys/class/net/. Каждый сетевой интерфейс имеет папку, где находится файл address, содержащий MAC-адрес, и файл uevent, который содержит информацию об интерфейсе.

  2. Использование 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-адреса и выводит их вместе с именами интерфейсов.

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

Оцените материал
Добавить комментарий

Капча загружается...