mirror of https://github.com/PCSX2/pcsx2.git
DEV9: Amend ICMP_Session comments
This commit is contained in:
parent
3c7cff99f4
commit
54782cbf70
|
@ -36,36 +36,38 @@ using namespace PacketReader::IP::ICMP;
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
/* Ping is kindof annoying to do crossplatform
|
/*
|
||||||
All platforms restrict raw sockets
|
* Ping is kindof annoying to do crossplatform
|
||||||
|
* All platforms restrict raw sockets
|
||||||
Windows provides an api for ICMP
|
*
|
||||||
ICMP_ECHO_REPLY should always be used, ignore ICMP_ECHO_REPLY32
|
* Windows provides an api for ICMP
|
||||||
IP_OPTION_INFORMATION should always be used, ignore IP_OPTION_INFORMATION32
|
* ICMP_ECHO_REPLY should always be used, ignore ICMP_ECHO_REPLY32
|
||||||
|
* IP_OPTION_INFORMATION should always be used, ignore IP_OPTION_INFORMATION32
|
||||||
Linux
|
*
|
||||||
We have access to raw sockets via CAP_NET_RAW (for pcap)
|
* Linux
|
||||||
However we may be missing that cap on some builds
|
* We have access to raw sockets via CAP_NET_RAW (for pcap)
|
||||||
Linux has socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP), used similar to raw sockets but for ICMP only
|
* However we may be missing that cap on some builds
|
||||||
Auto filters responses
|
* Also hava socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP), used similarly to raw sockets, but for ICMP only
|
||||||
Requires net.ipv4.ping_group_range sysctl, default off on a lot of distros
|
* Auto filters responses
|
||||||
Timeouts reported via sock_extended_err control messages (with IP_RECVERR socket option set)
|
* Requires net.ipv4.ping_group_range sysctl, default off on a lot of distros
|
||||||
|
* Timeouts reported via sock_extended_err control messages (with IP_RECVERR socket option set)
|
||||||
Mac
|
*
|
||||||
Raw sockets restricted
|
* Mac
|
||||||
Mac has socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)
|
* Raw sockets restricted
|
||||||
No restriction to using it
|
* Mac has socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)
|
||||||
Implementation differs, is more versatile than linux
|
* No restriction to using it with ICMP_ECHO
|
||||||
Does not auto filter responses
|
* Implementation differs, is more versatile than linux
|
||||||
Timeouts reported as a normal packet
|
* Does not auto filter responses
|
||||||
|
* Timeouts reported as a normal packet
|
||||||
FreeBSD
|
*
|
||||||
Raw sockets restricted
|
* FreeBSD
|
||||||
No unprivilaged ICMP sockets
|
* Raw sockets restricted
|
||||||
Timeouts reported as a normal packet??
|
* No unprivilaged ICMP sockets
|
||||||
|
* Timeouts reported as a normal packet??
|
||||||
Ping cli
|
*
|
||||||
Present for all platforms, but command args differ
|
* Ping cli
|
||||||
|
* Present for all platforms, but command args differ
|
||||||
|
* Not used here
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Sessions
|
namespace Sessions
|
||||||
|
@ -96,28 +98,34 @@ namespace Sessions
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Allocate return buffer
|
/*
|
||||||
//Documentation says + 8 to allow for an ICMP error message
|
* Allocate response buffer
|
||||||
//In testing, ICMP_ECHO_REPLY structure itself was returned with data set to null
|
* Documentation says + 8 to allow for an ICMP error message
|
||||||
|
* In testing, ICMP_ECHO_REPLY structure itself was returned with data set to null
|
||||||
|
*/
|
||||||
icmpResponseBufferLen = sizeof(ICMP_ECHO_REPLY) + requestSize + 8;
|
icmpResponseBufferLen = sizeof(ICMP_ECHO_REPLY) + requestSize + 8;
|
||||||
icmpResponseBuffer = std::make_unique<std::byte[]>(icmpResponseBufferLen);
|
icmpResponseBuffer = std::make_unique<std::byte[]>(icmpResponseBufferLen);
|
||||||
#elif defined(__POSIX__)
|
#elif defined(__POSIX__)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Allocate response buffer
|
||||||
|
* Size needed depends on which socket protocol (ICMP or raw) we use aswell as os
|
||||||
|
*/
|
||||||
switch (icmpConnectionKind)
|
switch (icmpConnectionKind)
|
||||||
{
|
{
|
||||||
//Two different methods for raw/icmp sockets between the unix OSes
|
// Two different methods for raw/icmp sockets between the Unix OSes
|
||||||
//Play it safe and only enable when we know which of the two methods we use
|
// Play it safe and only enable when we know which of the two methods we use
|
||||||
#if defined(ICMP_SOCKETS_LINUX) || defined(ICMP_SOCKETS_BSD)
|
#if defined(ICMP_SOCKETS_LINUX) || defined(ICMP_SOCKETS_BSD)
|
||||||
case (PingType::ICMP):
|
case (PingType::ICMP):
|
||||||
icmpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
icmpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||||
if (icmpSocket != -1)
|
if (icmpSocket != -1)
|
||||||
{
|
{
|
||||||
#if defined(ICMP_SOCKETS_LINUX)
|
#if defined(ICMP_SOCKETS_LINUX)
|
||||||
//Space for ICMP header, as MSG_ERRQUEUE returns what we sent
|
// Only need space for ICMP header, as MSG_ERRQUEUE returns data we sent
|
||||||
//An extra +8 required sometimes, for some reason?
|
// Testing found an extra + 8 was required sometimes, for some reason?
|
||||||
icmpResponseBufferLen = 8 + requestSize + 8;
|
icmpResponseBufferLen = 8 + requestSize + 8;
|
||||||
#elif defined(ICMP_SOCKETS_BSD)
|
#elif defined(ICMP_SOCKETS_BSD)
|
||||||
//Returned IP Header, ICMP Header & either data or failed ICMP packet
|
// Returned IP Header, ICMP Header & either data or failed ICMP packet
|
||||||
icmpResponseBufferLen = 20 + 8 + std::max(20 + 8, requestSize);
|
icmpResponseBufferLen = 20 + 8 + std::max(20 + 8, requestSize);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
@ -133,10 +141,10 @@ namespace Sessions
|
||||||
if (icmpSocket != -1)
|
if (icmpSocket != -1)
|
||||||
{
|
{
|
||||||
#if defined(ICMP_SOCKETS_LINUX)
|
#if defined(ICMP_SOCKETS_LINUX)
|
||||||
//We get packet + header
|
// We get IP packet + ICMP header
|
||||||
icmpResponseBufferLen = 20 + 8 + requestSize;
|
icmpResponseBufferLen = 20 + 8 + requestSize;
|
||||||
#elif defined(ICMP_SOCKETS_BSD)
|
#elif defined(ICMP_SOCKETS_BSD)
|
||||||
//As above, but we will also directly receive error ICMP messages
|
// As above, but we will also directly receive error ICMP messages
|
||||||
icmpResponseBufferLen = 20 + 8 + std::max(20 + 8, requestSize);
|
icmpResponseBufferLen = 20 + 8 + std::max(20 + 8, requestSize);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
@ -171,21 +179,21 @@ namespace Sessions
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note, we can finish reading but have no data
|
// Returned PingResult.data is only valid when PingResult.type is 0
|
||||||
ICMP_Session::PingResult* ICMP_Session::Ping::Recv()
|
ICMP_Session::PingResult* ICMP_Session::Ping::Recv()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (WaitForSingleObject(icmpEvent, 0) == WAIT_OBJECT_0)
|
if (WaitForSingleObject(icmpEvent, 0) == WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
ResetEvent(icmpEvent);
|
ResetEvent(icmpEvent);
|
||||||
//Prep buffer for reasing
|
|
||||||
[[maybe_unused]] int count = IcmpParseReplies(icmpResponseBuffer.get(), icmpResponseBufferLen);
|
[[maybe_unused]] int count = IcmpParseReplies(icmpResponseBuffer.get(), icmpResponseBufferLen);
|
||||||
pxAssert(count == 1);
|
pxAssert(count == 1);
|
||||||
|
|
||||||
// Rely on implicit object creation
|
// Rely on implicit object creation
|
||||||
ICMP_ECHO_REPLY* pingRet = reinterpret_cast<ICMP_ECHO_REPLY*>(icmpResponseBuffer.get());
|
ICMP_ECHO_REPLY* pingRet = reinterpret_cast<ICMP_ECHO_REPLY*>(icmpResponseBuffer.get());
|
||||||
|
|
||||||
//Map status to ICMP type/code
|
// Map status to ICMP type/code
|
||||||
switch (pingRet->Status)
|
switch (pingRet->Status)
|
||||||
{
|
{
|
||||||
case (IP_SUCCESS):
|
case (IP_SUCCESS):
|
||||||
|
@ -212,21 +220,23 @@ namespace Sessions
|
||||||
result.type = 3;
|
result.type = 3;
|
||||||
result.code = 4;
|
result.code = 4;
|
||||||
break;
|
break;
|
||||||
case (IP_BAD_ROUTE): //Bad source route
|
case (IP_BAD_ROUTE): // Bad source route
|
||||||
result.type = 3;
|
result.type = 3;
|
||||||
result.code = 5;
|
result.code = 5;
|
||||||
break;
|
break;
|
||||||
case (IP_BAD_DESTINATION):
|
case (IP_BAD_DESTINATION):
|
||||||
//I think this could be either
|
/*
|
||||||
//Destination network unknown
|
* I think this could be mapped to either
|
||||||
//or
|
* Destination network unknown
|
||||||
//Destination host unknown
|
* or
|
||||||
//Use host unknown
|
* Destination host unknown
|
||||||
|
* Lets map to host unknown
|
||||||
|
*/
|
||||||
result.type = 3;
|
result.type = 3;
|
||||||
result.code = 7;
|
result.code = 7;
|
||||||
break;
|
break;
|
||||||
case (IP_REQ_TIMED_OUT):
|
case (IP_REQ_TIMED_OUT):
|
||||||
//Return nothing
|
// Return nothing
|
||||||
result.type = -2;
|
result.type = -2;
|
||||||
result.code = 0;
|
result.code = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -243,7 +253,7 @@ namespace Sessions
|
||||||
result.code = 0;
|
result.code = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//Unexpected Errors
|
// Unexpected errors
|
||||||
case (IP_BUF_TOO_SMALL):
|
case (IP_BUF_TOO_SMALL):
|
||||||
case (IP_NO_RESOURCES):
|
case (IP_NO_RESOURCES):
|
||||||
case (IP_BAD_OPTION):
|
case (IP_BAD_OPTION):
|
||||||
|
@ -326,9 +336,9 @@ namespace Sessions
|
||||||
iov.iov_len = icmpResponseBufferLen;
|
iov.iov_len = icmpResponseBufferLen;
|
||||||
|
|
||||||
#if defined(ICMP_SOCKETS_LINUX)
|
#if defined(ICMP_SOCKETS_LINUX)
|
||||||
//Needs to hold cmsghdr + sock_extended_err + sockaddr_in
|
// Needs to hold cmsghdr + sock_extended_err + sockaddr_in
|
||||||
//for ICMP error responses (total 44 bytes)
|
// for ICMP error responses, this is a total of 44 bytes
|
||||||
//Unknown for other types of error
|
// Unknown size needed for other error types
|
||||||
std::byte cbuff[64]{};
|
std::byte cbuff[64]{};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -341,6 +351,10 @@ namespace Sessions
|
||||||
#if defined(ICMP_SOCKETS_LINUX)
|
#if defined(ICMP_SOCKETS_LINUX)
|
||||||
ret = recvmsg(icmpSocket, &msg, MSG_DONTWAIT);
|
ret = recvmsg(icmpSocket, &msg, MSG_DONTWAIT);
|
||||||
#elif defined(ICMP_SOCKETS_BSD)
|
#elif defined(ICMP_SOCKETS_BSD)
|
||||||
|
// Why not use MSG_DONTWAIT?
|
||||||
|
// We can get rid of the select logic above if we do use it
|
||||||
|
// Might be doing it for extra error checking??
|
||||||
|
// maybe we can just make the socket non-blocking
|
||||||
ret = recvmsg(icmpSocket, &msg, 0);
|
ret = recvmsg(icmpSocket, &msg, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -381,14 +395,17 @@ namespace Sessions
|
||||||
if (msg.msg_flags & MSG_CTRUNC)
|
if (msg.msg_flags & MSG_CTRUNC)
|
||||||
Console.Error("DEV9: ICMP: RecvMsg Control Truncated");
|
Console.Error("DEV9: ICMP: RecvMsg Control Truncated");
|
||||||
|
|
||||||
|
// On Linux, ICMP errors are stored in control messages retrieved using MSG_ERRQUEUE
|
||||||
sock_extended_err* exErrorPtr = nullptr;
|
sock_extended_err* exErrorPtr = nullptr;
|
||||||
cmsghdr* cmsg;
|
cmsghdr* cmsg;
|
||||||
|
|
||||||
/* Receive auxiliary data in msgh */
|
// Search though control messages, taking the latest mesage
|
||||||
|
// We should only have at most 1 message
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
|
||||||
{
|
{
|
||||||
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
|
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
|
||||||
{
|
{
|
||||||
|
pxAssert(!exErrorPtr);
|
||||||
exErrorPtr = reinterpret_cast<sock_extended_err*>(CMSG_DATA(cmsg));
|
exErrorPtr = reinterpret_cast<sock_extended_err*>(CMSG_DATA(cmsg));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -404,6 +421,7 @@ namespace Sessions
|
||||||
sock_extended_err exError;
|
sock_extended_err exError;
|
||||||
std::memcpy(&exError, exErrorPtr, sizeof(exError));
|
std::memcpy(&exError, exErrorPtr, sizeof(exError));
|
||||||
|
|
||||||
|
// Process the error
|
||||||
if (exError.ee_origin == SO_EE_ORIGIN_ICMP)
|
if (exError.ee_origin == SO_EE_ORIGIN_ICMP)
|
||||||
{
|
{
|
||||||
result.type = exError.ee_type;
|
result.type = exError.ee_type;
|
||||||
|
@ -449,9 +467,9 @@ namespace Sessions
|
||||||
|
|
||||||
offset = headerLength;
|
offset = headerLength;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
//Apple (old BSD)'s raw IP sockets implementation converts the ip_len field to host byte order
|
// Apple (old BSD)'s raw IP sockets implementation converts the ip_len field to host byte order
|
||||||
//and additionally subtracts the header length.
|
// and additionally subtracts the header length.
|
||||||
//https://www.unix.com/man-page/mojave/4/ip/
|
// https://www.unix.com/man-page/mojave/4/ip/
|
||||||
length = ipHeader->ip_len;
|
length = ipHeader->ip_len;
|
||||||
#else
|
#else
|
||||||
length = ntohs(ipHeader->ip_len) - headerLength;
|
length = ntohs(ipHeader->ip_len) - headerLength;
|
||||||
|
@ -469,7 +487,7 @@ namespace Sessions
|
||||||
|
|
||||||
if (icmp.type == 0)
|
if (icmp.type == 0)
|
||||||
{
|
{
|
||||||
//Check if response is to us
|
// Check if response is for us
|
||||||
if (icmpConnectionKind == PingType::RAW)
|
if (icmpConnectionKind == PingType::RAW)
|
||||||
{
|
{
|
||||||
ICMP_HeaderDataIdentifier headerData(icmp.headerData);
|
ICMP_HeaderDataIdentifier headerData(icmp.headerData);
|
||||||
|
@ -477,26 +495,28 @@ namespace Sessions
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//While icmp (and its PayloadPtr) will be destroyed when leaving this function
|
// While icmp (and its PayloadPtr) will be destroyed when leaving this function
|
||||||
//the data pointed to it persist in icmpResponseBuffer
|
// the data it points to persists in icmpResponseBuffer
|
||||||
result.dataLength = icmpPayload->GetLength();
|
result.dataLength = icmpPayload->GetLength();
|
||||||
result.data = icmpPayload->data;
|
result.data = icmpPayload->data;
|
||||||
return &result;
|
return &result;
|
||||||
}
|
}
|
||||||
#if defined(ICMP_SOCKETS_BSD)
|
#if defined(ICMP_SOCKETS_BSD)
|
||||||
|
// On BSD/Mac, ICMP errors are returned as normal packets
|
||||||
else if (icmp.type == 3 || icmp.type == 4 || icmp.type == 5 || icmp.type == 11)
|
else if (icmp.type == 3 || icmp.type == 4 || icmp.type == 5 || icmp.type == 11)
|
||||||
{
|
{
|
||||||
//Check if response is to us
|
// Extract the packet the ICMP message is responding to
|
||||||
//We need to extract the sent header
|
|
||||||
IP_Packet ipPacket(icmpPayload->data, icmpPayload->GetLength(), true);
|
IP_Packet ipPacket(icmpPayload->data, icmpPayload->GetLength(), true);
|
||||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(ipPacket.GetPayload());
|
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(ipPacket.GetPayload());
|
||||||
|
// TODO, validate proto?
|
||||||
ICMP_Packet retIcmp(ipPayload->data, ipPayload->GetLength());
|
ICMP_Packet retIcmp(ipPayload->data, ipPayload->GetLength());
|
||||||
|
|
||||||
|
// Check if response is for us
|
||||||
ICMP_HeaderDataIdentifier headerData(icmp.headerData);
|
ICMP_HeaderDataIdentifier headerData(icmp.headerData);
|
||||||
if (headerData.identifier != icmpId)
|
if (headerData.identifier != icmpId)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
//This response is for us
|
// This response is for us
|
||||||
return &result;
|
return &result;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -506,7 +526,7 @@ namespace Sessions
|
||||||
Console.Error("DEV9: ICMP: Unexpected packet");
|
Console.Error("DEV9: ICMP: Unexpected packet");
|
||||||
pxAssert(false);
|
pxAssert(false);
|
||||||
#endif
|
#endif
|
||||||
//Assume not for us
|
// Assume not for us
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,7 +542,7 @@ namespace Sessions
|
||||||
bool ICMP_Session::Ping::Send(IP_Address parAdapterIP, IP_Address parDestIP, int parTimeToLive, PayloadPtr* parPayload)
|
bool ICMP_Session::Ping::Send(IP_Address parAdapterIP, IP_Address parDestIP, int parTimeToLive, PayloadPtr* parPayload)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
//Documentation is incorrect, IP_OPTION_INFORMATION is to be used regardless of platform
|
// Documentation is incorrect, IP_OPTION_INFORMATION is to be used regardless of platform
|
||||||
IP_OPTION_INFORMATION ipInfo{};
|
IP_OPTION_INFORMATION ipInfo{};
|
||||||
ipInfo.Ttl = parTimeToLive;
|
ipInfo.Ttl = parTimeToLive;
|
||||||
DWORD ret;
|
DWORD ret;
|
||||||
|
@ -533,8 +553,8 @@ namespace Sessions
|
||||||
ret = IcmpSendEcho2(icmpFile, icmpEvent, nullptr, nullptr, parDestIP.integer, parPayload->data, parPayload->GetLength(), &ipInfo, icmpResponseBuffer.get(), icmpResponseBufferLen,
|
ret = IcmpSendEcho2(icmpFile, icmpEvent, nullptr, nullptr, parDestIP.integer, parPayload->data, parPayload->GetLength(), &ipInfo, icmpResponseBuffer.get(), icmpResponseBufferLen,
|
||||||
static_cast<DWORD>(std::chrono::duration_cast<std::chrono::milliseconds>(ICMP_TIMEOUT).count()));
|
static_cast<DWORD>(std::chrono::duration_cast<std::chrono::milliseconds>(ICMP_TIMEOUT).count()));
|
||||||
|
|
||||||
//Documentation states that IcmpSendEcho2 returns ERROR_IO_PENDING
|
// Documentation states that IcmpSendEcho2 returns ERROR_IO_PENDING
|
||||||
//However, it actually returns zero, with the error set to ERROR_IO_PENDING
|
// However, it actually returns zero, with the error set to ERROR_IO_PENDING
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
ret = GetLastError();
|
ret = GetLastError();
|
||||||
|
|
||||||
|
@ -553,8 +573,8 @@ namespace Sessions
|
||||||
{
|
{
|
||||||
icmpDeathClockStart = std::chrono::steady_clock::now();
|
icmpDeathClockStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
//broadcast and multicast mignt need extra setsockopts
|
// Broadcast and multicast might need extra setsockopts calls
|
||||||
//I don't think any game will broadcast/multicast ping
|
// I don't think any game will do a broadcast/multicast ping
|
||||||
|
|
||||||
if (parAdapterIP.integer != 0)
|
if (parAdapterIP.integer != 0)
|
||||||
{
|
{
|
||||||
|
@ -594,7 +614,7 @@ namespace Sessions
|
||||||
#if defined(ICMP_SOCKETS_LINUX)
|
#if defined(ICMP_SOCKETS_LINUX)
|
||||||
if (icmpConnectionKind == PingType::ICMP)
|
if (icmpConnectionKind == PingType::ICMP)
|
||||||
{
|
{
|
||||||
//We get assigned a port
|
// We get assigned a port/Id
|
||||||
sockaddr_in endpoint{};
|
sockaddr_in endpoint{};
|
||||||
socklen_t endpointsize = sizeof(endpoint);
|
socklen_t endpointsize = sizeof(endpoint);
|
||||||
if (getsockname(icmpSocket, reinterpret_cast<sockaddr*>(&endpoint), &endpointsize) == -1)
|
if (getsockname(icmpSocket, reinterpret_cast<sockaddr*>(&endpoint), &endpointsize) == -1)
|
||||||
|
@ -610,7 +630,7 @@ namespace Sessions
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
//Use time, in ms, as id
|
// Use time, in ms, as id
|
||||||
icmpId = static_cast<u16>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
icmpId = static_cast<u16>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,7 +638,7 @@ namespace Sessions
|
||||||
icmp.type = 8;
|
icmp.type = 8;
|
||||||
icmp.code = 0;
|
icmp.code = 0;
|
||||||
|
|
||||||
//We only send one icmp packet per identifier
|
// We only send one icmp packet per identifier
|
||||||
ICMP_HeaderDataIdentifier headerData(icmpId, 1);
|
ICMP_HeaderDataIdentifier headerData(icmpId, 1);
|
||||||
headerData.WriteHeaderData(icmp.headerData);
|
headerData.WriteHeaderData(icmp.headerData);
|
||||||
|
|
||||||
|
@ -689,11 +709,11 @@ namespace Sessions
|
||||||
if (pingRet != nullptr)
|
if (pingRet != nullptr)
|
||||||
{
|
{
|
||||||
Ping* ping = pings[i];
|
Ping* ping = pings[i];
|
||||||
//Remove ping from list and unlock
|
// Remove ping from list and unlock mutex
|
||||||
pings.erase(pings.begin() + i);
|
pings.erase(pings.begin() + i);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
//Create return ICMP packet
|
// Create return ICMP packet
|
||||||
std::optional<ReceivedPayload> ret;
|
std::optional<ReceivedPayload> ret;
|
||||||
if (pingRet->type >= 0)
|
if (pingRet->type >= 0)
|
||||||
{
|
{
|
||||||
|
@ -705,21 +725,21 @@ namespace Sessions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//We will copy the original packet back here
|
// Copy the original packet into the returned ICMP packet
|
||||||
//Allocate fullsize buffer
|
// Allocate fullsize buffer
|
||||||
u8* temp = new u8[ping->originalPacket->GetLength()];
|
u8* temp = new u8[ping->originalPacket->GetLength()];
|
||||||
//Allocate data
|
// Allocate returned ICMP payload
|
||||||
int responseSize = ping->originalPacket->GetHeaderLength() + 8;
|
int responseSize = ping->originalPacket->GetHeaderLength() + 8;
|
||||||
data = new PayloadData(responseSize);
|
data = new PayloadData(responseSize);
|
||||||
|
|
||||||
//Write packet back into bytes
|
// Write packet into buffer
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
ping->originalPacket->WriteBytes(temp, &offset);
|
ping->originalPacket->WriteBytes(temp, &offset);
|
||||||
|
|
||||||
//Copy only needed bytes
|
// Copy only needed bytes
|
||||||
memcpy(data->data.get(), temp, responseSize);
|
memcpy(data->data.get(), temp, responseSize);
|
||||||
|
|
||||||
//cleanup
|
// Cleanup
|
||||||
delete[] temp;
|
delete[] temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,7 +755,7 @@ namespace Sessions
|
||||||
else
|
else
|
||||||
DevCon.WriteLn("DEV9: ICMP: ICMP timeout");
|
DevCon.WriteLn("DEV9: ICMP: ICMP timeout");
|
||||||
|
|
||||||
//free ping
|
// Free ping
|
||||||
delete ping;
|
delete ping;
|
||||||
|
|
||||||
if (--open == 0)
|
if (--open == 0)
|
||||||
|
@ -744,7 +764,7 @@ namespace Sessions
|
||||||
if (ret.has_value())
|
if (ret.has_value())
|
||||||
DevCon.WriteLn("DEV9: ICMP: Return Ping");
|
DevCon.WriteLn("DEV9: ICMP: Return Ping");
|
||||||
|
|
||||||
//Return packet
|
// Return packet
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -760,7 +780,7 @@ namespace Sessions
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note, expects caller to set ipTimeToLive before calling
|
// Expects caller to set ipTimeToLive before calling
|
||||||
bool ICMP_Session::Send(PacketReader::IP::IP_Payload* payload, IP_Packet* packet)
|
bool ICMP_Session::Send(PacketReader::IP::IP_Payload* payload, IP_Packet* packet)
|
||||||
{
|
{
|
||||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(payload);
|
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(payload);
|
||||||
|
@ -770,16 +790,19 @@ namespace Sessions
|
||||||
|
|
||||||
switch (icmp.type)
|
switch (icmp.type)
|
||||||
{
|
{
|
||||||
case 3: //Port Closed
|
case 3: // Port Closed
|
||||||
switch (icmp.code)
|
switch (icmp.code)
|
||||||
{
|
{
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
Console.Error("DEV9: ICMP: Received Packet Rejected, Port Closed");
|
Console.Error("DEV9: ICMP: Received Packet Rejected, Port Closed");
|
||||||
|
|
||||||
//RE:Outbreak Hackfix
|
/*
|
||||||
//TODO, check if still needed
|
* RE:Outbreak Hackfix
|
||||||
|
* ICMP port closed messages has an extra 4 bytes of padding before the packet copy
|
||||||
|
* this can be tested by trying to connect without using the resurrection server DNS
|
||||||
|
* turbo mode may be needed to trigger the bug, depending on the DNS server's latency
|
||||||
|
*/
|
||||||
std::unique_ptr<IP_Packet> retPkt;
|
std::unique_ptr<IP_Packet> retPkt;
|
||||||
if ((icmpPayload->data[0] & 0xF0) == (4 << 4))
|
if ((icmpPayload->data[0] & 0xF0) == (4 << 4))
|
||||||
retPkt = std::make_unique<IP_Packet>(icmpPayload->data, icmpPayload->GetLength(), true);
|
retPkt = std::make_unique<IP_Packet>(icmpPayload->data, icmpPayload->GetLength(), true);
|
||||||
|
@ -803,12 +826,12 @@ namespace Sessions
|
||||||
{
|
{
|
||||||
case static_cast<u8>(IP_Type::TCP):
|
case static_cast<u8>(IP_Type::TCP):
|
||||||
case static_cast<u8>(IP_Type::UDP):
|
case static_cast<u8>(IP_Type::UDP):
|
||||||
//Read ports directly from the payload
|
// Read ports directly from the payload
|
||||||
//both UDP and TCP have the same locations for ports
|
// both UDP and TCP have the same locations for ports
|
||||||
IP_PayloadPtr* payload = static_cast<IP_PayloadPtr*>(retPkt->GetPayload());
|
IP_PayloadPtr* payload = static_cast<IP_PayloadPtr*>(retPkt->GetPayload());
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
NetLib::ReadUInt16(payload->data, &offset, &srvPort); //src
|
NetLib::ReadUInt16(payload->data, &offset, &srvPort); // src
|
||||||
NetLib::ReadUInt16(payload->data, &offset, &ps2Port); //dst
|
NetLib::ReadUInt16(payload->data, &offset, &ps2Port); // dst
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionKey Key{};
|
ConnectionKey Key{};
|
||||||
|
@ -817,7 +840,7 @@ namespace Sessions
|
||||||
Key.ps2Port = ps2Port;
|
Key.ps2Port = ps2Port;
|
||||||
Key.srvPort = srvPort;
|
Key.srvPort = srvPort;
|
||||||
|
|
||||||
//is from Normal Port?
|
// Is from Normal Port?
|
||||||
BaseSession* s = nullptr;
|
BaseSession* s = nullptr;
|
||||||
connections->TryGetValue(Key, &s);
|
connections->TryGetValue(Key, &s);
|
||||||
|
|
||||||
|
@ -828,7 +851,7 @@ namespace Sessions
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Is from Fixed Port?
|
// Is from Fixed Port?
|
||||||
Key.ip = {};
|
Key.ip = {};
|
||||||
Key.srvPort = 0;
|
Key.srvPort = 0;
|
||||||
connections->TryGetValue(Key, &s);
|
connections->TryGetValue(Key, &s);
|
||||||
|
@ -846,7 +869,7 @@ namespace Sessions
|
||||||
Console.Error("DEV9: ICMP: Unsupported ICMP Code For Destination Unreachable %d", icmp.code);
|
Console.Error("DEV9: ICMP: Unsupported ICMP Code For Destination Unreachable %d", icmp.code);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 8: //Echo
|
case 8: // Echo
|
||||||
{
|
{
|
||||||
DevCon.WriteLn("DEV9: ICMP: Send Ping");
|
DevCon.WriteLn("DEV9: ICMP: Send Ping");
|
||||||
open++;
|
open++;
|
||||||
|
@ -871,7 +894,7 @@ namespace Sessions
|
||||||
|
|
||||||
memcpy(ping->headerData, icmp.headerData, 4);
|
memcpy(ping->headerData, icmp.headerData, 4);
|
||||||
|
|
||||||
//Need to copy IP_Packet, original is stack allocated
|
// Need to copy IP_Packet, original is stack allocated
|
||||||
ping->originalPacket = std::make_unique<IP_Packet>(*packet);
|
ping->originalPacket = std::make_unique<IP_Packet>(*packet);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -897,7 +920,7 @@ namespace Sessions
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(ping_mutex);
|
std::scoped_lock lock(ping_mutex);
|
||||||
|
|
||||||
//Cleanup
|
// Cleanup
|
||||||
for (size_t i = 0; i < pings.size(); i++)
|
for (size_t i = 0; i < pings.size(); i++)
|
||||||
delete pings[i];
|
delete pings[i];
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,14 +47,14 @@ namespace Sessions
|
||||||
|
|
||||||
static PingType icmpConnectionKind;
|
static PingType icmpConnectionKind;
|
||||||
|
|
||||||
//Sockets
|
// Sockets
|
||||||
int icmpSocket{-1};
|
int icmpSocket{-1};
|
||||||
std::chrono::steady_clock::time_point icmpDeathClockStart;
|
std::chrono::steady_clock::time_point icmpDeathClockStart;
|
||||||
u16 icmpId;
|
u16 icmpId;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Return buffers
|
// Return buffers
|
||||||
PingResult result{};
|
PingResult result{};
|
||||||
int icmpResponseBufferLen{0};
|
int icmpResponseBufferLen{0};
|
||||||
std::unique_ptr<std::byte[]> icmpResponseBuffer;
|
std::unique_ptr<std::byte[]> icmpResponseBuffer;
|
||||||
|
|
Loading…
Reference in New Issue