Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #6075 from sepalani/pcap-log
PCAP logging with fake TCP/UDP packet
  • Loading branch information
leoetlino committed Feb 10, 2021
2 parents ddacbf8 + 82bb5d9 commit 1fc6fbc
Show file tree
Hide file tree
Showing 12 changed files with 485 additions and 29 deletions.
115 changes: 115 additions & 0 deletions Source/Core/Common/Network.cpp
Expand Up @@ -6,6 +6,15 @@

#include <algorithm>
#include <cctype>
#include <string_view>

#ifndef _WIN32
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#else
#include <WinSock2.h>
#endif

#include <fmt/format.h>

Expand Down Expand Up @@ -72,4 +81,110 @@ std::optional<MACAddress> StringToMacAddress(std::string_view mac_string)

return std::make_optional(mac);
}

EthernetHeader::EthernetHeader() = default;

EthernetHeader::EthernetHeader(u16 ether_type)
{
ethertype = htons(ether_type);
}

u16 EthernetHeader::Size() const
{
return static_cast<u16>(SIZE);
}

IPv4Header::IPv4Header() = default;

IPv4Header::IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to)
{
version_ihl = 0x45;
total_len = htons(Size() + data_size);
flags_fragment_offset = htons(0x4000);
ttl = 0x40;
protocol = ip_proto;
std::memcpy(&source_addr, &from.sin_addr, IPV4_ADDR_LEN);
std::memcpy(&destination_addr, &to.sin_addr, IPV4_ADDR_LEN);

header_checksum = htons(ComputeNetworkChecksum(this, Size()));
}

u16 IPv4Header::Size() const
{
return static_cast<u16>(SIZE);
}

TCPHeader::TCPHeader() = default;

TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data,
u16 length)
{
std::memcpy(&source_port, &from.sin_port, 2);
std::memcpy(&destination_port, &to.sin_port, 2);
sequence_number = htonl(seq);

// TODO: Write flags
// Write data offset
std::memset(&properties, 0x50, 1);

window_size = 0xFFFF;

// Compute the TCP checksum with its pseudo header
const u32 source_addr = ntohl(from.sin_addr.s_addr);
const u32 destination_addr = ntohl(to.sin_addr.s_addr);
const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) +
(destination_addr >> 16) + (destination_addr & 0xFFFF) + IPProto() +
Size() + length;
u32 tcp_checksum = ComputeNetworkChecksum(this, Size(), initial_value);
tcp_checksum += ComputeNetworkChecksum(data, length);
while (tcp_checksum > 0xFFFF)
tcp_checksum = (tcp_checksum >> 16) + (tcp_checksum & 0xFFFF);
checksum = htons(static_cast<u16>(tcp_checksum));
}

u16 TCPHeader::Size() const
{
return static_cast<u16>(SIZE);
}

u8 TCPHeader::IPProto() const
{
return static_cast<u8>(IPPROTO_TCP);
}

UDPHeader::UDPHeader() = default;

UDPHeader::UDPHeader(const sockaddr_in& from, const sockaddr_in& to, u16 data_length)
{
std::memcpy(&source_port, &from.sin_port, 2);
std::memcpy(&destination_port, &to.sin_port, 2);
length = htons(Size() + data_length);
}

u16 UDPHeader::Size() const
{
return static_cast<u16>(SIZE);
}

u8 UDPHeader::IPProto() const
{
return static_cast<u8>(IPPROTO_UDP);
}

// Compute the network checksum with a 32-bit accumulator using the
// "Normal" order, see RFC 1071 for more details.
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
{
u32 checksum = initial_value;
std::size_t index = 0;
const std::string_view data_view{reinterpret_cast<const char*>(data), length};
for (u8 b : data_view)
{
const bool is_hi = index++ % 2 == 0;
checksum += is_hi ? b << 8 : b;
}
while (checksum > 0xFFFF)
checksum = (checksum >> 16) + (checksum & 0xFFFF);
return ~static_cast<u16>(checksum);
}
} // namespace Common
75 changes: 75 additions & 0 deletions Source/Core/Common/Network.h
Expand Up @@ -11,6 +11,8 @@

#include "Common/CommonTypes.h"

struct sockaddr_in;

namespace Common
{
enum class MACConsumer
Expand All @@ -25,8 +27,81 @@ enum
};

using MACAddress = std::array<u8, MAC_ADDRESS_SIZE>;
constexpr std::size_t IPV4_ADDR_LEN = 4;

struct EthernetHeader
{
EthernetHeader();
explicit EthernetHeader(u16 ether_type);
u16 Size() const;

static constexpr std::size_t SIZE = 14;

MACAddress destination = {};
MACAddress source = {};
u16 ethertype = 0;
};
static_assert(sizeof(EthernetHeader) == EthernetHeader::SIZE);

struct IPv4Header
{
IPv4Header();
IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to);
u16 Size() const;

static constexpr std::size_t SIZE = 20;

u8 version_ihl = 0;
u8 dscp_esn = 0;
u16 total_len = 0;
u16 identification = 0;
u16 flags_fragment_offset = 0;
u8 ttl = 0;
u8 protocol = 0;
u16 header_checksum = 0;
u8 source_addr[IPV4_ADDR_LEN]{};
u8 destination_addr[IPV4_ADDR_LEN]{};
};
static_assert(sizeof(IPv4Header) == IPv4Header::SIZE);

struct TCPHeader
{
TCPHeader();
TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, u16 length);
u16 Size() const;
u8 IPProto() const;

static constexpr std::size_t SIZE = 20;

u16 source_port = 0;
u16 destination_port = 0;
u32 sequence_number = 0;
u32 acknowledgement_number = 0;
u16 properties = 0;
u16 window_size = 0;
u16 checksum = 0;
u16 urgent_pointer = 0;
};
static_assert(sizeof(TCPHeader) == TCPHeader::SIZE);

struct UDPHeader
{
UDPHeader();
UDPHeader(const sockaddr_in& from, const sockaddr_in& to, u16 data_length);
u16 Size() const;
u8 IPProto() const;

static constexpr std::size_t SIZE = 8;

u16 source_port = 0;
u16 destination_port = 0;
u16 length = 0;
u16 checksum = 0;
};
static_assert(sizeof(UDPHeader) == UDPHeader::SIZE);

MACAddress GenerateMacAddress(MACConsumer type);
std::string MacAddressToString(const MACAddress& mac);
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0);
} // namespace Common
7 changes: 2 additions & 5 deletions Source/Core/Common/PcapFile.cpp
Expand Up @@ -17,9 +17,6 @@ const u16 PCAP_VERSION_MAJOR = 2;
const u16 PCAP_VERSION_MINOR = 4;
const u32 PCAP_CAPTURE_LENGTH = 65535;

// TODO(delroth): Make this configurable at PCAP creation time?
const u32 PCAP_DATA_LINK_TYPE = 147; // Reserved for internal use.

// Designed to be directly written into the PCAP file. The PCAP format is
// endian independent, so this works just fine.
#pragma pack(push, 1)
Expand All @@ -45,10 +42,10 @@ struct PCAPRecordHeader

} // namespace

void PCAP::AddHeader()
void PCAP::AddHeader(u32 link_type)
{
PCAPHeader hdr = {PCAP_MAGIC, PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR, 0,
0, PCAP_CAPTURE_LENGTH, PCAP_DATA_LINK_TYPE};
0, PCAP_CAPTURE_LENGTH, link_type};
m_fp->WriteBytes(&hdr, sizeof(hdr));
}

Expand Down
13 changes: 11 additions & 2 deletions Source/Core/Common/PcapFile.h
Expand Up @@ -24,9 +24,18 @@ namespace Common
class PCAP final
{
public:
enum class LinkType : u32
{
Ethernet = 1, // IEEE 802.3 Ethernet
User = 147, // Reserved for internal use
};

// Takes ownership of the file object. Assumes the file object is already
// opened in write mode.
explicit PCAP(File::IOFile* fp) : m_fp(fp) { AddHeader(); }
explicit PCAP(File::IOFile* fp, LinkType link_type = LinkType::User) : m_fp(fp)
{
AddHeader(static_cast<u32>(link_type));
}
template <typename T>
void AddPacket(const T& obj)
{
Expand All @@ -36,7 +45,7 @@ class PCAP final
void AddPacket(const u8* bytes, size_t size);

private:
void AddHeader();
void AddHeader(u32 link_type);

std::unique_ptr<File::IOFile> m_fp;
};
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/Config/MainSettings.cpp
Expand Up @@ -156,6 +156,7 @@ const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES{
const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA{{System::Main, "Network", "SSLDumpRootCA"}, false};
const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT{{System::Main, "Network", "SSLDumpPeerCert"},
false};
const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP{{System::Main, "Network", "DumpAsPCAP"}, false};

// Main.Interface

Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/Config/MainSettings.h
Expand Up @@ -129,6 +129,7 @@ extern const Info<bool> MAIN_NETWORK_SSL_DUMP_WRITE;
extern const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT;
extern const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP;

// Main.Interface

Expand Down
13 changes: 11 additions & 2 deletions Source/Core/Core/Debugger/PPCDebugInterface.cpp
Expand Up @@ -388,14 +388,23 @@ std::shared_ptr<Core::NetworkCaptureLogger> PPCDebugInterface::NetworkLogger()
{
const bool has_ssl = Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ) ||
Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE);
const auto current_capture_type =
has_ssl ? Core::NetworkCaptureType::Raw : Core::NetworkCaptureType::None;
const bool is_pcap = Config::Get(Config::MAIN_NETWORK_DUMP_AS_PCAP);
const auto current_capture_type = [&] {
if (is_pcap)
return Core::NetworkCaptureType::PCAP;
if (has_ssl)
return Core::NetworkCaptureType::Raw;
return Core::NetworkCaptureType::None;
}();

if (m_network_logger && m_network_logger->GetCaptureType() == current_capture_type)
return m_network_logger;

switch (current_capture_type)
{
case Core::NetworkCaptureType::PCAP:
m_network_logger = std::make_shared<Core::PCAPSSLCaptureLogger>();
break;
case Core::NetworkCaptureType::Raw:
m_network_logger = std::make_shared<Core::BinarySSLCaptureLogger>();
break;
Expand Down
25 changes: 16 additions & 9 deletions Source/Core/Core/IOS/Network/Socket.cpp
Expand Up @@ -462,8 +462,9 @@ void WiiSocket::Update(bool read, bool write, bool except)

if (ret >= 0)
{
PowerPC::debug_interface.NetworkLogger()->LogWrite(Memory::GetPointer(BufferOut2),
ret);
PowerPC::debug_interface.NetworkLogger()->LogSSLWrite(
Memory::GetPointer(BufferOut2), ret,
static_cast<mbedtls_net_context*>(Device::NetSSL::_SSL[sslID].ctx.p_bio)->fd);
// Return bytes written or SSL_ERR_ZERO if none
WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
}
Expand Down Expand Up @@ -495,7 +496,9 @@ void WiiSocket::Update(bool read, bool write, bool except)

if (ret >= 0)
{
PowerPC::debug_interface.NetworkLogger()->LogRead(Memory::GetPointer(BufferIn2), ret);
PowerPC::debug_interface.NetworkLogger()->LogSSLRead(
Memory::GetPointer(BufferIn2), ret,
static_cast<mbedtls_net_context*>(Device::NetSSL::_SSL[sslID].ctx.p_bio)->fd);
// Return bytes read or SSL_ERR_ZERO if none
WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
}
Expand Down Expand Up @@ -553,10 +556,12 @@ void WiiSocket::Update(bool read, bool write, bool except)
WiiSockMan::Convert(*wii_name, local_name);
}

const int ret = sendto(fd, data, BufferInSize, flags,
has_destaddr ? (struct sockaddr*)&local_name : nullptr,
has_destaddr ? sizeof(sockaddr) : 0);
auto* to = has_destaddr ? reinterpret_cast<sockaddr*>(&local_name) : nullptr;
socklen_t tolen = has_destaddr ? sizeof(sockaddr) : 0;
const int ret = sendto(fd, data, BufferInSize, flags, to, tolen);
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true);
if (ret > 0)
PowerPC::debug_interface.NetworkLogger()->LogWrite(data, ret, fd, to);

INFO_LOG_FMT(IOS_NET,
"{} = {} Socket: {:08x}, BufferIn: ({:08x}, {}), BufferIn2: ({:08x}, {}), "
Expand Down Expand Up @@ -599,11 +604,13 @@ void WiiSocket::Update(bool read, bool write, bool except)
}
#endif
socklen_t addrlen = sizeof(sockaddr_in);
const int ret = recvfrom(fd, data, data_len, flags,
BufferOutSize2 ? (struct sockaddr*)&local_name : nullptr,
BufferOutSize2 ? &addrlen : nullptr);
auto* from = BufferOutSize2 ? reinterpret_cast<sockaddr*>(&local_name) : nullptr;
socklen_t* fromlen = BufferOutSize2 ? &addrlen : nullptr;
const int ret = recvfrom(fd, data, data_len, flags, from, fromlen);
ReturnValue =
WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true);
if (ret > 0)
PowerPC::debug_interface.NetworkLogger()->LogRead(data, ret, fd, from);

INFO_LOG_FMT(IOS_NET,
"{}({}, {}) Socket: {:08X}, Flags: {:08X}, "
Expand Down

0 comments on commit 1fc6fbc

Please sign in to comment.