Как получить MAC-адрес моего локального сетевого интерфейса с помощью сокета Win или удаленного IP-адреса

Есть ли способ получить MAC-адрес (физический адрес) моего сетевого интерфейса, через который проходит соединение сокета, имея win сокет и удаленный ip. То есть у нас есть SOCKET, через который мы подключаемся к удаленному серверу, и нам нужно узнать, к какому сетевому интерфейсу подключена моя локальная машина и какой у нее MAC. Не удаленный MAC.

В ситуации, когда у нас 2 и более сетевых интерфейса, мы не можем достоверно понять, что соединение было осуществлено с использованием основного адаптера.

У нас есть ifreq и <arpa/inet.h> в Linux — Поиск MAC-адреса по IP-адресу Но я не смог найти аналогичного решения под Windows. Где взять arpa/inet.h?

У нас есть https://learn.microsoft.com/ru-ru/windows/win32/api/winsock2/nf-winsock2-getsockopt с SOL_SOCKET с SO_PROTOCOL_INFO. Но это не так.

У нас есть IPX_ADDRESS, но мы используем TCP/IP

ГОТОВОЕ решение:

std::vector<int> find_mac_by_in_ip(const std::string& in_ip); // function body is below

int main()
{
    SOCKET pRSocket = some_socket;

    sockaddr_storage optVal;
    int optLen = sizeof(sockaddr_storage);
    std::string interface_ip;

    if (getsockname(pRSocket, (struct sockaddr*)&optVal, &optLen) != SOCKET_ERROR) {
        std::cout << "\n---\n";

        char in_ip[INET6_ADDRSTRLEN]; 

        if (optVal.ss_family == AF_INET) {
            sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(&optVal);
            inet_ntop(AF_INET, &(addr_in->sin_addr), in_ip, INET6_ADDRSTRLEN);
        }
        else if (optVal.ss_family == AF_INET6) {
            sockaddr_in6* addr_in6 = reinterpret_cast<sockaddr_in6*>(&optVal);
            inet_ntop(AF_INET6, &(addr_in6->sin6_addr), in_ip, INET6_ADDRSTRLEN);
        }
        else {
            // TODO
        }
        interface_ip = in_ip;

        std::cout << in_ip << std::endl;
        std::cout << "\n";

    }
    else {
        std::cerr << "\n Error socket Connect() " << WSAGetLastError() << "\n";
    }

    if (interface_ip == "::1")
    {
    }
    else if (optVal.ss_family == AF_INET6)
    {
    }
    else if (optVal.ss_family == AF_INET)
    {
        auto res = find_mac_by_in_ip(interface_ip);
        for (auto i : res)
        {
            std::cout << std::hex << i << " "; // we have mac 
        }
    }
    return 0;
}

std::vector<int> find_mac_by_in_ip(const std::string& in_ip)
{
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter = NULL;
    DWORD dwRetVal = 0;
    UINT i;

    /* variables used to print DHCP time info */
    struct tm newtime;
    char buffer[32];
    errno_t error;

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
    if (pAdapterInfo == NULL) {
        printf("Error allocating memory needed to call GetAdaptersinfo\n");
        return {};
    }
    // Make an initial call to GetAdaptersInfo to get
    // the necessary size into the ulOutBufLen variable
    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
        if (pAdapterInfo == NULL) {
            printf("Error allocating memory needed to call GetAdaptersinfo\n");
            return {};
        }
    }

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        pAdapter = pAdapterInfo;
        while (pAdapter) {
            if (in_ip == pAdapter->IpAddressList.IpAddress.String)
            {
                std::vector<int> ret_mac;
                ret_mac.reserve(6);
                for (i = 0; i < pAdapter->AddressLength; i++) {
                        ret_mac.push_back((int)pAdapter->Address[i]);
                }
                if (pAdapterInfo)
                    free(pAdapterInfo);
                return ret_mac;
            }

            pAdapter = pAdapter->Next;
            // printf("\n");
        }
    }
    else {
        printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);

    }
    if (pAdapterInfo)
        free(pAdapterInfo);
    return {};
}

Предоставьте достаточно кода, чтобы другие могли лучше понять или воспроизвести проблему.

Superlokkus 04.07.2024 16:00
GetAdaptersAddresses для запроса информации о сетевой карте. Используйте поле PhysicalAddress в структуре.
Mestkon 04.07.2024 16:01

@Mestkon В ситуации, когда у нас есть 2 или более сетевых интерфейса, мы не можем достоверно понять, что соединение было выполнено с использованием основного адаптера.

Akri 04.07.2024 16:13

@Superlokkus Дело в том, что у меня нет кода, который мог бы это сделать.

Akri 04.07.2024 16:16

Как вы определяете «основной адаптер»? Это маршрут через 0.0.0.0/8 проложен? или, может быть, VPN вашей компании? Можете ли вы выполнить обычный трюк: открыть сокет UDP (без отправки каких-либо данных), запросить локальный IP-адрес и определить оттуда MAC-адрес?

Botje 04.07.2024 16:21

@Botje Основной адаптер — это основной сетевой интерфейс моего компьютера. И как мы можем «оттуда» идентифицировать локальный Mac, если наш Mac видит только ближайший маршрутизатор/шлюз?

Akri 04.07.2024 16:26

@Akri У вашего компьютера нет «основного сетевого интерфейса». GetAdaptersAddresses дает вам информацию обо ВСЕХ адаптерах, доступных на вашем компьютере.

Mestkon 04.07.2024 16:27

@Akri Этот вопрос становится очень запутанным, если учесть VPN. Ваш «основной сетевой интерфейс» по-прежнему может быть проводным/Wi-Fi-адаптером, но интерфейсом, который фактически используется для представления VPN, будет именно VPN-адаптер. С поддельным MAC-адресом.

Botje 04.07.2024 16:31

@Botje для меня главное получить этот MAC, так как в работающей системе сетевое окружение не должно меняться. И вопрос, какой MAC будет взят при включенном VPN, очень интересен. Я постараюсь узнать

Akri 04.07.2024 16:42

А еще есть еще более забавные вещи, такие как разделенная маршрутизация, когда ваш VPN-адаптер может использовать только себя по умолчанию для сетевого пространства компании (например, 10/8), но позволять другим соединениям проходить через реальный сетевой интерфейс. Скажу сразу: «сетевой интерфейс по умолчанию» — очень запутанная концепция, которая действительно зависит от того, чего вы пытаетесь достичь.

Botje 04.07.2024 16:58

@Botje На самом деле мне нужен тот сетевой интерфейс, через который мы куда-то подключаемся

Akri 04.07.2024 17:13

есть ли у этого «где-то» имя или IP-адрес? Если да, см. трюк с сокетом UDP выше.

Botje 04.07.2024 17:14

Единственный раз, когда мне понадобился MAC-адрес, это выяснить, у какого физического соединения возникли проблемы с подключением. (Оказалось, что кабель был неисправен, а сетевой адаптер был неисправен.)

Eljay 04.07.2024 18:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
13
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если у вас есть подключенный (или привязанный) сокет, вы можете вызвать getsockname() , чтобы узнать локальный IP-адрес сокета.

Если у вас есть локальный IP-адрес сокета, все, что вам нужно сделать, это выяснить, какой локальный сетевой адаптер связан с этим IP-адресом, а затем узнать MAC-адрес, связанный с этим локальным сетевым адаптером.

Как упомянул Месткон в своем комментарии, вы можете вызвать GetAdaptersAddresses(), чтобы получить информацию о локальных сетевых картах, тогда вам просто нужно просмотреть возвращенные структуры, чтобы найти конкретные значения, которые вас интересуют; например для MAC-адреса, который вам нужен, поле PhysicalAddressPIP_ADAPTER_ADDRESSES, связанное с вашим IP-адресом.

Если вы хотите увидеть пример кода, который делает подобные вещи, взгляните на строки 1993–2092 этого файла (который я написал как часть своей сетевой библиотеки); в частности, обратите внимание на то, как локальная переменная mac установлена ​​в строке 2067 и содержит MAC-адрес локального сетевого адаптера.

Спасибо большое <3. Код опубликую чуть позже

Akri 04.07.2024 19:04

Другие вопросы по теме

Как прочитать необработанный диск без блокировки/размонтирования тома, используя Win32 API?
Информация о растровом изображении регулярно возвращает серию одних и тех же неправильных значений, прежде чем возвращать правильные цвета пикселей
Запуск приложения с правами обычного пользователя из экземпляра с правами администратора
Как сохранить вывод сценария Python в действиях GitHub, запущенных в последней версии Windows?
Как перечислить потоки с помощью типов CreateToolhelp32Snapshot и Python?
Как связать приложения Rusqlite с пользовательскими сборками SQLite в Windows?
Какой API возвращает сообщение для NTSTATUS?
«Запуск с PowerShell» выдает ошибку политики выполнения, но запуск непосредственно в окне PowerShell не дает
Могу ли я держать ручку открытой в течение всего срока службы программы?
Регулярное выражение в findstr для поиска URL-адресов в текстовых файлах во всех подпапках