Я пытаюсь программно отправлять и получать пинг-пакеты ICMPv6 с компьютера с Windows 7.Код, который я использую, адаптирован из существующего кода, который успешно используется для отправки / получения пакетов проверки связи IPv4.Единственное отличие, которое я вижу, состоит в том, что я использую IPv6 вместо IPv4, и что я использую локальные адреса связи для адресов источника и назначения.
Адрес назначения, на который я пингуюсь, - это fe80 :: b617:80ff: fe40: fe21% 12, где% 12 выбирает соответствующий интерфейс.Запуск ipconfig показывает несколько сетевых адаптеров на моей машине:
Ethernet adapter Local Area Connection:
Connection-specific DNS Suffix . : dti.lan
Link-local IPv6 Address . . . . . : fe80::49d5:a4a1:1d10:7e42%11
IPv4 Address. . . . . . . . . . . : 192.168.0.71
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.0.1
Ethernet adapter ARM-dev-board-10.x.x.x:
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::e540:1d52:7bf7:3e4%12
IPv4 Address. . . . . . . . . . . : 10.86.11.123
Subnet Mask . . . . . . . . . . . : 255.0.0.0
IPv4 Address. . . . . . . . . . . : 172.16.17.6
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 172.16.17.1
, и я использую идентификатор области% 12, чтобы выбрать fe80 :: e540: 1d52: 7bf7: 3e4% 12 локальный адрес ссылки на ARM-Адаптер dev-board-10.xxx.
Я использовал wireshark для мониторинга сетевых пакетов, и я вижу, что мой код правильно отправляет запрос проверки связи, а цель отправляет ответ проверки связи.Проблема в том, что мой код никогда не получает ответный пакет.Вызов recv () истекает (возвращает SOCKET_ERROR, а WSAGetLastError возвращает 10600).
Есть ли какая-то опция волшебного сокета, которую мне нужно установить, чтобы заставить Windows передавать мне пакеты ответа?
Я пытался добавить вызов bind () перед sendto (), но это не имело никакого значения (я не думаю, что мне нужно вызывать bind (), так как sendto () неявно связывает интерфейс для меня, я думаю).
Я вызываю следующий код с address = "fe80::b617:80ff:fe40:fe21%12"
и timeoutInMs = 1000
bool Ping6Internal(const char* address, const int timeoutInMs)
{
bool result = false;
int timeout = timeoutInMs;
// get the destination address
struct addrinfo* addrInfo;
struct addrinfo hints = { 0 };
struct sockaddr_in6 dstAddr = { 0 };
// We only care about IPV6 results
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
int errcode = getaddrinfo(address, nullptr, &hints, &addrInfo);
if (errcode != 0)
{
perror("[ERROR] getaddrinfo ");
}
for (auto p = addrInfo; p; p = p->ai_next)
{
// Check to make sure we have a valid AF_INET6 address
if (p->ai_family == AF_INET6)
{
// Use memcpy since we're going to free the addrInfo variable
int foo = sizeof(sockaddr_in6);
memcpy(&dstAddr, p->ai_addr, p->ai_addrlen);
dstAddr.sin6_family = AF_INET6;
break;
}
}
freeaddrinfo(addrInfo);
int sockRaw = WSASocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, nullptr, 0, WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)
{
throw std::runtime_error("WSASocket failed");
}
int rv = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
if (rv == SOCKET_ERROR)
{
closesocket(sockRaw);
throw std::runtime_error("setsockopt SO_RCVTIMEO failed");
}
rv = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
if (rv == SOCKET_ERROR)
{
closesocket(sockRaw);
throw std::runtime_error("setsockopt SO_SNDTIMEO failed");
}
// Find out which local interface will be used when sending to this destination
DWORD bytes;
sockaddr_in6 srcAddr;
rv = WSAIoctl(sockRaw, SIO_ROUTING_INTERFACE_QUERY, &dstAddr, sizeof(dstAddr),
(SOCKADDR *)&srcAddr, sizeof(srcAddr), &bytes, nullptr, nullptr);
if (rv == SOCKET_ERROR)
{
closesocket(sockRaw);
throw std::runtime_error("could not determine which interface to use");
}
string localAddress = FormatAddress((SOCKADDR*)&srcAddr, sizeof(srcAddr));
#if 0
rv = bind(sockRaw, reinterpret_cast<struct sockaddr*>(&srcAddr), sizeof(srcAddr));
if (rv == SOCKET_ERROR)
{
int errCode = WSAGetLastError();
string errMsg = GetErrorString(errCode);
}
#endif
std::vector<char> icmpPacket(MAX_PACKET_SIZE);
IcmpHeader* pHeaderTx = reinterpret_cast<IcmpHeader*>(icmpPacket.data());
pHeaderTx->type = ICMPV6_ECHO;
pHeaderTx->code = 0;
pHeaderTx->checksum = 0;
pHeaderTx->id = static_cast<uint16_t>(GetCurrentProcessId());
pHeaderTx->seqNum = 0;
pHeaderTx->timestamp = GetTickCount();
// the upper 32 bits is the process id and the lower 32 bits is an incrementing counter
pHeaderTx->uniqueId = (uint64_t(GetCurrentProcessId()) << 32) | g_threadData.GetNextCounter();
const int headerSize = sizeof(IcmpHeader);
const int packetSize = headerSize + DEF_PACKET_SIZE;
std::fill(icmpPacket.data() + headerSize, icmpPacket.data() + packetSize, 'E');
// Calculate the packet checksum.
// The checksum is calculated over the IPv6 pseudo header plus the real packet.
IpV6PseudoHeader pseudoHeader = { 0 };
pseudoHeader.srcAddress = srcAddr.sin6_addr;
pseudoHeader.dstAddress = dstAddr.sin6_addr;
pseudoHeader.length = htonl(sizeof(IcmpHeader));
pseudoHeader.nextHeader = IPPROTO_ICMPV6;
unsigned long sum = 0;
const uint16_t* hdrU16 = reinterpret_cast<uint16_t*>(&pseudoHeader);
for (int n = 0; n < sizeof(IpV6PseudoHeader) / 2; n++)
{
sum += hdrU16[n];
}
const uint16_t* dataU16 = reinterpret_cast<uint16_t*>(icmpPacket.data());
for (int n = 0; n < packetSize / 2; n++)
{
sum += dataU16[n];
}
if (packetSize % 2) // odd number of bytes so grab the final byte
{
sum += icmpPacket[packetSize - 1];
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >>16);
pHeaderTx->checksum = static_cast<uint16_t>(~sum);
rv = sendto(sockRaw, icmpPacket.data(), packetSize, 0, (struct sockaddr*)&dstAddr, sizeof(dstAddr));
if (rv != SOCKET_ERROR)
{
char recvBuf[MAX_PACKET_SIZE];
// struct sockaddr_in6 from = { 0 };
// sockaddr_storage from;
auto now = boost::chrono::system_clock::now();
const auto timeExpired = now + boost::chrono::milliseconds(timeoutInMs);
do
{
// int fromSize = sizeof(from);
// const int rv = recvfrom(sockRaw, recvBuf, MAX_PACKET_SIZE, 0, (struct sockaddr*) &from, &fromSize);
const int rv = recv(sockRaw, recvBuf, MAX_PACKET_SIZE, 0);
if (rv == SOCKET_ERROR)
{
int errCode = WSAGetLastError();
string errMsg = GetErrorString(errCode);
break;
}
IcmpHeader* pHeaderRx = reinterpret_cast<IcmpHeader*>(recvBuf + IP_HEADER_SIZE);
if (pHeaderRx->uniqueId == pHeaderTx->uniqueId)
{
result = true;
break;
}
now = boost::chrono::system_clock::now();
} while (now < timeExpired);
}
closesocket(sockRaw);
return result;
}
Отредактировано 10 апреля 2019 г. для добавления:
Просмотр в средстве просмотра событий Windowsна событиях с идентификатором 5152 в разделе безопасности я вижу следующее:
The Windows Filtering Platform has blocked a packet.
Application Information:
Process ID: 4
Application Name: System
Network Information:
Direction: Inbound
Source Address: fe80::b617:80ff:fe40:fe21
Source Port: 0
Destination Address: fe80::e540:1d52:7bf7:3e4
Destination Port: 129
Protocol: 58
Filter Information:
Filter Run-Time ID: 648218
Layer Name: Receive/Accept
Layer Run-Time ID: 46
, который выглядит так, как будто брандмауэр Windows заблокировал пинг-ответ.После выключения брандмауэра (и временного отключения конечной точки Sophos) я больше не вижу событий ID 5152, показывающих, что пакет был отфильтрован.Но моя программа все еще не получает пакет ответа: - (