Есть ли способ получить 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 {};
}
GetAdaptersAddresses
для запроса информации о сетевой карте. Используйте поле PhysicalAddress
в структуре.
@Mestkon В ситуации, когда у нас есть 2 или более сетевых интерфейса, мы не можем достоверно понять, что соединение было выполнено с использованием основного адаптера.
@Superlokkus Дело в том, что у меня нет кода, который мог бы это сделать.
Как вы определяете «основной адаптер»? Это маршрут через 0.0.0.0/8 проложен? или, может быть, VPN вашей компании? Можете ли вы выполнить обычный трюк: открыть сокет UDP (без отправки каких-либо данных), запросить локальный IP-адрес и определить оттуда MAC-адрес?
@Botje Основной адаптер — это основной сетевой интерфейс моего компьютера. И как мы можем «оттуда» идентифицировать локальный Mac, если наш Mac видит только ближайший маршрутизатор/шлюз?
@Akri У вашего компьютера нет «основного сетевого интерфейса». GetAdaptersAddresses
дает вам информацию обо ВСЕХ адаптерах, доступных на вашем компьютере.
@Akri Этот вопрос становится очень запутанным, если учесть VPN. Ваш «основной сетевой интерфейс» по-прежнему может быть проводным/Wi-Fi-адаптером, но интерфейсом, который фактически используется для представления VPN, будет именно VPN-адаптер. С поддельным MAC-адресом.
@Botje для меня главное получить этот MAC, так как в работающей системе сетевое окружение не должно меняться. И вопрос, какой MAC будет взят при включенном VPN, очень интересен. Я постараюсь узнать
А еще есть еще более забавные вещи, такие как разделенная маршрутизация, когда ваш VPN-адаптер может использовать только себя по умолчанию для сетевого пространства компании (например, 10/8), но позволять другим соединениям проходить через реальный сетевой интерфейс. Скажу сразу: «сетевой интерфейс по умолчанию» — очень запутанная концепция, которая действительно зависит от того, чего вы пытаетесь достичь.
@Botje На самом деле мне нужен тот сетевой интерфейс, через который мы куда-то подключаемся
есть ли у этого «где-то» имя или IP-адрес? Если да, см. трюк с сокетом UDP выше.
Единственный раз, когда мне понадобился MAC-адрес, это выяснить, у какого физического соединения возникли проблемы с подключением. (Оказалось, что кабель был неисправен, а сетевой адаптер был неисправен.)
Если у вас есть подключенный (или привязанный) сокет, вы можете вызвать getsockname() , чтобы узнать локальный IP-адрес сокета.
Если у вас есть локальный IP-адрес сокета, все, что вам нужно сделать, это выяснить, какой локальный сетевой адаптер связан с этим IP-адресом, а затем узнать MAC-адрес, связанный с этим локальным сетевым адаптером.
Как упомянул Месткон в своем комментарии, вы можете вызвать GetAdaptersAddresses(), чтобы получить информацию о локальных сетевых картах, тогда вам просто нужно просмотреть возвращенные структуры, чтобы найти конкретные значения, которые вас интересуют; например для MAC-адреса, который вам нужен, поле PhysicalAddress
PIP_ADAPTER_ADDRESSES
, связанное с вашим IP-адресом.
Если вы хотите увидеть пример кода, который делает подобные вещи, взгляните на строки 1993–2092 этого файла (который я написал как часть своей сетевой библиотеки); в частности, обратите внимание на то, как локальная переменная mac
установлена в строке 2067 и содержит MAC-адрес локального сетевого адаптера.
Спасибо большое <3. Код опубликую чуть позже
Предоставьте достаточно кода, чтобы другие могли лучше понять или воспроизвести проблему.