c++windowssocketsnetwork-programmingmac-address

How to get the MAC address of my local network interface by win socket or remote ip


Is there a way to get the MAC address (physical address) of my network interface, through which the socket connection passes, having win socket and remote ip. That is, we have a SOCKET through which we connect to a remote server, and we need to find out which network interface my local machine is connected to and which MAC it has. Not remote MAC.

In a situation where we have 2 or more network interfaces, we cannot reliably understand that the connection was made using the main adapter.

We have ifreq and <arpa/inet.h> on Linux - Finding MAC address from IP address But I couldn't find a similar solution under Windows Where do I get arpa/inet.h?

We have https://learn.microsoft.com/ru-ru/windows/win32/api/winsock2/nf-winsock2-getsockopt with SOL_SOCKET with SO_PROTOCOL_INFO. But it's not.

We have IPX_ADDRESS but we use TCP/IP

A READY solution:

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 {};
}

Solution

  • If you have a connected (or bound) socket, you can call getsockname() to find out the socket's local IP address.

    Once you have the socket's local IP address, all you need to do is figure out which local NIC is associated with that IP address, and then find out the MAC address associated with that local NIC.

    As Mestkon mentioned in his comment, you can call GetAdaptersAddresses() to obtain information about the local NICs, then it's just a matter of looking through the returned structs to find the specific values you are interested in; e.g. for the MAC address you want the PhysicalAddress field of the PIP_ADAPTER_ADDRESSES associated with your IP address.

    If you'd like to see some example code that does this sort of thing, take a look at lines 1993-2092 of this file (that I wrote as part of my networking library); in particular look at the way the local variable mac is set at line 2067, to contain the MAC address of a local NIC.