Split networking code into its own target (#2091)

This commit is contained in:
Jesse Talavera
2024-07-14 11:03:21 -04:00
committed by GitHub
parent a812a43bda
commit 94ba7c1594
213 changed files with 159 additions and 108 deletions

20
src/net/CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
add_library(net-utils STATIC
Net.cpp
Net_PCap.cpp
Net_Slirp.cpp
PacketDispatcher.cpp
LocalMP.cpp
)
target_include_directories(net-utils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(net-utils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" OFF)
if (USE_SYSTEM_LIBSLIRP)
pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp)
target_link_libraries(net-utils PRIVATE PkgConfig::Slirp)
else()
add_subdirectory(libslirp EXCLUDE_FROM_ALL)
target_link_libraries(net-utils PRIVATE slirp)
endif()

443
src/net/LocalMP.cpp Normal file
View File

@ -0,0 +1,443 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <cstring>
#include "LocalMP.h"
#include "Platform.h"
#include "types.h"
using namespace melonDS;
using namespace melonDS::Platform;
using Platform::Log;
using Platform::LogLevel;
namespace LocalMP
{
struct MPStatusData
{
u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive packets
u32 PacketWriteOffset;
u32 ReplyWriteOffset;
u16 MPHostinst; // instance ID from which the last CMD frame was sent
u16 MPReplyBitmask; // bitmask of which clients replied in time
};
struct MPPacketHeader
{
u32 Magic;
u32 SenderID;
u32 Type; // 0=regular 1=CMD 2=reply 3=ack
u32 Length;
u64 Timestamp;
};
const u32 kPacketQueueSize = 0x10000;
const u32 kReplyQueueSize = 0x10000;
const u32 kMaxFrameSize = 0x948;
Mutex* MPQueueLock;
MPStatusData MPStatus;
u8 MPPacketQueue[kPacketQueueSize];
u8 MPReplyQueue[kReplyQueueSize];
u32 PacketReadOffset[16];
u32 ReplyReadOffset[16];
int RecvTimeout;
int LastHostID;
Semaphore* SemPool[32];
void SemPoolInit()
{
for (int i = 0; i < 32; i++)
{
SemPool[i] = Semaphore_Create();
}
}
bool SemPost(int num)
{
Semaphore_Post(SemPool[num]);
return true;
}
bool SemWait(int num, int timeout)
{
return Semaphore_TryWait(SemPool[num], timeout);
}
void SemReset(int num)
{
Semaphore_Reset(SemPool[num]);
}
bool Init()
{
MPQueueLock = Mutex_Create();
Mutex_Lock(MPQueueLock);
memset(MPPacketQueue, 0, kPacketQueueSize);
memset(MPReplyQueue, 0, kReplyQueueSize);
memset(&MPStatus, 0, sizeof(MPStatus));
memset(PacketReadOffset, 0, sizeof(PacketReadOffset));
memset(ReplyReadOffset, 0, sizeof(ReplyReadOffset));
Mutex_Unlock(MPQueueLock);
// prepare semaphores
// semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame
// semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply
SemPoolInit();
LastHostID = -1;
Log(LogLevel::Info, "MP comm init OK\n");
RecvTimeout = 25;
return true;
}
void DeInit()
{
for (int i = 0; i < 32; i++)
{
Semaphore_Free(SemPool[i]);
SemPool[i] = nullptr;
}
Mutex_Free(MPQueueLock);
}
void SetRecvTimeout(int timeout)
{
RecvTimeout = timeout;
}
void Begin(int inst)
{
Mutex_Lock(MPQueueLock);
PacketReadOffset[inst] = MPStatus.PacketWriteOffset;
ReplyReadOffset[inst] = MPStatus.ReplyWriteOffset;
SemReset(inst);
SemReset(16+inst);
MPStatus.ConnectedBitmask |= (1 << inst);
Mutex_Unlock(MPQueueLock);
}
void End(int inst)
{
Mutex_Lock(MPQueueLock);
MPStatus.ConnectedBitmask &= ~(1 << inst);
Mutex_Unlock(MPQueueLock);
}
void FIFORead(int inst, int fifo, void* buf, int len)
{
u8* data;
u32 offset, datalen;
if (fifo == 0)
{
offset = PacketReadOffset[inst];
data = MPPacketQueue;
datalen = kPacketQueueSize;
}
else
{
offset = ReplyReadOffset[inst];
data = MPReplyQueue;
datalen = kReplyQueueSize;
}
if ((offset + len) >= datalen)
{
u32 part1 = datalen - offset;
memcpy(buf, &data[offset], part1);
memcpy(&((u8*)buf)[part1], data, len - part1);
offset = len - part1;
}
else
{
memcpy(buf, &data[offset], len);
offset += len;
}
if (fifo == 0) PacketReadOffset[inst] = offset;
else ReplyReadOffset[inst] = offset;
}
void FIFOWrite(int inst, int fifo, void* buf, int len)
{
u8* data;
u32 offset, datalen;
if (fifo == 0)
{
offset = MPStatus.PacketWriteOffset;
data = MPPacketQueue;
datalen = kPacketQueueSize;
}
else
{
offset = MPStatus.ReplyWriteOffset;
data = MPReplyQueue;
datalen = kReplyQueueSize;
}
if ((offset + len) >= datalen)
{
u32 part1 = datalen - offset;
memcpy(&data[offset], buf, part1);
memcpy(data, &((u8*)buf)[part1], len - part1);
offset = len - part1;
}
else
{
memcpy(&data[offset], buf, len);
offset += len;
}
if (fifo == 0) MPStatus.PacketWriteOffset = offset;
else MPStatus.ReplyWriteOffset = offset;
}
int SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp)
{
if (len > kMaxFrameSize)
{
Log(LogLevel::Warn, "wifi: attempting to send frame too big (len=%d max=%d)\n", len, kMaxFrameSize);
return 0;
}
Mutex_Lock(MPQueueLock);
u16 mask = MPStatus.ConnectedBitmask;
// TODO: check if the FIFO is full!
MPPacketHeader pktheader;
pktheader.Magic = 0x4946494E;
pktheader.SenderID = inst;
pktheader.Type = type;
pktheader.Length = len;
pktheader.Timestamp = timestamp;
type &= 0xFFFF;
int nfifo = (type == 2) ? 1 : 0;
FIFOWrite(inst, nfifo, &pktheader, sizeof(pktheader));
if (len)
FIFOWrite(inst, nfifo, packet, len);
if (type == 1)
{
// NOTE: this is not guarded against, say, multiple multiplay games happening on the same machine
// we would need to pass the packet's SenderID through the wifi module for that
MPStatus.MPHostinst = inst;
MPStatus.MPReplyBitmask = 0;
ReplyReadOffset[inst] = MPStatus.ReplyWriteOffset;
SemReset(16 + inst);
}
else if (type == 2)
{
MPStatus.MPReplyBitmask |= (1 << inst);
}
Mutex_Unlock(MPQueueLock);
if (type == 2)
{
SemPost(16 + MPStatus.MPHostinst);
}
else
{
for (int i = 0; i < 16; i++)
{
if (mask & (1<<i))
SemPost(i);
}
}
return len;
}
int RecvPacketGeneric(int inst, u8* packet, bool block, u64* timestamp)
{
for (;;)
{
if (!SemWait(inst, block ? RecvTimeout : 0))
{
return 0;
}
Mutex_Lock(MPQueueLock);
MPPacketHeader pktheader;
FIFORead(inst, 0, &pktheader, sizeof(pktheader));
if (pktheader.Magic != 0x4946494E)
{
Log(LogLevel::Warn, "PACKET FIFO OVERFLOW\n");
PacketReadOffset[inst] = MPStatus.PacketWriteOffset;
SemReset(inst);
Mutex_Unlock(MPQueueLock);
return 0;
}
if (pktheader.SenderID == inst)
{
// skip this packet
PacketReadOffset[inst] += pktheader.Length;
if (PacketReadOffset[inst] >= kPacketQueueSize)
PacketReadOffset[inst] -= kPacketQueueSize;
Mutex_Unlock(MPQueueLock);
continue;
}
if (pktheader.Length)
{
FIFORead(inst, 0, packet, pktheader.Length);
if (pktheader.Type == 1)
LastHostID = pktheader.SenderID;
}
if (timestamp) *timestamp = pktheader.Timestamp;
Mutex_Unlock(MPQueueLock);
return pktheader.Length;
}
}
int SendPacket(int inst, u8* packet, int len, u64 timestamp)
{
return SendPacketGeneric(inst, 0, packet, len, timestamp);
}
int RecvPacket(int inst, u8* packet, u64* timestamp)
{
return RecvPacketGeneric(inst, packet, false, timestamp);
}
int SendCmd(int inst, u8* packet, int len, u64 timestamp)
{
return SendPacketGeneric(inst, 1, packet, len, timestamp);
}
int SendReply(int inst, u8* packet, int len, u64 timestamp, u16 aid)
{
return SendPacketGeneric(inst, 2 | (aid<<16), packet, len, timestamp);
}
int SendAck(int inst, u8* packet, int len, u64 timestamp)
{
return SendPacketGeneric(inst, 3, packet, len, timestamp);
}
int RecvHostPacket(int inst, u8* packet, u64* timestamp)
{
if (LastHostID != -1)
{
// check if the host is still connected
u16 curinstmask = MPStatus.ConnectedBitmask;
if (!(curinstmask & (1 << LastHostID)))
return -1;
}
return RecvPacketGeneric(inst, packet, true, timestamp);
}
u16 RecvReplies(int inst, u8* packets, u64 timestamp, u16 aidmask)
{
u16 ret = 0;
u16 myinstmask = (1 << inst);
u16 curinstmask;
curinstmask = MPStatus.ConnectedBitmask;
// if all clients have left: return early
if ((myinstmask & curinstmask) == curinstmask)
return 0;
for (;;)
{
if (!SemWait(16+inst, RecvTimeout))
{
// no more replies available
return ret;
}
Mutex_Lock(MPQueueLock);
MPPacketHeader pktheader;
FIFORead(inst, 1, &pktheader, sizeof(pktheader));
if (pktheader.Magic != 0x4946494E)
{
Log(LogLevel::Warn, "REPLY FIFO OVERFLOW\n");
ReplyReadOffset[inst] = MPStatus.ReplyWriteOffset;
SemReset(16+inst);
Mutex_Unlock(MPQueueLock);
return 0;
}
if ((pktheader.SenderID == inst) || // packet we sent out (shouldn't happen, but hey)
(pktheader.Timestamp < (timestamp - 32))) // stale packet
{
// skip this packet
ReplyReadOffset[inst] += pktheader.Length;
if (ReplyReadOffset[inst] >= kReplyQueueSize)
ReplyReadOffset[inst] -= kReplyQueueSize;
Mutex_Unlock(MPQueueLock);
continue;
}
if (pktheader.Length)
{
u32 aid = (pktheader.Type >> 16);
FIFORead(inst, 1, &packets[(aid-1)*1024], pktheader.Length);
ret |= (1 << aid);
}
myinstmask |= (1 << pktheader.SenderID);
if (((myinstmask & curinstmask) == curinstmask) ||
((ret & aidmask) == aidmask))
{
// all the clients have sent their reply
Mutex_Unlock(MPQueueLock);
return ret;
}
Mutex_Unlock(MPQueueLock);
}
}
}

46
src/net/LocalMP.h Normal file
View File

@ -0,0 +1,46 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LOCALMP_H
#define LOCALMP_H
#include "types.h"
namespace LocalMP
{
using namespace melonDS;
bool Init();
void DeInit();
void SetRecvTimeout(int timeout);
void Begin(int inst);
void End(int inst);
int SendPacket(int inst, u8* data, int len, u64 timestamp);
int RecvPacket(int inst, u8* data, u64* timestamp);
int SendCmd(int inst, u8* data, int len, u64 timestamp);
int SendReply(int inst, u8* data, int len, u64 timestamp, u16 aid);
int SendAck(int inst, u8* data, int len, u64 timestamp);
int RecvHostPacket(int inst, u8* data, u64* timestamp);
u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask);
}
#endif // LOCALMP_H

111
src/net/Net.cpp Normal file
View File

@ -0,0 +1,111 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "Net.h"
#include "Net_PCap.h"
#include "Net_Slirp.h"
#include "PacketDispatcher.h"
#include "Platform.h"
using namespace melonDS;
namespace Net
{
using Platform::Log;
using Platform::LogLevel;
bool Inited = false;
bool DirectMode;
PacketDispatcher Dispatcher;
bool Init(bool direct, const char* devicename)
{
if (Inited) DeInit();
Dispatcher.clear();
DirectMode = direct;
bool ret = false;
if (DirectMode)
ret = Net_PCap::Init(devicename);
else
ret = Net_Slirp::Init();
Inited = ret;
return ret;
}
void DeInit()
{
if (!Inited) return;
if (DirectMode)
Net_PCap::DeInit();
else
Net_Slirp::DeInit();
Inited = false;
}
void RegisterInstance(int inst)
{
Dispatcher.registerInstance(inst);
}
void UnregisterInstance(int inst)
{
Dispatcher.unregisterInstance(inst);
}
void RXEnqueue(const void* buf, int len)
{
Dispatcher.sendPacket(nullptr, 0, buf, len, 16, 0xFFFF);
}
int SendPacket(u8* data, int len, int inst)
{
if (DirectMode)
return Net_PCap::SendPacket(data, len);
else
return Net_Slirp::SendPacket(data, len);
}
int RecvPacket(u8* data, int inst)
{
if (DirectMode)
Net_PCap::RecvCheck();
else
Net_Slirp::RecvCheck();
int ret = 0;
if (!Dispatcher.recvPacket(nullptr, nullptr, data, &ret, inst))
return 0;
return ret;
}
}

45
src/net/Net.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef NET_H
#define NET_H
#include "types.h"
namespace Net
{
using namespace melonDS;
///
/// @param direct Whether to use direct or indirect mode
/// @param devicename The name of the network device to use; ignored if direct is false
/// @return true if initialization succeeded
bool Init(bool direct, const char* devicename);
void DeInit();
void RegisterInstance(int inst);
void UnregisterInstance(int inst);
void RXEnqueue(const void* buf, int len);
int SendPacket(u8* data, int len, int inst);
int RecvPacket(u8* data, int inst);
}
#endif // NET_H

379
src/net/Net_PCap.cpp Normal file
View File

@ -0,0 +1,379 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <string.h>
#include <pcap/pcap.h>
#include "Net.h"
#include "Net_PCap.h"
#include "Platform.h"
#ifdef __WIN32__
#include <iphlpapi.h>
#else
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#ifdef __linux__
#include <linux/if_packet.h>
#else
#include <net/if.h>
#include <net/if_dl.h>
#endif
#endif
using namespace melonDS;
using Platform::Log;
using Platform::LogLevel;
// welp
#ifndef PCAP_OPENFLAG_PROMISCUOUS
#define PCAP_OPENFLAG_PROMISCUOUS 1
#endif
#define DECL_PCAP_FUNC(ret, name, args, args2) \
typedef ret (*type_##name) args; \
type_##name ptr_##name = nullptr; \
ret name args { return ptr_##name args2; }
DECL_PCAP_FUNC(int, pcap_findalldevs, (pcap_if_t** alldevs, char* errbuf), (alldevs,errbuf))
DECL_PCAP_FUNC(void, pcap_freealldevs, (pcap_if_t* alldevs), (alldevs))
DECL_PCAP_FUNC(pcap_t*, pcap_open_live, (const char* src, int snaplen, int flags, int readtimeout, char* errbuf), (src,snaplen,flags,readtimeout,errbuf))
DECL_PCAP_FUNC(void, pcap_close, (pcap_t* dev), (dev))
DECL_PCAP_FUNC(int, pcap_setnonblock, (pcap_t* dev, int nonblock, char* errbuf), (dev,nonblock,errbuf))
DECL_PCAP_FUNC(int, pcap_sendpacket, (pcap_t* dev, const u_char* data, int len), (dev,data,len))
DECL_PCAP_FUNC(int, pcap_dispatch, (pcap_t* dev, int num, pcap_handler callback, u_char* data), (dev,num,callback,data))
DECL_PCAP_FUNC(const u_char*, pcap_next, (pcap_t* dev, struct pcap_pkthdr* hdr), (dev,hdr))
namespace Net_PCap
{
const char* PCapLibNames[] =
{
#ifdef __WIN32__
// TODO: name for npcap in non-WinPCap mode
"wpcap.dll",
#elif defined(__APPLE__)
"libpcap.A.dylib",
"libpcap.dylib",
#else
// Linux lib names
"libpcap.so.1",
"libpcap.so",
#endif
nullptr
};
AdapterData* Adapters = nullptr;
int NumAdapters = 0;
Platform::DynamicLibrary* PCapLib = nullptr;
pcap_t* PCapAdapter = nullptr;
AdapterData* PCapAdapterData;
#define LOAD_PCAP_FUNC(sym) \
ptr_##sym = (type_##sym)DynamicLibrary_LoadFunction(lib, #sym); \
if (!ptr_##sym) return false;
bool TryLoadPCap(Platform::DynamicLibrary *lib)
{
LOAD_PCAP_FUNC(pcap_findalldevs)
LOAD_PCAP_FUNC(pcap_freealldevs)
LOAD_PCAP_FUNC(pcap_open_live)
LOAD_PCAP_FUNC(pcap_close)
LOAD_PCAP_FUNC(pcap_setnonblock)
LOAD_PCAP_FUNC(pcap_sendpacket)
LOAD_PCAP_FUNC(pcap_dispatch)
LOAD_PCAP_FUNC(pcap_next)
return true;
}
bool InitAdapterList()
{
NumAdapters = 0;
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
PCapLib = nullptr;
PCapAdapter = nullptr;
for (int i = 0; PCapLibNames[i]; i++)
{
Platform::DynamicLibrary* lib = Platform::DynamicLibrary_Load(PCapLibNames[i]);
if (!lib) continue;
if (!TryLoadPCap(lib))
{
Platform::DynamicLibrary_Unload(lib);
continue;
}
Log(LogLevel::Info, "PCap: lib %s, init successful\n", PCapLibNames[i]);
PCapLib = lib;
break;
}
if (PCapLib == nullptr)
{
Log(LogLevel::Error, "PCap: init failed\n");
return false;
}
}
char errbuf[PCAP_ERRBUF_SIZE];
int ret;
pcap_if_t* alldevs;
ret = pcap_findalldevs(&alldevs, errbuf);
if (ret < 0 || alldevs == nullptr)
{
Log(LogLevel::Warn, "PCap: no devices available\n");
return false;
}
pcap_if_t* dev = alldevs;
while (dev) { NumAdapters++; dev = dev->next; }
Adapters = new AdapterData[NumAdapters];
memset(Adapters, 0, sizeof(AdapterData)*NumAdapters);
AdapterData* adata = &Adapters[0];
dev = alldevs;
while (dev)
{
strncpy(adata->DeviceName, dev->name, 127);
adata->DeviceName[127] = '\0';
#ifndef __WIN32__
strncpy(adata->FriendlyName, adata->DeviceName, 127);
adata->FriendlyName[127] = '\0';
#endif // __WIN32__
dev = dev->next;
adata++;
}
#ifdef __WIN32__
ULONG bufsize = 16384;
IP_ADAPTER_ADDRESSES* buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
ULONG uret = GetAdaptersAddresses(AF_INET, 0, nullptr, buf, &bufsize);
if (uret == ERROR_BUFFER_OVERFLOW)
{
HeapFree(GetProcessHeap(), 0, buf);
buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
uret = GetAdaptersAddresses(AF_INET, 0, nullptr, buf, &bufsize);
}
if (uret != ERROR_SUCCESS)
{
Log(LogLevel::Error, "GetAdaptersAddresses() shat itself: %08X\n", uret);
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
IP_ADAPTER_ADDRESSES* addr = buf;
while (addr)
{
if (strcmp(addr->AdapterName, &adata->DeviceName[12]))
{
addr = addr->Next;
continue;
}
WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, nullptr, nullptr);
adata->FriendlyName[127] = '\0';
WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, nullptr, nullptr);
adata->Description[127] = '\0';
if (addr->PhysicalAddressLength != 6)
{
Log(LogLevel::Warn, "weird MAC addr length %d for %s\n", addr->PhysicalAddressLength, addr->AdapterName);
}
else
memcpy(adata->MAC, addr->PhysicalAddress, 6);
IP_ADAPTER_UNICAST_ADDRESS* ipaddr = addr->FirstUnicastAddress;
while (ipaddr)
{
SOCKADDR* sa = ipaddr->Address.lpSockaddr;
if (sa->sa_family == AF_INET)
{
struct in_addr sa4 = ((sockaddr_in*)sa)->sin_addr;
memcpy(adata->IP_v4, &sa4, 4);
}
ipaddr = ipaddr->Next;
}
break;
}
}
HeapFree(GetProcessHeap(), 0, buf);
#else
struct ifaddrs* addrs;
if (getifaddrs(&addrs) != 0)
{
Log(LogLevel::Error, "getifaddrs() shat itself :(\n");
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
struct ifaddrs* curaddr = addrs;
while (curaddr)
{
if (strcmp(curaddr->ifa_name, adata->DeviceName))
{
curaddr = curaddr->ifa_next;
continue;
}
if (!curaddr->ifa_addr)
{
Log(LogLevel::Error, "Device (%s) does not have an address :/\n", curaddr->ifa_name);
curaddr = curaddr->ifa_next;
continue;
}
u16 af = curaddr->ifa_addr->sa_family;
if (af == AF_INET)
{
struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr;
memcpy(adata->IP_v4, &sa->sin_addr, 4);
}
#ifdef __linux__
else if (af == AF_PACKET)
{
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
if (sa->sll_halen != 6)
Log(LogLevel::Warn, "weird MAC length %d for %s\n", sa->sll_halen, curaddr->ifa_name);
else
memcpy(adata->MAC, sa->sll_addr, 6);
}
#else
else if (af == AF_LINK)
{
struct sockaddr_dl* sa = (sockaddr_dl*)curaddr->ifa_addr;
if (sa->sdl_alen != 6)
Log(LogLevel::Warn, "weird MAC length %d for %s\n", sa->sdl_alen, curaddr->ifa_name);
else
memcpy(adata->MAC, LLADDR(sa), 6);
}
#endif
curaddr = curaddr->ifa_next;
}
}
freeifaddrs(addrs);
#endif // __WIN32__
pcap_freealldevs(alldevs);
return true;
}
bool Init(std::string_view devicename)
{
if (!PCapLib) PCapAdapter = nullptr;
if (PCapAdapter) pcap_close(PCapAdapter);
InitAdapterList();
// open pcap device
PCapAdapterData = &Adapters[0];
for (int i = 0; i < NumAdapters; i++)
{
if (!strncmp(Adapters[i].DeviceName, devicename.data(), 128))
PCapAdapterData = &Adapters[i];
}
char errbuf[PCAP_ERRBUF_SIZE];
PCapAdapter = pcap_open_live(PCapAdapterData->DeviceName, 2048, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf);
if (!PCapAdapter)
{
Log(LogLevel::Error, "PCap: failed to open adapter %s\n", errbuf);
return false;
}
if (pcap_setnonblock(PCapAdapter, 1, errbuf) < 0)
{
Log(LogLevel::Error, "PCap: failed to set nonblocking mode\n");
pcap_close(PCapAdapter); PCapAdapter = nullptr;
return false;
}
return true;
}
void DeInit()
{
if (PCapLib)
{
if (PCapAdapter)
{
pcap_close(PCapAdapter);
PCapAdapter = nullptr;
}
Platform::DynamicLibrary_Unload(PCapLib);
PCapLib = nullptr;
}
}
void RXCallback(u_char* userdata, const struct pcap_pkthdr* header, const u_char* data)
{
Net::RXEnqueue(data, header->len);
}
int SendPacket(u8* data, int len)
{
if (PCapAdapter == nullptr)
return 0;
if (len > 2048)
{
Log(LogLevel::Error, "Net_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
pcap_sendpacket(PCapAdapter, data, len);
// TODO: check success
return len;
}
void RecvCheck()
{
if (PCapAdapter == nullptr)
return;
pcap_dispatch(PCapAdapter, 1, RXCallback, nullptr);
}
}

53
src/net/Net_PCap.h Normal file
View File

@ -0,0 +1,53 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef NET_PCAP_H
#define NET_PCAP_H
#include <string_view>
#include "types.h"
namespace Net_PCap
{
using namespace melonDS;
struct AdapterData
{
char DeviceName[128];
char FriendlyName[128];
char Description[128];
u8 MAC[6];
u8 IP_v4[4];
};
extern AdapterData* Adapters;
extern int NumAdapters;
bool InitAdapterList();
bool Init(std::string_view devicename);
void DeInit();
int SendPacket(u8* data, int len);
void RecvCheck();
}
#endif // NET_PCAP_H

467
src/net/Net_Slirp.cpp Normal file
View File

@ -0,0 +1,467 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "Net.h"
#include "FIFO.h"
#include "Platform.h"
#include <libslirp.h>
#ifdef __WIN32__
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#include <poll.h>
#include <time.h>
#endif
using namespace melonDS;
namespace Net_Slirp
{
using Platform::Log;
using Platform::LogLevel;
const u32 kSubnet = 0x0A400000;
const u32 kServerIP = kSubnet | 0x01;
const u32 kDNSIP = kSubnet | 0x02;
const u32 kClientIP = kSubnet | 0x10;
const u8 kServerMAC[6] = {0x00, 0xAB, 0x33, 0x28, 0x99, 0x44};
FIFO<u32, (0x8000 >> 2)> RXBuffer;
u32 IPv4ID;
Slirp* Ctx = nullptr;
#ifdef __WIN32__
#define poll WSAPoll
// https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows
struct timespec { long tv_sec; long tv_nsec; };
#define CLOCK_MONOTONIC 1312
int clock_gettime(int, struct timespec *spec)
{
__int64 wintime;
GetSystemTimeAsFileTime((FILETIME*)&wintime);
wintime -=116444736000000000LL; //1jan1601 to 1jan1970
spec->tv_sec = wintime / 10000000LL; //seconds
spec->tv_nsec = wintime % 10000000LL * 100; //nano-seconds
return 0;
}
#endif // __WIN32__
ssize_t SlirpCbSendPacket(const void* buf, size_t len, void* opaque)
{
if (len > 2048)
{
Log(LogLevel::Warn, "slirp: packet too big (%zu)\n", len);
return 0;
}
Log(LogLevel::Debug, "slirp: response packet of %zu bytes, type %04X\n", len, ntohs(((u16*)buf)[6]));
Net::RXEnqueue(buf, len);
return len;
}
void SlirpCbGuestError(const char* msg, void* opaque)
{
Log(LogLevel::Error, "SLIRP: error: %s\n", msg);
}
int64_t SlirpCbClockGetNS(void* opaque)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
}
void* SlirpCbTimerNew(SlirpTimerCb cb, void* cb_opaque, void* opaque)
{
return nullptr;
}
void SlirpCbTimerFree(void* timer, void* opaque)
{
}
void SlirpCbTimerMod(void* timer, int64_t expire_time, void* opaque)
{
}
void SlirpCbRegisterPollFD(int fd, void* opaque)
{
Log(LogLevel::Debug, "Slirp: register poll FD %d\n", fd);
}
void SlirpCbUnregisterPollFD(int fd, void* opaque)
{
Log(LogLevel::Debug, "Slirp: unregister poll FD %d\n", fd);
}
void SlirpCbNotify(void* opaque)
{
Log(LogLevel::Debug, "Slirp: notify???\n");
}
SlirpCb cb =
{
.send_packet = SlirpCbSendPacket,
.guest_error = SlirpCbGuestError,
.clock_get_ns = SlirpCbClockGetNS,
.timer_new = SlirpCbTimerNew,
.timer_free = SlirpCbTimerFree,
.timer_mod = SlirpCbTimerMod,
.register_poll_fd = SlirpCbRegisterPollFD,
.unregister_poll_fd = SlirpCbUnregisterPollFD,
.notify = SlirpCbNotify
};
bool Init()
{
IPv4ID = 0;
SlirpConfig cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.version = 1;
cfg.in_enabled = true;
*(u32*)&cfg.vnetwork = htonl(kSubnet);
*(u32*)&cfg.vnetmask = htonl(0xFFFFFF00);
*(u32*)&cfg.vhost = htonl(kServerIP);
cfg.vhostname = "melonServer";
*(u32*)&cfg.vdhcp_start = htonl(kClientIP);
*(u32*)&cfg.vnameserver = htonl(kDNSIP);
Ctx = slirp_new(&cfg, &cb, nullptr);
return true;
}
void DeInit()
{
if (Ctx)
{
slirp_cleanup(Ctx);
Ctx = nullptr;
}
}
void FinishUDPFrame(u8* data, int len)
{
u8* ipheader = &data[0xE];
u8* udpheader = &data[0x22];
// lengths
*(u16*)&ipheader[2] = htons(len - 0xE);
*(u16*)&udpheader[4] = htons(len - (0xE + 0x14));
// IP checksum
u32 tmp = 0;
for (int i = 0; i < 20; i += 2)
tmp += ntohs(*(u16*)&ipheader[i]);
while (tmp >> 16)
tmp = (tmp & 0xFFFF) + (tmp >> 16);
tmp ^= 0xFFFF;
*(u16*)&ipheader[10] = htons(tmp);
// UDP checksum
// (note: normally not mandatory, but some older sgIP versions require it)
tmp = 0;
tmp += ntohs(*(u16*)&ipheader[12]);
tmp += ntohs(*(u16*)&ipheader[14]);
tmp += ntohs(*(u16*)&ipheader[16]);
tmp += ntohs(*(u16*)&ipheader[18]);
tmp += ntohs(0x1100);
tmp += (len-0x22);
for (u8* i = udpheader; i < &udpheader[len-0x23]; i += 2)
tmp += ntohs(*(u16*)i);
if (len & 1)
tmp += ntohs((u_short)udpheader[len-0x23]);
while (tmp >> 16)
tmp = (tmp & 0xFFFF) + (tmp >> 16);
tmp ^= 0xFFFF;
if (tmp == 0) tmp = 0xFFFF;
*(u16*)&udpheader[6] = htons(tmp);
}
void HandleDNSFrame(u8* data, int len)
{
u8* ipheader = &data[0xE];
u8* udpheader = &data[0x22];
u8* dnsbody = &data[0x2A];
u32 srcip = ntohl(*(u32*)&ipheader[12]);
u16 srcport = ntohs(*(u16*)&udpheader[0]);
u16 id = ntohs(*(u16*)&dnsbody[0]);
u16 flags = ntohs(*(u16*)&dnsbody[2]);
u16 numquestions = ntohs(*(u16*)&dnsbody[4]);
u16 numanswers = ntohs(*(u16*)&dnsbody[6]);
u16 numauth = ntohs(*(u16*)&dnsbody[8]);
u16 numadd = ntohs(*(u16*)&dnsbody[10]);
Log(LogLevel::Debug, "DNS: ID=%04X, flags=%04X, Q=%d, A=%d, auth=%d, add=%d\n",
id, flags, numquestions, numanswers, numauth, numadd);
// for now we only take 'simple' DNS requests
if (flags & 0x8000) return;
if (numquestions != 1 || numanswers != 0) return;
u8 resp[1024];
u8* out = &resp[0];
// ethernet
memcpy(out, &data[6], 6); out += 6;
memcpy(out, kServerMAC, 6); out += 6;
*(u16*)out = htons(0x0800); out += 2;
// IP
u8* resp_ipheader = out;
*out++ = 0x45;
*out++ = 0x00;
*(u16*)out = 0; out += 2; // total length
*(u16*)out = htons(IPv4ID); out += 2; IPv4ID++;
*out++ = 0x00;
*out++ = 0x00;
*out++ = 0x80; // TTL
*out++ = 0x11; // protocol (UDP)
*(u16*)out = 0; out += 2; // checksum
*(u32*)out = htonl(kDNSIP); out += 4; // source IP
*(u32*)out = htonl(srcip); out += 4; // destination IP
// UDP
u8* resp_udpheader = out;
*(u16*)out = htons(53); out += 2; // source port
*(u16*)out = htons(srcport); out += 2; // destination port
*(u16*)out = 0; out += 2; // length
*(u16*)out = 0; out += 2; // checksum
// DNS
u8* resp_body = out;
*(u16*)out = htons(id); out += 2; // ID
*(u16*)out = htons(0x8000); out += 2; // flags
*(u16*)out = htons(numquestions); out += 2; // num questions
*(u16*)out = htons(numquestions); out += 2; // num answers
*(u16*)out = 0; out += 2; // num authority
*(u16*)out = 0; out += 2; // num additional
u32 curoffset = 12;
for (u16 i = 0; i < numquestions; i++)
{
if (curoffset >= (len-0x2A)) return;
u8 bitlength = 0;
while ((bitlength = dnsbody[curoffset++]) != 0)
curoffset += bitlength;
curoffset += 4;
}
u32 qlen = curoffset-12;
if (qlen > 512) return;
memcpy(out, &dnsbody[12], qlen); out += qlen;
curoffset = 12;
for (u16 i = 0; i < numquestions; i++)
{
// assemble the requested domain name
u8 bitlength = 0;
char domainname[256] = ""; int o = 0;
while ((bitlength = dnsbody[curoffset++]) != 0)
{
if ((o+bitlength) >= 255)
{
// welp. atleast try not to explode.
domainname[o++] = '\0';
break;
}
strncpy(&domainname[o], (const char *)&dnsbody[curoffset], bitlength);
o += bitlength;
curoffset += bitlength;
if (dnsbody[curoffset] != 0)
domainname[o++] = '.';
else
domainname[o++] = '\0';
}
u16 type = ntohs(*(u16*)&dnsbody[curoffset]);
u16 cls = ntohs(*(u16*)&dnsbody[curoffset+2]);
printf("- q%d: %04X %04X %s", i, type, cls, domainname);
// get answer
struct addrinfo dns_hint;
struct addrinfo* dns_res;
u32 addr_res;
memset(&dns_hint, 0, sizeof(dns_hint));
dns_hint.ai_family = AF_INET; // TODO: other address types (INET6, etc)
if (getaddrinfo(domainname, "0", &dns_hint, &dns_res) == 0)
{
struct addrinfo* p = dns_res;
while (p)
{
struct sockaddr_in* addr = (struct sockaddr_in*)p->ai_addr;
addr_res = *(u32*)&addr->sin_addr;
printf(" -> %d.%d.%d.%d",
addr_res & 0xFF, (addr_res >> 8) & 0xFF,
(addr_res >> 16) & 0xFF, addr_res >> 24);
break;
p = p->ai_next;
}
}
else
{
printf(" shat itself :(");
addr_res = 0;
}
printf("\n");
curoffset += 4;
// TODO: betterer support
// (under which conditions does the C00C marker work?)
*(u16*)out = htons(0xC00C); out += 2;
*(u16*)out = htons(type); out += 2;
*(u16*)out = htons(cls); out += 2;
*(u32*)out = htonl(3600); out += 4; // TTL (hardcoded for now)
*(u16*)out = htons(4); out += 2; // address length
*(u32*)out = addr_res; out += 4; // address
}
u32 framelen = (u32)(out - &resp[0]);
if (framelen & 1) { *out++ = 0; framelen++; }
FinishUDPFrame(resp, framelen);
Net::RXEnqueue(resp, framelen);
}
int SendPacket(u8* data, int len)
{
if (!Ctx) return 0;
if (len > 2048)
{
Log(LogLevel::Error, "Net_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
u16 ethertype = ntohs(*(u16*)&data[0xC]);
if (ethertype == 0x800)
{
u8 protocol = data[0x17];
if (protocol == 0x11) // UDP
{
u16 dstport = ntohs(*(u16*)&data[0x24]);
if (dstport == 53 && htonl(*(u32*)&data[0x1E]) == kDNSIP) // DNS
{
HandleDNSFrame(data, len);
return len;
}
}
}
slirp_input(Ctx, data, len);
return len;
}
const int PollListMax = 64;
struct pollfd PollList[PollListMax];
int PollListSize;
int SlirpCbAddPoll(int fd, int events, void* opaque)
{
if (PollListSize >= PollListMax)
{
Log(LogLevel::Error, "slirp: POLL LIST FULL\n");
return -1;
}
int idx = PollListSize++;
u16 evt = 0;
if (events & SLIRP_POLL_IN) evt |= POLLIN;
if (events & SLIRP_POLL_OUT) evt |= POLLWRNORM;
#ifndef __WIN32__
// CHECKME
if (events & SLIRP_POLL_PRI) evt |= POLLPRI;
if (events & SLIRP_POLL_ERR) evt |= POLLERR;
if (events & SLIRP_POLL_HUP) evt |= POLLHUP;
#endif // !__WIN32__
PollList[idx].fd = fd;
PollList[idx].events = evt;
return idx;
}
int SlirpCbGetREvents(int idx, void* opaque)
{
if (idx < 0 || idx >= PollListSize)
return 0;
u16 evt = PollList[idx].revents;
int ret = 0;
if (evt & POLLIN) ret |= SLIRP_POLL_IN;
if (evt & POLLWRNORM) ret |= SLIRP_POLL_OUT;
if (evt & POLLPRI) ret |= SLIRP_POLL_PRI;
if (evt & POLLERR) ret |= SLIRP_POLL_ERR;
if (evt & POLLHUP) ret |= SLIRP_POLL_HUP;
return ret;
}
void RecvCheck()
{
if (!Ctx) return;
//if (PollListSize > 0)
{
u32 timeout = 0;
PollListSize = 0;
slirp_pollfds_fill(Ctx, &timeout, SlirpCbAddPoll, nullptr);
int res = poll(PollList, PollListSize, timeout);
slirp_pollfds_poll(Ctx, res<0, SlirpCbGetREvents, nullptr);
}
}
}

36
src/net/Net_Slirp.h Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef NET_SLIRP_H
#define NET_SLIRP_H
#include "types.h"
namespace Net_Slirp
{
using namespace melonDS;
bool Init();
void DeInit();
int SendPacket(u8* data, int len);
void RecvCheck();
}
#endif // NET_SLIRP_H

View File

@ -0,0 +1,162 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include "PacketDispatcher.h"
using namespace melonDS;
struct PacketHeader
{
u32 magic;
u32 senderID;
u32 headerLength;
u32 dataLength;
};
const u32 kPacketMagic = 0x4B504C4D;
PacketDispatcher::PacketDispatcher() : mutex(Platform::Mutex_Create())
{
instanceMask = 0;
memset(packetQueues, 0, sizeof(packetQueues));
}
PacketDispatcher::~PacketDispatcher()
{
Platform::Mutex_Free(mutex);
}
void PacketDispatcher::registerInstance(int inst)
{
Mutex_Lock(mutex);
instanceMask |= (1 << inst);
packetQueues[inst] = new PacketQueue();
Mutex_Unlock(mutex);
}
void PacketDispatcher::unregisterInstance(int inst)
{
Mutex_Lock(mutex);
instanceMask &= ~(1 << inst);
delete packetQueues[inst];
Mutex_Unlock(mutex);
}
void PacketDispatcher::clear()
{
Mutex_Lock(mutex);
for (int i = 0; i < 16; i++)
{
if (!(instanceMask & (1 << i)))
continue;
PacketQueue* queue = packetQueues[i];
queue->Clear();
}
Mutex_Unlock(mutex);
}
void PacketDispatcher::sendPacket(const void* header, int headerlen, const void* data, int datalen, int sender, u16 recv_mask)
{
if (!header) headerlen = 0;
if (!data) datalen = 0;
if ((!headerlen) && (!datalen)) return;
if ((sizeof(PacketHeader) + headerlen + datalen) >= 0x8000) return;
if (sender < 0 || sender > 16) return;
recv_mask &= instanceMask;
if (sender < 16) recv_mask &= ~(1 << sender);
if (!recv_mask) return;
PacketHeader phdr;
phdr.magic = kPacketMagic;
phdr.senderID = sender;
phdr.headerLength = headerlen;
phdr.dataLength = datalen;
int totallen = sizeof(phdr) + headerlen + datalen;
Mutex_Lock(mutex);
for (int i = 0; i < 16; i++)
{
if (!(recv_mask & (1 << i)))
continue;
PacketQueue* queue = packetQueues[i];
// if we run out of space: discard old packets
while (!queue->CanFit(totallen))
{
PacketHeader tmp;
queue->Read(&tmp, sizeof(tmp));
queue->Skip(tmp.headerLength + tmp.dataLength);
}
queue->Write(&phdr, sizeof(phdr));
if (headerlen) queue->Write(header, headerlen);
if (datalen) queue->Write(data, datalen);
}
Mutex_Unlock(mutex);
}
bool PacketDispatcher::recvPacket(void *header, int *headerlen, void *data, int *datalen, int receiver)
{
if ((!header) && (!data)) return false;
if (receiver < 0 || receiver > 15) return false;
Mutex_Lock(mutex);
PacketQueue* queue = packetQueues[receiver];
PacketHeader phdr;
if (!queue->Read(&phdr, sizeof(phdr)))
{
Mutex_Unlock(mutex);
return false;
}
if (phdr.magic != kPacketMagic)
{
Mutex_Unlock(mutex);
return false;
}
if (phdr.headerLength)
{
if (headerlen) *headerlen = phdr.headerLength;
if (header) queue->Read(header, phdr.headerLength);
else queue->Skip(phdr.headerLength);
}
if (phdr.dataLength)
{
if (datalen) *datalen = phdr.dataLength;
if (data) queue->Read(data, phdr.dataLength);
else queue->Skip(phdr.dataLength);
}
Mutex_Unlock(mutex);
return true;
}

View File

@ -0,0 +1,48 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef PACKETDISPATCHER_H
#define PACKETDISPATCHER_H
#include "Platform.h"
#include "types.h"
#include "FIFO.h"
using PacketQueue = melonDS::RingBuffer<0x8000>;
class PacketDispatcher
{
public:
PacketDispatcher();
~PacketDispatcher();
void registerInstance(int inst);
void unregisterInstance(int inst);
void clear();
void sendPacket(const void* header, int headerlen, const void* data, int datalen, int sender, melonDS::u16 recv_mask);
bool recvPacket(void* header, int* headerlen, void* data, int* datalen, int receiver);
private:
melonDS::Platform::Mutex* mutex;
melonDS::u16 instanceMask;
PacketQueue* packetQueues[16];
};
#endif // PACKETDISPATCHER_H

58
src/net/libslirp/.clang-format vendored Normal file
View File

@ -0,0 +1,58 @@
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
---
Language: Cpp
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false # although we like it, it creates churn
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: false # churn
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterStruct: false
AfterUnion: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: false
BreakStringLiterals: true
ColumnLimit: 80
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ?
MacroBlockEnd: '.*_END$'
MaxEmptyLinesToKeep: 2
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
UseTab: Never
...

11
src/net/libslirp/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
*.[aod]
*.gcda
*.gcno
*.gcov
*.lib
*.obj
/build/
/TAGS
/cscope*
/src/libslirp-version.h
/tags

110
src/net/libslirp/.gitlab-ci.yml vendored Normal file
View File

@ -0,0 +1,110 @@
image: fedora:latest
variables:
DEPS: meson ninja-build
gcc libasan liblsan libubsan pkg-config glib2-devel
mingw64-gcc mingw64-pkg-config mingw64-glib2
clang-analyzer git-core
before_script:
- dnf install -y $DEPS
- git fetch --tags https://gitlab.freedesktop.org/slirp/libslirp.git
- git describe
build:
script:
- meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1)
- ninja -C build scan-build
build-asan:
script:
- CFLAGS=-fsanitize=address meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- (cd build && ASAN_OPTIONS=detect_leaks=0 meson test) || (cat build/meson-logs/testlog.txt && exit 1)
build-lsan:
script:
- CFLAGS=-fsanitize=leak meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1)
build-usan:
script:
- CFLAGS=-fsanitize=undefined meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1)
fuzz:
parallel:
matrix:
- TARGET: [arp, ip-header, udp, udp-h, tftp, dhcp, icmp, tcp, tcp-h, ndp, ip6-header, udp6, udp6-h, tftp6, icmp6, tcp6, tcp6-h]
script:
- CC=clang CXX=clang++ meson build -Dllvm-fuzz=true || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- build/fuzzing/fuzz-$TARGET -seed=1234 -runs=1000000 fuzzing/IN_$TARGET
artifacts:
when: on_failure
paths:
- crash-*
- leak-*
- oom-*
- timeout-*
build-mingw64:
script:
- (mkdir buildw && cd buildw && mingw64-meson --werror) || (cat buildw/meson-logs/meson-log.txt && exit 1)
- ninja -C buildw
Coverity:
only:
refs:
- master
- coverity
script:
- dnf update -y
- dnf install -y curl clang
- curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64
--form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
- tar xfz /tmp/cov-analysis-linux64.tgz
- CC=clang meson build
- cov-analysis-linux64-*/bin/cov-build --dir cov-int ninja -C build
- tar cfz cov-int.tar.gz cov-int
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID "
integration-slirp4netns:
variables:
SLIRP4NETNS_VERSION: "v1.1.12"
# Consumed by `make benchmark`
BENCHMARK_IPERF3_DURATION: "10"
script:
# Install libslirp
- meson build
- ninja -C build install
# Register the path of libslirp.so.0
- echo /usr/local/lib64 >/etc/ld.so.conf.d/libslirp.conf
- ldconfig
# Install the dependencies of slirp4netns and its test suite
# TODO: install udhcpc for `slirp4netns/tests/test-slirp4netns-dhcp.sh` (currently skipped, due to lack of udhcpc)
- dnf install -y autoconf automake findutils iperf3 iproute iputils jq libcap-devel libseccomp-devel nmap-ncat util-linux
# Check whether the runner environment is configured correctly
- unshare -rn true || (echo Make sure you have relaxed seccomp and appamor && exit 1)
- unshare -rn ip tap add tap0 mode tap || (echo Make sure you have /dev/net/tun && exit 1)
# Install slirp4netns
- git clone https://github.com/rootless-containers/slirp4netns -b "${SLIRP4NETNS_VERSION}"
- cd slirp4netns
- ./autogen.sh
- ./configure
- make
- make install
- slirp4netns --version
# Run slirp4netns integration test
- make distcheck || (cat $(find . -name 'test-suite.log' ) && exit 1)
# Run benchmark test to ensure that libslirp can actually handle packets, with several MTU configurations
- make benchmark MTU=1500
- make benchmark MTU=512
- make benchmark MTU=65520

3
src/net/libslirp/.gitpublish vendored Normal file
View File

@ -0,0 +1,3 @@
[gitpublishprofile "default"]
base = master
to = slirp@lists.freedesktop.org

238
src/net/libslirp/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,238 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [4.8.0] - TODO
## Security
- tcp: Fix testing for last fragment
- tftp: Fix use-after-free
### Added
- Add support for Haiku !123
- ncsi: Add manufacturer's ID !122
- ncsi: Add Get Version ID command !122
- ncsi: Add out-of-band ethernet address !125
- ncsi: Add Mellanox Get Mac Address handler !125
- icmp6: Add echo request forwarding support
- Add fuzzing infrastructure
### Fixed
- Fix missing cleanups
- windows: Build fixes
- ipv6: Use target address from Neighbor Advertisement !129
- dns: Reject domain-search when any entry ends with ".."
- dns: Use localhost as dns when /etc/resolv.conf empty !130
- icmp: Handle ICMP packets as IPPROTO_IP on BSD !133
- eth: pad ethernet frames to 60 bytes #34
### Removed
- windows: Bump the minimum Windows version to Windows 7
## [4.7.0] - 2022-04-26
### Added
- Allow disabling the internal DHCP server !22
- icmp: Support falling back on trying a SOCK_RAW socket !92
- Support Unix sockets in hostfwd !103
- IPv6 DNS proxying support !110
- bootp: add support for UEFI HTTP boot !111
- New callback that supports CFI better !117
### Fixed
- dhcp: Always send DHCP_OPT_LEN bytes in options !97
- Fix Haiku build !98 !99
- Fix memory leak when using libresolv !100
- Ensure sin6_scope_id is zero for global addresses !102
- resolv: fix IPv6 resolution on Darwin !104
- socket: Initialize so_type in socreate !109
- Handle ECONNABORTED from recv !116
## [4.6.1] - 2021-06-18
### Fixed
- Fix DHCP regression introduced in 4.6.0. !95
## [4.6.0] - 2021-06-14
### Added
- mbuf: Add debugging helpers for allocation. !90
### Changed
- Revert "Set macOS deployment target to macOS 10.4". !93
### Fixed
- mtod()-related buffer overflows (CVE-2021-3592 #44, CVE-2021-3593 #45,
CVE-2021-3594 #47, CVE-2021-3595 #46).
- poll_fd: add missing fd registration for UDP and ICMP
- ncsi: make ncsi_calculate_checksum work with unaligned data. !89
- Various typos and doc fixes. !88
## [4.5.0] - 2021-05-18
### Added
- IPv6 forwarding. !62 !75 !77
- slirp_neighbor_info() to dump the ARP/NDP tables. !71
### Changed
- Lazy guest address resolution for IPv6. !81
- Improve signal handling when spawning a child. !61
- Set macOS deployment target to macOS 10.4. !72
- slirp_add_hostfwd: Ensure all error paths set errno. !80
- More API documentation.
### Fixed
- Assertion failure on unspecified IPv6 address. !86
- Disable polling for PRI on MacOS, fixing some closing streams issues. !73
- Various memory leak fixes on fastq/batchq. !68
- Memory leak on IPv6 fast-send. !67
- Slow socket response on Windows. !64
- Misc build and code cleanups. !60 !63 !76 !79 !84
## [4.4.0] - 2020-12-02
### Added
- udp, udp6, icmp: handle TTL value. !48
- Enable forwarding ICMP errors. !49
- Add DNS resolving for iOS. !54
### Changed
- Improve meson subproject() support. !53
- Removed Makefile-based build system. !56
### Fixed
- socket: consume empty packets. !55
- check pkt_len before reading protocol header (CVE-2020-29129). !57
- ip_stripoptions use memmove (fixes undefined behaviour). !47
- various Coverity-related changes/fixes.
## [4.3.1] - 2020-07-08
### Changed
- A silent truncation could occur in `slirp_fmt()`, which will now print a
critical message. See also #22.
### Fixed
- CVE-2020-10756 - Drop bogus IPv6 messages that could lead to data leakage.
See !44 and !42.
- Fix win32 builds by using the SLIRP_PACKED definition.
- Various coverity scan errors fixed. !41
- Fix new GCC warnings. !43
## [4.3.0] - 2020-04-22
### Added
- `SLIRP_VERSION_STRING` macro, with the git sha suffix when building from git
- `SlirpConfig.disable_dns`, to disable DNS redirection #16
### Changed
- `slirp_version_string()` now has the git sha suffix when building form git
- Limit DNS redirection to port 53 #16
### Fixed
- Fix build regression with mingw & NetBSD
- Fix use-afte-free in `ip_reass()` (CVE-2020-1983)
## [4.2.0] - 2020-03-17
### Added
- New API function `slirp_add_unix`: add a forward rule to a Unix socket.
- New API function `slirp_remove_guestfwd`: remove a forward rule previously
added by `slirp_add_exec`, `slirp_add_unix` or `slirp_add_guestfwd`
- New `SlirpConfig.outbound_addr{,6}` fields to bind output socket to a
specific address
### Changed
- socket: do not fallback on host loopback if `get_dns_addr()` failed
or the address is in slirp network
### Fixed
- ncsi: fix checksum OOB memory access
- `tcp_emu()`: fix OOB accesses
- tftp: restrict relative path access
- state: fix loading of guestfwd state
## [4.1.0] - 2019-12-02
### Added
- The `slirp_new()` API, simpler and more extensible than `slirp_init()`.
- Allow custom MTU configuration.
- Option to disable host loopback connections.
- CI now runs scan-build too.
### Changed
- Disable `tcp_emu()` by default. `tcp_emu()` is known to have caused
several CVEs, and not useful today in most cases. The feature can
be still enabled by setting `SlirpConfig.enable_emu` to true.
- meson build system is now `subproject()` friendly.
- Replace remaining `malloc()`/`free()` with glib (which aborts on OOM)
- Various code cleanups.
### Deprecated
- The `slirp_init()` API.
### Fixed
- `getpeername()` error after `shutdown(SHUT_WR)`.
- Exec forward: correctly parse command lines that contain spaces.
- Allow 0.0.0.0 destination address.
- Make host receive broadcast packets.
- Various memory related fixes (heap overflow, leaks, NULL
dereference).
- Compilation warnings, dead code.
## [4.0.0] - 2019-05-24
### Added
- Installable as a shared library.
- meson build system
(& make build system for in-tree QEMU integration)
### Changed
- Standalone project, removing any QEMU dependency.
- License clarifications.
[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.8.0...master
[4.8.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.7.0...v4.8.0
[4.7.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.1...v4.7.0
[4.6.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.0...v4.6.1
[4.6.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.5.0...v4.6.0
[4.5.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.4.0...v4.5.0
[4.4.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.1...v4.4.0
[4.3.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.0...v4.3.1
[4.3.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.2.0...v4.3.0
[4.2.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.1.0...v4.2.0
[4.1.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.0.0...v4.1.0
[4.0.0]: https://gitlab.freedesktop.org/slirp/libslirp/commits/v4.0.0

67
src/net/libslirp/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.16)
project(libslirp VERSION 4.8.0 LANGUAGES C)
set(SLIRP_MAJOR_VERSION "${libslirp_VERSION_MAJOR}")
set(SLIRP_MINOR_VERSION "${libslirp_VERSION_MINOR}")
set(SLIRP_MICRO_VERSION "${libslirp_VERSION_PATCH}")
set(SLIRP_VERSION_STRING "\"${libslirp_VERSION}\"")
set(SOURCES
src/arp_table.c
src/bootp.c
src/cksum.c
src/dhcpv6.c
src/dnssearch.c
src/if.c
src/ip6_icmp.c
src/ip6_input.c
src/ip6_output.c
src/ip_icmp.c
src/ip_input.c
src/ip_output.c
src/mbuf.c
src/misc.c
src/ncsi.c
src/ndp_table.c
src/sbuf.c
src/slirp.c
src/socket.c
src/state.c
src/stream.c
src/tcp_input.c
src/tcp_output.c
src/tcp_subr.c
src/tcp_timer.c
src/tftp.c
src/udp6.c
src/udp.c
src/util.c
src/version.c
src/vmstate.c
# glib shim
glib/glib.c
)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/libslirp-version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/libslirp-version.h")
add_library(slirp STATIC ${SOURCES})
target_compile_definitions(slirp PUBLIC LIBSLIRP_STATIC_BUILD)
target_include_directories(slirp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/glib")
target_include_directories(slirp PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_include_directories(slirp PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
target_compile_definitions(slirp PRIVATE BUILDING_LIBSLIRP)
target_compile_definitions(slirp PRIVATE "G_LOG_DOMAIN=\"Slirp\"")
if (WIN32)
target_link_libraries(slirp PRIVATE ws2_32 iphlpapi)
elseif(HAIKU)
target_Link_libraries(slirp PRIVATE network)
elseif(APPLE)
target_link_libraries(slirp PRIVATE resolv)
else()
set_source_files_properties(glib/glib.c PROPERTIES COMPILE_FLAGS -fvisibility=hidden)
endif()

62
src/net/libslirp/COPYRIGHT vendored Normal file
View File

@ -0,0 +1,62 @@
Slirp was written by Danny Gasparovski.
Copyright (c), 1995,1996 All Rights Reserved.
Slirp is free software; "free" as in you don't have to pay for it, and you
are free to do whatever you want with it. I do not accept any donations,
monetary or otherwise, for Slirp. Instead, I would ask you to pass this
potential donation to your favorite charity. In fact, I encourage
*everyone* who finds Slirp useful to make a small donation to their
favorite charity (for example, GreenPeace). This is not a requirement, but
a suggestion from someone who highly values the service they provide.
The copyright terms and conditions:
---BEGIN---
Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---END---
This basically means you can do anything you want with the software, except
1) call it your own, and 2) claim warranty on it. There is no warranty for
this software. None. Nada. If you lose a million dollars while using
Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***.
If these conditions cannot be met due to legal restrictions (E.g. where it
is against the law to give out Software without warranty), you must cease
using the software and delete all copies you have.
Slirp uses code that is copyrighted by the following people/organizations:
Juha Pirkola.
Gregory M. Christy.
The Regents of the University of California.
Carnegie Mellon University.
The Australian National University.
RSA Data Security, Inc.
Please read the top of each source file for the details on the various
copyrights.

60
src/net/libslirp/README.md vendored Normal file
View File

@ -0,0 +1,60 @@
# libslirp
libslirp is a user-mode networking library used by virtual machines,
containers or various tools.
## Getting Started
### Prerequisites
A C compiler, meson and glib2 development libraries.
(see also [.gitlab-ci.yml](.gitlab-ci.yml) DEPS variable for the list
of dependencies on Fedora)
### Building
You may build and install the shared library with meson:
``` sh
meson build
ninja -C build install
```
And configure QEMU with --enable-slirp=system to link against it.
(QEMU may build with the submodule static library using --enable-slirp=git)
### Testing
Unfortunately, there are no automated tests available.
You may run QEMU ``-net user`` linked with your development version.
## Contributing
Feel free to open issues on the [project
issues](https://gitlab.freedesktop.org/slirp/libslirp/issues) page.
You may clone the [gitlab
project](https://gitlab.freedesktop.org/slirp/libslirp) and create a
merge request.
Contributing with gitlab allows gitlab workflow, tracking issues,
running CI etc.
Alternatively, you may send patches to slirp@lists.freedesktop.org
mailing list.
## Versioning
We intend to use [libtool's
versioning](https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html)
for the shared libraries and use [SemVer](http://semver.org/) for
project versions.
For the versions available, see the [tags on this
repository](https://gitlab.freedesktop.org/slirp/libslirp/releases).
## License
See the [COPYRIGHT](COPYRIGHT) file for details.

BIN
src/net/libslirp/fuzzing/IN_arp/arp.pcap vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
../IN_ndp/ndp.pcap

Binary file not shown.

View File

@ -0,0 +1 @@
../IN_udp/DNS_freedesktop_1-1-1-1.pcap

View File

@ -0,0 +1 @@
../IN_dhcp/dhcp.pkt

View File

@ -0,0 +1 @@
../IN_dhcp/dhcp_capture.pcap

View File

@ -0,0 +1 @@
../IN_icmp/icmp_capture.pcap

View File

@ -0,0 +1 @@
../IN_tcp/nc-10.0.2.2-8080.pcap

View File

@ -0,0 +1 @@
../IN_tcp/nc-ident.pcap

View File

@ -0,0 +1 @@
../IN_icmp/ping_10-0-2-2.pcap

View File

@ -0,0 +1 @@
../IN_tcp/tcp_qemucapt.pcap

View File

@ -0,0 +1 @@
../IN_tftp/tftp-get-blah.pkt

View File

@ -0,0 +1 @@
../IN_tftp/tftp_capture.pcap

View File

@ -0,0 +1 @@
../IN_tftp/tftp_get_libslirp-txt.pcap

View File

@ -0,0 +1 @@
../IN_udp6/DNS_freedesktop_1-1-1-1.pcap

View File

@ -0,0 +1 @@
../IN_icmp6/icmp_capture.pcap

View File

@ -0,0 +1 @@
../IN_icmp6/ping_10-0-2-2.pcap

View File

@ -0,0 +1 @@
../IN_tcp6/tcp_qemucapt.pcap

View File

@ -0,0 +1 @@
../IN_udp6/tftp_capture.pcap

View File

@ -0,0 +1 @@
../IN_udp6/tftp_get_libslirp-txt.pcap

BIN
src/net/libslirp/fuzzing/IN_ndp/ndp.pcap vendored Normal file

Binary file not shown.

1
src/net/libslirp/fuzzing/IN_tcp-d vendored Normal file
View File

@ -0,0 +1 @@
IN_tcp

1
src/net/libslirp/fuzzing/IN_tcp-h vendored Normal file
View File

@ -0,0 +1 @@
IN_tcp

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
src/net/libslirp/fuzzing/IN_tcp6-d vendored Normal file
View File

@ -0,0 +1 @@
IN_tcp6

1
src/net/libslirp/fuzzing/IN_tcp6-h vendored Normal file
View File

@ -0,0 +1 @@
IN_tcp6

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
src/net/libslirp/fuzzing/IN_udp-h vendored Normal file
View File

@ -0,0 +1 @@
IN_udp

Binary file not shown.

View File

@ -0,0 +1 @@
../IN_dhcp/dhcp.pkt

View File

@ -0,0 +1 @@
../IN_dhcp/dhcp_capture.pcap

View File

@ -0,0 +1 @@
../IN_tftp/tftp-get-blah.pkt

View File

@ -0,0 +1 @@
../IN_tftp/tftp_capture.pcap

View File

@ -0,0 +1 @@
../IN_tftp/tftp_get_libslirp-txt.pcap

1
src/net/libslirp/fuzzing/IN_udp6-h vendored Normal file
View File

@ -0,0 +1 @@
IN_udp6

Binary file not shown.

View File

@ -0,0 +1 @@
../IN_tftp6/tftp_capture.pcap

View File

@ -0,0 +1 @@
../IN_tftp6/tftp_get_libslirp-txt.pcap

59
src/net/libslirp/fuzzing/README.md vendored Normal file
View File

@ -0,0 +1,59 @@
# Fuzzing libslirp state and instructions
## Current state
We chose to use libFuzzer because of its custom mutator feature, which allows to keep coherent informations inside the packets being sent to libslirp. This ease the process of fuzzing as packets are less likely to be rejected early during processing them.
In the current state, the `meson.build` file is not compatible with the original one used by libSlirp main repository but it should be easy to merge them in a clean way. Also **in the current state, it seems that there is a memory leak inside the fuzzing code**, which make it run out of memory. The current goal is to find and get rid of this leak to allow fuzzing for longer without the process being interrupted because of it.
Six harness are currently available, more are to be added later to focus on other parts of the code :
- **fuzz-ip-header** : the mutator focuses on the ip header field informations,
- **fuzz-udp** : the mutator only work on udp packets, mutating the udp header and content, or only one or the other (-h,-d),
- **fuzz-tcp** : the mutator targets tcp packets, header+data or only one or the other, or only one or the other (-h,-d),
- **fuzz-icmp** : the mutator focuses on icmp packets,
These harness should be good starting examples on how to fuzz libslirp using libFuzzer.
## Running the fuzzer
Building the fuzzers/harness requires the use of clang as libFuzzer is part of LLVM.
You can build it running :
`CC=clang meson build && ninja -C build`
It will build the fuzzer in the ./build/fuzzing/ directory.
A script named `fuzzing/coverage.py` is available to generate coverage informations. **It makes a lot of assumptions on the directory structure** and should be read before use.
To run the fuzzer, simply run some of:
- `build/fuzzing/fuzz-ip-header fuzzing/IN_ip-header`
- `build/fuzzing/fuzz-udp fuzzing/IN_udp`
- `build/fuzzing/fuzz-udp-h fuzzing/IN_udp-h`
- `build/fuzzing/fuzz-tftp fuzzing/IN_tftp`
- `build/fuzzing/fuzz-dhcp fuzzing/IN_dhcp`
- `build/fuzzing/fuzz-icmp fuzzing/IN_icmp`
- `build/fuzzing/fuzz-tcp fuzzing/IN_tcp`
Your current directory should be a separate directory as crashes to it. New inputs found by the fuzzer will go directly in the `IN` folder.
# Adding new files to the corpus
In its current state, the fuzzing code is taking pcap files as input, we produced some using `tcpdump` on linux inside qemu with default settings.
Those files should be captured using the `EN10MB (Ethernet)` data link type, this can be set with the flag `-y` but it seems this can't be done while listening on all interfaces (`-i any`).
New files should give new coverage, to ensure a new file is usefull the `coverage.py` script (see next section) can be used to compare the coverage with and without that new file.
# Coverage
The `coverage.py` script allows to see coverage informations about the corpus. It makes a lot of assumptions on the directory structure so it should be read and probably modified before running it.
It must be called with the protocol to cover: `python coverage.py udp report`.
To generate coverage informations, the following flags are passed to the fuzzer and libslirp :
- g
- fsanitize-coverage=edge,indirect-calls,trace-cmp
- fprofile-instr-generate
- fcoverage-mapping
The last 2 arguments should also be passed to the linker.
Then the `llvm-profdata` and `llvm-cov` tools can be used to generate a report and a fancy set of HTML files with line-coverage informations.

37
src/net/libslirp/fuzzing/coverage.py vendored Normal file
View File

@ -0,0 +1,37 @@
from os import chdir,listdir,environ
from os.path import isfile,join,isdir
from subprocess import DEVNULL, run
import sys
ignored_files = "-ignore-filename-regex=glib -ignore-filename-regex=fuzz -ignore-filename-regex=helper -ignore-filename-regex=h$"
if __name__ == "__main__":
chdir("build/fuzzing/out")
available_targets = [exe for exe in listdir("../") if isfile(join("..", exe))]
available_corpus_path = [exe for exe in listdir("../../../fuzzing/") if isdir(join("../../../fuzzing/", exe))]
available_result_types = ["export", "show", "report"]
if len(sys.argv) != 4 or sys.argv[1] not in available_targets or sys.argv[2] not in available_corpus_path or sys.argv[3] not in available_result_types:
print("usage : python coverage.py fuzz_target IN_protol result_type")
print(" - available targets : ")
print(available_targets)
print(" - available_corpus_path : ")
print(available_corpus_path)
print(" - available result types : ")
print(available_result_types)
exit(0)
fuzzing_target = sys.argv[1]
corpus_path = "../../../fuzzing/"+sys.argv[2]+"/"
result_type = sys.argv[3]
if fuzzing_target in available_targets:
environ["LLVM_PROFILE_FILE"] = fuzzing_target + "_%p.profraw"
corpus = listdir(corpus_path)
for f in corpus:
#print(corpus_path+f)
run(["../" + fuzzing_target, corpus_path+f,"-detect_leaks=0"], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
run(["llvm-profdata merge -sparse " + fuzzing_target + "_*.profraw -o " + fuzzing_target + ".profdata"], shell=True)
if result_type == "export" :
run(["llvm-cov show ../" + fuzzing_target + " -format=html -output-dir=../report -instr-profile=" + fuzzing_target + ".profdata " + ignored_files], shell=True)
elif result_type == "show" :
run(["llvm-cov show ../" + fuzzing_target + " -instr-profile=" + fuzzing_target + ".profdata " + ignored_files], shell=True)
else:
run(["llvm-cov report ../" + fuzzing_target + " -instr-profile=" + fuzzing_target + ".profdata " + ignored_files], shell=True)

View File

@ -0,0 +1,2 @@
[libfuzzer]
max_len = 1024

35
src/net/libslirp/fuzzing/fuzz-main.c vendored Normal file
View File

@ -0,0 +1,35 @@
#include <glib.h>
#include <stdlib.h>
#define MIN_NUMBER_OF_RUNS 1
#define EXIT_TEST_SKIP 77
extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
int main(int argc, char **argv)
{
int i, j;
for (i = 1; i < argc; i++) {
GError *err = NULL;
char *name = argv[i];
char *buf;
size_t size;
if (!g_file_get_contents(name, &buf, &size, &err)) {
g_warning("Failed to read '%s': %s", name, err->message);
g_clear_error(&err);
return EXIT_FAILURE;
}
g_print("%s...\n", name);
for (j = 0; j < MIN_NUMBER_OF_RUNS; j++) {
if (LLVMFuzzerTestOneInput((void *)buf, size) == EXIT_TEST_SKIP) {
return EXIT_TEST_SKIP;
}
}
g_free(buf);
}
return EXIT_SUCCESS;
}

271
src/net/libslirp/fuzzing/helper.c vendored Normal file
View File

@ -0,0 +1,271 @@
#include "helper.h"
#include <glib.h>
#include <stdlib.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "slirp_base_fuzz.h"
#define MIN_NUMBER_OF_RUNS 1
#define EXIT_TEST_SKIP 77
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
struct in6_addr ip6_host;
struct in6_addr ip6_dns;
/// Function to compute the checksum of the ip header, should be compatible with
/// TCP and UDP checksum calculation too.
uint16_t compute_checksum(uint8_t *Data, size_t Size)
{
uint32_t sum = 0;
uint16_t *Data_as_u16 = (uint16_t *)Data;
for (size_t i = 0; i < Size / 2; i++) {
uint16_t val = ntohs(*(Data_as_u16 + i));
sum += val;
}
if (Size % 2 == 1)
sum += Data[Size - 1] << 8;
uint16_t carry = sum >> 16;
uint32_t sum_val = carry + (sum & 0xFFFF);
uint16_t result = (sum_val >> 16) + (sum_val & 0xFFFF);
return ~result;
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
/* FIXME: fail on some addr? */
return 0;
}
int listen(int sockfd, int backlog)
{
return 0;
}
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
/* FIXME: fail on some addr? */
return 0;
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
/* FIXME: partial send? */
return len;
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
/* FIXME: partial send? */
return len;
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
memset(buf, 0, len);
return len / 2;
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen)
{
memset(buf, 0, len);
memset(src_addr, 0, *addrlen);
return len / 2;
}
int setsockopt(int sockfd, int level, int optname, const void *optval,
socklen_t optlen)
{
return 0;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
static void empty_logging_func(const gchar *log_domain,
GLogLevelFlags log_level, const gchar *message,
gpointer user_data)
{
}
#endif
/* Disables logging for oss-fuzz. Must be used with each target. */
static void fuzz_set_logging_func(void)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
g_log_set_default_handler(empty_logging_func, NULL);
#endif
}
static ssize_t send_packet(const void *pkt, size_t pkt_len, void *opaque)
{
return pkt_len;
}
static int64_t clock_get_ns(void *opaque)
{
return 0;
}
static void *timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque)
{
return NULL;
}
static void timer_mod(void *timer, int64_t expire_timer, void *opaque)
{
}
static void timer_free(void *timer, void *opaque)
{
}
static void guest_error(const char *msg, void *opaque)
{
}
static void register_poll_fd(int fd, void *opaque)
{
}
static void unregister_poll_fd(int fd, void *opaque)
{
}
static void notify(void *opaque)
{
}
static const SlirpCb slirp_cb = {
.send_packet = send_packet,
.guest_error = guest_error,
.clock_get_ns = clock_get_ns,
.timer_new = timer_new,
.timer_mod = timer_mod,
.timer_free = timer_free,
.register_poll_fd = register_poll_fd,
.unregister_poll_fd = unregister_poll_fd,
.notify = notify,
};
#define MAX_EVID 1024
static int fake_events[MAX_EVID];
static int add_poll_cb(int fd, int events, void *opaque)
{
g_assert(fd < G_N_ELEMENTS(fake_events));
fake_events[fd] = events;
return fd;
}
static int get_revents_cb(int idx, void *opaque)
{
return fake_events[idx] & ~(SLIRP_POLL_ERR | SLIRP_POLL_HUP);
}
// Fuzzing strategy is the following :
// LLVMFuzzerTestOneInput :
// - build a slirp instance,
// - extract the packets from the pcap one by one,
// - send the data to `slirp_input`
// - call `slirp_pollfds_fill` and `slirp_pollfds_poll` to advance slirp
// - cleanup slirp when the whole pcap has been unwrapped.
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
Slirp *slirp = NULL;
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
struct in_addr fwd = { .s_addr = htonl(0x0a000205) }; /* 10.0.2.5 */
struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
struct in6_addr ip6_prefix;
int ret, vprefix6_len = 64;
const char *vhostname = NULL;
const char *tftp_server_name = NULL;
const char *tftp_export = "fuzzing/tftp";
const char *bootfile = NULL;
const char **dnssearch = NULL;
const char *vdomainname = NULL;
const pcap_hdr_t *hdr = (const void *)data;
const pcaprec_hdr_t *rec = NULL;
uint32_t timeout = 0;
if (size < sizeof(pcap_hdr_t)) {
return 0;
}
data += sizeof(*hdr);
size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
setenv("SLIRP_FUZZING", "1", 0);
fuzz_set_logging_func();
ret = inet_pton(AF_INET6, "fec0::", &ip6_prefix);
g_assert_cmpint(ret, ==, 1);
ip6_host = ip6_prefix;
ip6_host.s6_addr[15] |= 2;
ip6_dns = ip6_prefix;
ip6_dns.s6_addr[15] |= 3;
slirp =
slirp_init(false, true, net, mask, host, true, ip6_prefix, vprefix6_len,
ip6_host, vhostname, tftp_server_name, tftp_export, bootfile,
dhcp, dns, ip6_dns, dnssearch, vdomainname, &slirp_cb, NULL);
slirp_add_exec(slirp, "cat", &fwd, 1234);
for ( ; size > sizeof(*rec); data += rec->incl_len, size -= rec->incl_len) {
rec = (const void *)data;
data += sizeof(*rec);
size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
g_debug("unsupported rec->incl_len != rec->orig_len");
break;
}
if (rec->incl_len > size) {
break;
}
if (rec->incl_len >= 14) {
if (data[12] == 0x08 && data[13] == 0x00) {
/* IPv4 */
if (rec->incl_len >= 14 + 16) {
uint32_t ipsource = * (uint32_t*) (data + 14 + 12);
// This an answer, which we will produce, so don't receive
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
} else if (data[12] == 0x86 && data[13] == 0xdd) {
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (data + 14 + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
}
}
slirp_input(slirp, data, rec->incl_len);
slirp_pollfds_fill(slirp, &timeout, add_poll_cb, NULL);
slirp_pollfds_poll(slirp, 0, get_revents_cb, NULL);
}
slirp_cleanup(slirp);
return 0;
}

24
src/net/libslirp/fuzzing/helper.h vendored Normal file
View File

@ -0,0 +1,24 @@
#ifndef _HELPER_H
#define _HELPER_H
#ifdef _WIN32
/* as defined in sdkddkver.h */
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 /* Vista */
#endif
#include <ws2tcpip.h>
#endif
#include <stdlib.h>
#include <stdint.h>
#include <netinet/in.h>
#define PSEUDO_IP_SIZE (4*2 + 4)
#define PSEUDO_IPV6_SIZE (16*2 + 4)
uint16_t compute_checksum(uint8_t *Data, size_t Size);
extern struct in6_addr ip6_host;
extern struct in6_addr ip6_dns;
#endif /* _HELPER_H */

64
src/net/libslirp/fuzzing/meson.build vendored Normal file
View File

@ -0,0 +1,64 @@
extra_sources = []
extra_cargs = []
extra_ldargs = []
fuzzing_engine = []
extra_cargs += '-g'
if fuzzer_build
extra_cargs += '-fsanitize=fuzzer,address'
extra_cargs += '-fsanitize-coverage=edge,indirect-calls,trace-cmp'
extra_cargs += '-DCUSTOM_MUTATOR'
extra_cargs += '-fprofile-instr-generate'
extra_cargs += '-fcoverage-mapping'
extra_ldargs += '-fsanitize=fuzzer,address'
extra_ldargs += '-fprofile-instr-generate'
extra_ldargs += '-fcoverage-mapping'
endif
deps = [glib_dep, libslirp_dep, platform_deps]
exes = [
['fuzz-arp', ['slirp_fuzz_arp.c', 'helper.c']],
['fuzz-ip-header', ['slirp_fuzz_ip_header.c', 'helper.c']],
['fuzz-udp', ['slirp_fuzz_udp.c', 'helper.c']],
['fuzz-udp-h', ['slirp_fuzz_udp_header.c', 'helper.c']],
['fuzz-udp-d', ['slirp_fuzz_udp_data.c', 'helper.c']],
['fuzz-tftp', ['slirp_fuzz_udp_data.c', 'helper.c']],
['fuzz-dhcp', ['slirp_fuzz_udp_data.c', 'helper.c']],
['fuzz-tcp', ['slirp_fuzz_tcp.c', 'helper.c']],
['fuzz-tcp-h', ['slirp_fuzz_tcp_header.c', 'helper.c']],
['fuzz-tcp-d', ['slirp_fuzz_tcp_data.c', 'helper.c']],
['fuzz-icmp', ['slirp_fuzz_icmp.c', 'helper.c']],
['fuzz-ndp', ['slirp_fuzz_icmp6.c', 'helper.c']],
['fuzz-ip6-header', ['slirp_fuzz_ip6_header.c', 'helper.c']],
['fuzz-udp6', ['slirp_fuzz_udp6.c', 'helper.c']],
['fuzz-udp6-h', ['slirp_fuzz_udp6_header.c', 'helper.c']],
['fuzz-udp6-d', ['slirp_fuzz_udp6_data.c', 'helper.c']],
['fuzz-tftp6', ['slirp_fuzz_udp6_data.c', 'helper.c']],
['fuzz-tcp6', ['slirp_fuzz_tcp6.c', 'helper.c']],
['fuzz-tcp6-h', ['slirp_fuzz_tcp6_header.c', 'helper.c']],
['fuzz-tcp6-d', ['slirp_fuzz_tcp6_data.c', 'helper.c']],
['fuzz-icmp6', ['slirp_fuzz_icmp6.c', 'helper.c']],
]
if fuzzer_build
foreach exe : exes
executable(
exe[0], exe[1],
dependencies : deps,
c_args: extra_cargs,
link_args: extra_ldargs,
)
endforeach
endif
if fuzz_reproduce
executable(['reproducer', ['reproducer.c', 'helper.c']],
dependencies: deps,
c_args: extra_cargs,
link_args: extra_ldargs,
)
endif

48
src/net/libslirp/fuzzing/oss-fuzz.sh vendored Normal file
View File

@ -0,0 +1,48 @@
#!/bin/bash
set -ex
export CC=${CC:-clang}
export CXX=${CXX:-clang++}
export WORK=${WORK:-$(pwd)}
export OUT=${OUT:-$(pwd)/out}
build=$WORK/build
rm -rf $build
mkdir -p $build
mkdir -p $OUT
fuzzflag="oss-fuzz=true"
if [ -z "$FUZZING_ENGINE" ]; then
fuzzflag="llvm-fuzz=true"
fi
meson $build \
-D$fuzzflag \
-Db_lundef=false \
-Ddefault_library=static \
-Dstatic=true \
-Dbuildtype=debugoptimized
ninja -C $build
zip -jqr $OUT/fuzz-arp_seed_corpus.zip "$(dirname "$0")/IN_arp"
zip -jqr $OUT/fuzz-ip-header_seed_corpus.zip "$(dirname "$0")/IN_ip-header"
zip -jqr $OUT/fuzz-udp_seed_corpus.zip "$(dirname "$0")/IN_udp"
zip -jqr $OUT/fuzz-udp-h_seed_corpus.zip "$(dirname "$0")/IN_udp-h"
zip -jqr $OUT/fuzz-tftp_seed_corpus.zip "$(dirname "$0")/IN_tftp"
zip -jqr $OUT/fuzz-dhcp_seed_corpus.zip "$(dirname "$0")/IN_dhcp"
zip -jqr $OUT/fuzz-icmp_seed_corpus.zip "$(dirname "$0")/IN_icmp"
zip -jqr $OUT/fuzz-tcp_seed_corpus.zip "$(dirname "$0")/IN_tcp"
zip -jqr $OUT/fuzz-tcp-h_seed_corpus.zip "$(dirname "$0")/IN_tcp-h"
zip -jqr $OUT/fuzz-ndp_seed_corpus.zip "$(dirname "$0")/IN_ndp"
zip -jqr $OUT/fuzz-ip6-header_seed_corpus.zip "$(dirname "$0")/IN_ip6-header"
zip -jqr $OUT/fuzz-udp6_seed_corpus.zip "$(dirname "$0")/IN_udp6"
zip -jqr $OUT/fuzz-udp6-h_seed_corpus.zip "$(dirname "$0")/IN_udp6-h"
zip -jqr $OUT/fuzz-tftp6_seed_corpus.zip "$(dirname "$0")/IN_tftp6"
zip -jqr $OUT/fuzz-icmp6_seed_corpus.zip "$(dirname "$0")/IN_icmp6"
zip -jqr $OUT/fuzz-tcp6_seed_corpus.zip "$(dirname "$0")/IN_tcp6"
find $build -type f -executable -name "fuzz-*" -exec mv {} $OUT \;
find $build -type f -name "*.options" -exec mv {} $OUT \;

45
src/net/libslirp/fuzzing/reproducer.c vendored Normal file
View File

@ -0,0 +1,45 @@
#ifdef _WIN32
/* as defined in sdkddkver.h */
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 /* Vista */
#endif
#include <ws2tcpip.h>
#endif
#include <glib.h>
#include <stdlib.h>
#include "../src/libslirp.h"
#include "helper.h"
#define MIN_NUMBER_OF_RUNS 1
#define EXIT_TEST_SKIP 77
extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
int main(int argc, char **argv)
{
int i, j;
for (i = 1; i < argc; i++) {
GError *err = NULL;
char *name = argv[i];
char *buf;
size_t size;
if (!g_file_get_contents(name, &buf, &size, &err)) {
g_warning("Failed to read '%s': %s", name, err->message);
g_clear_error(&err);
return EXIT_FAILURE;
}
g_print("%s...\n", name);
for (j = 0; j < MIN_NUMBER_OF_RUNS; j++) {
if (LLVMFuzzerTestOneInput((void *)buf, size) == EXIT_TEST_SKIP) {
return EXIT_TEST_SKIP;
}
}
g_free(buf);
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,23 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
/* Structure for the fuzzers */
typedef struct pcap_hdr_s {
guint32 magic_number; /* magic number */
guint16 version_major; /* major version number */
guint16 version_minor; /* minor version number */
gint32 thiszone; /* GMT to local correction */
guint32 sigfigs; /* accuracy of timestamps */
guint32 snaplen; /* max length of captured packets, in octets */
guint32 network; /* data link type */
} pcap_hdr_t;
typedef struct pcaprec_hdr_s {
guint32 ts_sec; /* timestamp seconds */
guint32 ts_usec; /* timestamp microseconds */
guint32 incl_len; /* number of octets of packet saved in file */
guint32 orig_len; /* actual length of packet */
} pcaprec_hdr_t;

View File

@ -0,0 +1,90 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *arp_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
arp_data = Data_ptr + 14;
uint8_t Data_to_mutate[MaxSize];
uint16_t arp_size = rec->incl_len - 14;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the ip header, maybe the IPs or
// total length should be excluded ?
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, arp_data, arp_size);
// Call to libfuzzer's mutation function.
// For now we dont want to change the header size as it would require to
// resize the `Data` array to include the new bytes inside the whole
// packet.
// This should be easy as LibFuzzer probably does it by itself or
// reserved enough space in Data beforehand, needs some research to
// confirm.
// FIXME: allow up to grow header size to 60 bytes,
// requires to update the `header length` before calculating
// checksum
LLVMFuzzerMutate(Data_to_mutate, arp_size, arp_size);
// Copy the mutated data back to the `Data` array
memcpy(arp_data, Data_to_mutate, arp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,129 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not ICMP from the mutation strategy
if (ip_data[9] != IPPROTO_ICMP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_icmp = ip_data + ip_hl_in_bytes;
uint16_t total_length =
ntohs(*((uint16_t *)ip_data + 1)); // network order to host order
uint16_t icmp_size =
(total_length - ip_hl_in_bytes); /* total length -> is stored at the
offset 2 in the header */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (icmp_size > MaxSize || icmp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in icmp
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_icmp, icmp_size);
// Call to libfuzzer's mutation function.
// For now we dont want to change the header size as it would require to
// resize the `Data` array to include the new bytes inside the whole
// packet.
// This should be easy as LibFuzzer probably does it by itself or
// reserved enough space in Data beforehand, needs some research to
// confirm.
// FIXME: allow up to grow header size to 60 bytes,
// requires to update the `header length` before calculating
// checksum
LLVMFuzzerMutate(Data_to_mutate, icmp_size, icmp_size);
// Set the `checksum` field to 0 and calculate the new checksum
*(uint16_t *)(Data_to_mutate + 2) = 0;
uint16_t new_checksum =
compute_checksum(Data_to_mutate, icmp_size);
*(uint16_t *)(Data_to_mutate + 2) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_icmp, Data_to_mutate, icmp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,134 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not ICMP from the mutation strategy
if (ip_data[6] != IPPROTO_ICMPV6)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_icmp = ip_data + ip_hl_in_bytes;
uint16_t icmp_size = ntohs(*(uint16_t *)(ip_data + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (icmp_size > MaxSize || icmp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in icmp
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
memcpy(Data_to_mutate, start_of_icmp, icmp_size);
// Call to libfuzzer's mutation function.
// For now we dont want to change the header size as it would require to
// resize the `Data` array to include the new bytes inside the whole
// packet.
// This should be easy as LibFuzzer probably does it by itself or
// reserved enough space in Data beforehand, needs some research to
// confirm.
// FIXME: allow up to grow header size to 60 bytes,
// requires to update the `header length` before calculating
// checksum
LLVMFuzzerMutate(Data_to_mutate, icmp_size, icmp_size);
// Set the `checksum` field to 0 and calculate the new checksum
*(uint16_t *)(Data_to_mutate + 2) = 0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + icmp_size, ip_data + 8, 16*2);
*(Data_to_mutate + icmp_size + 16*2 + 1) = IPPROTO_ICMPV6;
*(Data_to_mutate + icmp_size + 16*2 + 2) = (uint8_t)(icmp_size / 256);
*(Data_to_mutate + icmp_size + 16*2 + 3) = (uint8_t)(icmp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, icmp_size + PSEUDO_IPV6_SIZE);
*(uint16_t *)(Data_to_mutate + 2) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_icmp, Data_to_mutate, icmp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,103 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the ip header, maybe the IPs or
// total length should be excluded ?
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, ip_data, ip_hl_in_bytes);
// Call to libfuzzer's mutation function.
// For now we dont want to change the header size as it would require to
// resize the `Data` array to include the new bytes inside the whole
// packet.
// This should be easy as LibFuzzer probably does it by itself or
// reserved enough space in Data beforehand, needs some research to
// confirm.
// FIXME: allow up to grow header size to 60 bytes,
// requires to update the `header length` before calculating
// checksum
LLVMFuzzerMutate(Data_to_mutate, ip_hl_in_bytes, ip_hl_in_bytes);
// Copy the mutated data back to the `Data` array
memcpy(ip_data, Data_to_mutate, ip_hl_in_bytes);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,112 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > MaxSize || ip_hl_in_bytes > rec->incl_len - 14)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the ip header, maybe the IPs or
// total length should be excluded ?
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, ip_data, ip_hl_in_bytes);
// Call to libfuzzer's mutation function.
// For now we dont want to change the header size as it would require to
// resize the `Data` array to include the new bytes inside the whole
// packet.
// This should be easy as LibFuzzer probably does it by itself or
// reserved enough space in Data beforehand, needs some research to
// confirm.
// FIXME: allow up to grow header size to 60 bytes,
// requires to update the `header length` before calculating
// checksum
LLVMFuzzerMutate(Data_to_mutate, ip_hl_in_bytes, ip_hl_in_bytes);
// Set the `checksum` field to 0 and calculate the new checksum
*(uint16_t *)(Data_to_mutate + 10) = 0;
uint16_t new_checksum =
compute_checksum(Data_to_mutate, ip_hl_in_bytes);
*(uint16_t *)(Data_to_mutate + 10) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(ip_data, Data_to_mutate, ip_hl_in_bytes);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,138 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not TCP from the mutation strategy
if (ip_data[9] != IPPROTO_TCP)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IP_SIZE];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
uint16_t total_length = ntohs(*((uint16_t *)ip_data + 1));
uint16_t tcp_size = (total_length - (uint16_t)ip_hl_in_bytes);
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use tcp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the tcp packet
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IP_SIZE);
memcpy(Data_to_mutate, start_of_tcp, tcp_size);
// Call to libfuzzer's mutation function.
// Pass the whole TCP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the TCP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, tcp_size, tcp_size);
// Set the `checksum` field to 0 to calculate the new checksum
*(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + tcp_size, ip_data + 12, 4*2);
*(Data_to_mutate + tcp_size + 9) = IPPROTO_TCP;
*(Data_to_mutate + tcp_size + 10) = (uint8_t)(tcp_size / 256);
*(Data_to_mutate + tcp_size + 11) = (uint8_t)(tcp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IP_SIZE);
*(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_tcp, Data_to_mutate, tcp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,134 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not TCP from the mutation strategy
if (ip_data[6] != IPPROTO_TCP)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use tcp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the tcp packet
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
memcpy(Data_to_mutate, start_of_tcp, tcp_size);
// Call to libfuzzer's mutation function.
// Pass the whole TCP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the TCP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, tcp_size, tcp_size);
// Set the `checksum` field to 0 to calculate the new checksum
*(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2);
*(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP;
*(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256);
*(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE);
*(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_tcp, Data_to_mutate, tcp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,137 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not TCP from the mutation strategy
if (ip_data[6] != IPPROTO_TCP)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
uint8_t tcp_header_size = 4 * (*(start_of_tcp + 12) >> 4);
uint8_t *start_of_data = ip_data + ip_hl_in_bytes + tcp_header_size;
uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4));
uint16_t tcp_data_size = (tcp_size - (uint16_t)tcp_header_size);
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use tcp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (tcp_data_size > MaxSize || tcp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - tcp_header_size)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the tcp packet
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
memcpy(Data_to_mutate, start_of_tcp, tcp_size);
// Call to libfuzzer's mutation function.
// Pass the whole TCP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the TCP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate + tcp_header_size, tcp_data_size, tcp_data_size);
// Set the `checksum` field to 0 to calculate the new checksum
*(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2);
*(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP;
*(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256);
*(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE);
*(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_tcp, Data_to_mutate, tcp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,136 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not TCP from the mutation strategy
if (ip_data[6] != IPPROTO_TCP)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
uint8_t tcp_header_size = (*(start_of_tcp + 12) >> 4) * 4;
uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use tcp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes ||
tcp_header_size > MaxSize || tcp_header_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the tcp packet
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE);
memcpy(Data_to_mutate, start_of_tcp, tcp_size);
// Call to libfuzzer's mutation function.
// Pass the whole TCP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the TCP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, tcp_header_size, tcp_header_size);
// Set the `checksum` field to 0 to calculate the new checksum
*(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2);
*(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP;
*(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256);
*(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE);
*(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_tcp, Data_to_mutate, tcp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,141 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not TCP from the mutation strategy
if (ip_data[9] != IPPROTO_TCP)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IP_SIZE];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
uint8_t tcp_header_size = 4 * (*(start_of_tcp + 12) >> 4);
uint8_t *start_of_data = ip_data + ip_hl_in_bytes + tcp_header_size;
uint16_t total_length = ntohs(*((uint16_t *)ip_data + 1));
uint16_t tcp_size = (total_length - (uint16_t)ip_hl_in_bytes);
uint16_t tcp_data_size = (tcp_size - (uint16_t)tcp_header_size);
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use tcp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (tcp_data_size > MaxSize || tcp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - tcp_header_size)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the tcp packet
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IP_SIZE);
memcpy(Data_to_mutate, start_of_tcp, tcp_size);
// Call to libfuzzer's mutation function.
// Pass the whole TCP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the TCP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate + tcp_header_size, tcp_data_size, tcp_data_size);
// Set the `checksum` field to 0 to calculate the new checksum
*(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + tcp_size, ip_data + 12, 4*2);
*(Data_to_mutate + tcp_size + 9) = IPPROTO_TCP;
*(Data_to_mutate + tcp_size + 10) = (uint8_t)(tcp_size / 256);
*(Data_to_mutate + tcp_size + 11) = (uint8_t)(tcp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IP_SIZE);
*(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_tcp, Data_to_mutate, tcp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,140 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not TCP from the mutation strategy
if (ip_data[9] != IPPROTO_TCP)
continue;
// Allocate a bit more than needed, this is useful for
// checksum calculation.
uint8_t Data_to_mutate[MaxSize + PSEUDO_IP_SIZE];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes;
uint8_t tcp_header_size = (*(start_of_tcp + 12) >> 4) * 4;
uint16_t total_length = ntohs(*((uint16_t *)ip_data + 1));
uint16_t tcp_size = (total_length - (uint16_t)ip_hl_in_bytes);
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use tcp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes ||
tcp_header_size > MaxSize || tcp_header_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the tcp packet
memset(Data_to_mutate, 0, MaxSize + PSEUDO_IP_SIZE);
memcpy(Data_to_mutate, start_of_tcp, tcp_size);
// Call to libfuzzer's mutation function.
// Pass the whole TCP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the TCP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, tcp_header_size, tcp_header_size);
// Set the `checksum` field to 0 to calculate the new checksum
*(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0;
// Copy the source and destination IP addresses, the tcp length and
// protocol number at the end of the `Data_to_mutate` array to calculate
// the new checksum.
memcpy(Data_to_mutate + tcp_size, ip_data + 12, 4*2);
*(Data_to_mutate + tcp_size + 9) = IPPROTO_TCP;
*(Data_to_mutate + tcp_size + 10) = (uint8_t)(tcp_size / 256);
*(Data_to_mutate + tcp_size + 11) = (uint8_t)(tcp_size % 256);
uint16_t new_checksum =
compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IP_SIZE);
*(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum);
// Copy the mutated data back to the `Data` array
memcpy(start_of_tcp, Data_to_mutate, tcp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,121 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not UDP from the mutation strategy
if (ip_data[9] != IPPROTO_UDP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
uint16_t udp_size = ntohs(*(uint16_t *)(start_of_udp + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the udp packet
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_udp, udp_size);
// Call to libfuzzer's mutation function.
// Pass the whole UDP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the UDP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, udp_size, udp_size);
// Drop checksum
*(uint16_t *)(Data_to_mutate + 6) = 0;
// Copy the mutated data back to the `Data` array
memcpy(start_of_udp, Data_to_mutate, udp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,120 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not UDP from the mutation strategy
if (ip_data[6] != IPPROTO_UDP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the udp packet
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_udp, udp_size);
// Call to libfuzzer's mutation function.
// Pass the whole UDP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the UDP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, udp_size, udp_size);
// Drop checksum
// Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't
// check that actually
*(uint16_t *)(Data_to_mutate + 6) = 0;
// Copy the mutated data back to the `Data` array
memcpy(start_of_udp, Data_to_mutate, udp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,122 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not UDP from the mutation strategy
if (ip_data[6] != IPPROTO_UDP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
uint8_t udp_header_size = 8;
uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4));
uint16_t udp_data_size = (udp_size - (uint16_t)udp_header_size);
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (udp_data_size > MaxSize || udp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - udp_header_size)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the udp packet
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_udp, udp_size);
// Call to libfuzzer's mutation function.
// Pass the whole UDP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the UDP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate + udp_header_size, udp_data_size, udp_data_size);
// Drop checksum
// Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't
// check that actually
*(uint16_t *)(Data_to_mutate + 6) = 0;
// Copy the mutated data back to the `Data` array
memcpy(start_of_udp, Data_to_mutate, udp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,121 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
// Exclude packets that are not UDP from the mutation strategy
if (ip_data[6] != IPPROTO_UDP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
uint8_t udp_header_size = 8;
uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the udp packet
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_udp, udp_size);
// Call to libfuzzer's mutation function.
// Pass the whole UDP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the UDP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, udp_header_size, udp_header_size);
// Drop checksum
// Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't
// check that actually
*(uint16_t *)(Data_to_mutate + 6) = 0;
// Copy the mutated data back to the `Data` array
memcpy(start_of_udp, Data_to_mutate, udp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,123 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not UDP from the mutation strategy
if (ip_data[9] != IPPROTO_UDP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
uint8_t udp_header_size = 8;
uint16_t udp_size = ntohs(*(uint16_t *)(start_of_udp + 4));
uint16_t udp_data_size = (udp_size - (uint16_t)udp_header_size);
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (udp_data_size > MaxSize || udp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - udp_header_size)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the udp packet
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_udp, udp_size);
// Call to libfuzzer's mutation function.
// Pass the whole UDP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the UDP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate + udp_header_size, udp_data_size, udp_data_size);
// Drop checksum
*(uint16_t *)(Data_to_mutate + 6) = 0;
// Copy the mutated data back to the `Data` array
memcpy(start_of_udp, Data_to_mutate, udp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

View File

@ -0,0 +1,122 @@
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include "../src/libslirp.h"
#include "helper.h"
#include "slirp_base_fuzz.h"
#ifdef CUSTOM_MUTATOR
extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed);
/// This is a custom mutator, this allows us to mutate only specific parts of
/// the input and fix the checksum so the packet isn't rejected for bad reasons.
extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed)
{
size_t current_size = Size;
uint8_t *Data_ptr = Data;
uint8_t *ip_data;
uint32_t ipsource;
bool mutated = false;
pcap_hdr_t *hdr = (void *)Data_ptr;
pcaprec_hdr_t *rec = NULL;
if (current_size < sizeof(pcap_hdr_t)) {
return 0;
}
Data_ptr += sizeof(*hdr);
current_size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) {
rec = (void *)Data_ptr;
Data_ptr += sizeof(*rec);
current_size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
return 0;
}
if (rec->incl_len > current_size) {
return 0;
}
if (rec->incl_len < 14 + 1) {
return 0;
}
ip_data = Data_ptr + 14;
if (rec->incl_len >= 14 + 16) {
ipsource = * (uint32_t*) (ip_data + 12);
// This an answer, which we will produce, so don't mutate
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
// Exclude packets that are not UDP from the mutation strategy
if (ip_data[9] != IPPROTO_UDP)
continue;
uint8_t Data_to_mutate[MaxSize];
uint8_t ip_hl = (ip_data[0] & 0xF);
uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (ip_hl_in_bytes > rec->incl_len - 14)
return 0;
uint8_t *start_of_udp = ip_data + ip_hl_in_bytes;
uint8_t udp_header_size = 8;
uint16_t udp_size = ntohs(*(uint16_t *)(start_of_udp + 4));
// The size inside the packet can't be trusted, if it is too big it can
// lead to heap overflows in the fuzzing code.
// Fixme : don't use udp_size inside the fuzzing code, maybe use the
// rec->incl_len and manually calculate the size.
if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes)
return 0;
// Copy interesting data to the `Data_to_mutate` array
// here we want to fuzz everything in the udp packet
memset(Data_to_mutate, 0, MaxSize);
memcpy(Data_to_mutate, start_of_udp, udp_size);
// Call to libfuzzer's mutation function.
// Pass the whole UDP packet, mutate it and then fix checksum value
// so the packet isn't rejected.
// The new size of the data is returned by LLVMFuzzerMutate.
// Fixme: allow to change the size of the UDP packet, this will require
// to fix the size before calculating the new checksum and change
// how the Data_ptr is advanced.
// Most offsets bellow should be good for when the switch will be
// done to avoid overwriting new/mutated data.
LLVMFuzzerMutate(Data_to_mutate, udp_header_size, udp_header_size);
// Drop checksum
*(uint16_t *)(Data_to_mutate + 6) = 0;
// Copy the mutated data back to the `Data` array
memcpy(start_of_udp, Data_to_mutate, udp_size);
mutated = true;
}
if (!mutated)
return 0;
return Size;
}
#endif // CUSTOM_MUTATOR

BIN
src/net/libslirp/fuzzing/tftp/toto vendored Normal file

Binary file not shown.

132
src/net/libslirp/glib/glib.c vendored Normal file
View File

@ -0,0 +1,132 @@
//
// Created by nhp on 14-05-24.
//
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <stdarg.h>
#include "glib.h"
GString* g_string_new(gchar* initial) {
GString* str = g_new0(GString, 1);
if (initial != NULL) {
int len = strlen(initial);
gchar* p = malloc(len);
memcpy(p, initial, len);
str->str = p;
str->len = len;
str->allocated_len = len;
} else {
gchar* p = malloc(64);
str->str = p;
str->len = 0;
str->allocated_len = 64;
}
return str;
}
gchar* g_string_free(GString* str, gboolean free_segment) {
char* seg = str->str;
free(str);
if (free_segment) {
free(str->str);
return NULL;
}
return seg;
}
void g_string_append_printf(GString* str, const gchar* format, ...) {
va_list args;
va_start(args, format);
int need_len = vsnprintf(NULL, 0, format, args);
va_end(args);
if (str->len + need_len + 1 < str->allocated_len) {
gsize new_len = str->len + need_len + 1;
gchar* newp = realloc(str->str, new_len);
str->str = newp;
str->allocated_len = new_len;
str->len = new_len - 1;
}
gchar* temp = malloc(need_len + 1);
va_start(args, format);
vsnprintf(temp, need_len, format, args);
va_end(args);
strcat(str->str, temp);
free(temp);
}
gchar* g_strstr_len(const gchar* haystack, int len, const gchar* needle) {
if (len == -1) return strstr(haystack, needle);
size_t needle_len = strlen(needle);
for (int i = 0; i < len; i++) {
size_t found = 0;
for (int j = i; j < len; j++) {
if (haystack[j] == needle[j - i]) found++;
else break;
if (found == needle_len) return (gchar*) haystack + j;
}
}
return NULL;
}
gchar* g_strdup(const gchar* str) {
if (str == NULL) return NULL;
else return strdup(str);
}
int g_strv_length(GStrv strings) {
gint count = 0;
while (strings[count])
count++;
return count;
}
void g_strfreev(GStrv strings) {
for (int i = 0; strings[i] != NULL; i++) {
free(strings[i]);
}
}
// This is not good but all we're using slirp for is beaming pokemans over the internet so it's probably okay
gint g_rand_int_range(GRand* grand, gint min, gint max) {
double r = (double) rand();
double range = (double) (max - min);
double r2 = (r / (double) RAND_MAX) * range;
return MIN(max, ((int) r2) + min);
}
GRand* g_rand_new() {
return malloc(sizeof(GRand));
}
void g_rand_free(GRand* rand) {
free(rand);
}
void g_error_free(GError* error) {
free(error);
}
gboolean g_shell_parse_argv(const gchar* command_line, gint* argcp, gchar*** argvp, GError** error) {
const gchar* message = "Unimplemented.";
GError* err = malloc(sizeof(GError));
err->message = message;
*error = err;
return false;
}
gboolean g_spawn_async_with_fds(const gchar *working_directory, gchar **argv,
gchar **envp, GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data, GPid *child_pid, gint stdin_fd,
gint stdout_fd, gint stderr_fd, GError **error)
{
return false;
}

162
src/net/libslirp/glib/glib.h vendored Normal file
View File

@ -0,0 +1,162 @@
#ifndef GLIB_SHIM_H
#define GLIB_SHIM_H
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stddef.h>
#if defined(WIN32) || defined(_WIN32) || defined(_MSC_VER)
#define G_OS_WIN32 1
#endif
#define G_LITTLE_ENDIAN 0
#define G_BIG_ENDIAN 1
#define G_BYTE_ORDER G_LITTLE_ENDIAN
#define GUINT16_FROM_BE(n) ntohs(n)
#define GUINT16_TO_BE(n) htons(n)
#define GUINT32_FROM_BE(n) ntohl(n)
#define GUINT32_TO_BE(n) htonl(n)
#define GINT16_TO_BE(n) (int16_t) htons(n)
#define GINT16_FROM_BE(n) (int16_t) ntohs(n)
#define GINT32_TO_BE(n) (int32_t) htonl(n)
#define GINT32_FROM_BE(n) (int32_t) ntohl(n)
#define G_N_ELEMENTS(arr) (sizeof(arr) / sizeof(arr[0]))
#define G_GNUC_PRINTF(x, y)
#define GLIB_CHECK_VERSION(x, y, z) 1
#define G_STATIC_ASSERT(...)
#define g_assert assert
#define G_UNLIKELY(x) __builtin_expect(x, 0)
#define g_return_if_fail(expr) \
do { \
if (!(expr)) \
return; \
} while (false)
#define g_return_val_if_fail(expr, val) \
do { \
if (!(expr)) \
return (val); \
} while (false)
#define g_warn_if_reached() \
do { \
g_warning("g_warn_if_reached: Reached " __FILE__ ":%d", __LINE__); \
} while (false)
#define g_warn_if_fail(expr) \
do { \
if (!(expr)) \
g_warning("g_warn_if_fail: Expression '" #expr "' failed at " __FILE__ ":%d", __LINE__); \
} while (false)
#define g_assert_not_reached() \
do { \
assert(false && "g_assert_not_reached"); \
__builtin_unreachable(); \
} while (false)
#define GLIB_SIZEOF_VOID_P 8
#ifndef MAX
#define MAX(a, b) (a > b ? a : b)
#endif
#ifndef MIN
#define MIN(a, b) (a < b ? a : b)
#endif
#ifndef TRUE
#define TRUE true
#endif
#ifndef FALSE
#define FALSE false
#endif
typedef bool gboolean;
typedef char gchar;
typedef int gint;
typedef size_t gsize;
typedef void* gpointer;
#define g_debug(format, ...) printf("[" G_LOG_DOMAIN ": debug] " format, ##__VA_ARGS__)
#define g_warning(format, ...) printf("[" G_LOG_DOMAIN ": warning] " format, ##__VA_ARGS__)
#define g_error(format, ...) printf("[" G_LOG_DOMAIN ": error] " format, ##__VA_ARGS__)
#define g_critical(format, ...) printf("[" G_LOG_DOMAIN ": critical] " format, ##__VA_ARGS__)
#define g_new(type, count) (type*) (count > 0 ? malloc(sizeof(type) * count) : NULL)
#define g_new0(type, count) (type*) (count > 0 ? calloc(count, sizeof(type)) : NULL)
#define g_malloc malloc
#define g_malloc0(size) calloc(1, size)
#define g_realloc realloc
#define g_free free
#define g_getenv(var) getenv(var)
typedef struct GString {
gchar* str;
gsize len;
gsize allocated_len;
} GString;
typedef gchar** GStrv;
GString* g_string_new(gchar* initial);
gchar* g_string_free(GString* string, gboolean free_segment);
void g_string_append_printf(GString* gstr, const gchar* format, ...);
gchar* g_strstr_len(const gchar* haystack, int len, const gchar* needle);
gchar* g_strdup(const gchar* str);
#ifdef _MSC_VER
#define g_ascii_strcasecmp(a, b) stricmp(a, b)
#else
#define g_ascii_strcasecmp(a, b) strcasecmp(a, b)
#endif
#define g_str_has_prefix(str, pfx) (strncmp(str, pfx, strlen(pfx)) == 0)
#define g_snprintf snprintf
#define g_vsnprintf vsnprintf
gint g_strv_length(GStrv strings);
void g_strfreev(GStrv strings);
typedef uint32_t GRand;
gint g_rand_int_range(GRand* grand, gint min, gint max);
GRand* g_rand_new();
void g_rand_free(GRand* rand);
typedef struct GError {
const gchar* message;
} GError;
void g_error_free(GError* error);
#define g_strerror(err) strerror(err)
typedef void (*GSpawnChildSetupFunc)(gpointer ptr);
typedef enum GSpawnFlags {
G_SPAWN_SEARCH_PATH
} GSpawnFlags;
typedef gint GPid;
gboolean g_shell_parse_argv(const gchar* command_line, gint* argcp, gchar*** argvp, GError** error);
gboolean g_spawn_async_with_fds(const gchar *working_directory, gchar **argv,
gchar **envp, GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data, GPid *child_pid, gint stdin_fd,
gint stdout_fd, gint stderr_fd, GError **error);
typedef struct { gchar* key; int value; } GDebugKey;
#define g_parse_debug_string(str, keys, nkeys) 0
#endif

248
src/net/libslirp/meson.build vendored Normal file
View File

@ -0,0 +1,248 @@
project('libslirp', 'c',
version : '4.8.0',
license : 'BSD-3-Clause',
default_options : ['warning_level=1', 'c_std=gnu99'],
meson_version : '>= 0.50',
)
version = meson.project_version()
varr = version.split('.')
major_version = varr[0]
minor_version = varr[1]
micro_version = varr[2]
conf = configuration_data()
conf.set('SLIRP_MAJOR_VERSION', major_version)
conf.set('SLIRP_MINOR_VERSION', minor_version)
conf.set('SLIRP_MICRO_VERSION', micro_version)
want_ossfuzz = get_option('oss-fuzz')
want_libfuzzer = get_option('llvm-fuzz')
fuzz_reproduce = get_option('fuzz-reproduce')
if want_ossfuzz and want_libfuzzer
error('only one of oss-fuzz and llvm-fuzz can be specified')
endif
fuzzer_build = want_ossfuzz or want_libfuzzer
if fuzzer_build and fuzz_reproduce
error('fuzzer build and reproducer build are mutually exclusive')
endif
cc = meson.get_compiler('c')
add_languages('cpp', required : fuzzer_build)
if get_option('static') == true
add_global_arguments('-static', language : 'c')
endif
if cc.get_argument_syntax() != 'msvc'
r = run_command('build-aux/git-version-gen',
'@0@/.tarball-version'.format(meson.current_source_dir()),
check : false)
full_version = r.stdout().strip()
if r.returncode() != 0 or full_version.startswith('UNKNOWN')
full_version = meson.project_version()
elif not full_version.startswith(meson.project_version())
error('meson.build project version @0@ does not match git-describe output @1@'
.format(meson.project_version(), full_version))
endif
else
full_version = meson.project_version()
endif
conf.set_quoted('SLIRP_VERSION_STRING', full_version + get_option('version_suffix'))
# libtool versioning - this applies to libslirp
#
# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details
#
# - If interfaces have been changed or added, but binary compatibility
# has been preserved, change:
# CURRENT += 1
# REVISION = 0
# AGE += 1
# - If binary compatibility has been broken (eg removed or changed
# interfaces), change:
# CURRENT += 1
# REVISION = 0
# AGE = 0
# - If the interface is the same as the previous version, but bugs are
# fixed, change:
# REVISION += 1
lt_current = 4
lt_revision = 0
lt_age = 4
lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision)
host_system = host_machine.system()
#glib_dep = dependency('glib-2.0', static : get_option('static'))
glib_dep = declare_dependency(
include_directories: [include_directories('glib', is_system: true)],
sources: ['glib/glib.c']
)
add_project_arguments(cc.get_supported_arguments('-Wmissing-prototypes', '-Wstrict-prototypes',
'-Wredundant-decls', '-Wundef', '-Wwrite-strings'),
language: 'c', native: false)
platform_deps = []
if host_system == 'windows'
platform_deps += [
cc.find_library('ws2_32'),
cc.find_library('iphlpapi')
]
elif host_system == 'darwin'
platform_deps += [
cc.find_library('resolv')
]
elif host_system == 'haiku'
platform_deps += [
cc.find_library('network')
]
endif
cargs = [
'-DG_LOG_DOMAIN="Slirp"',
'-DBUILDING_LIBSLIRP',
]
if cc.check_header('valgrind/valgrind.h')
cargs += [ '-DHAVE_VALGRIND=1' ]
endif
sources = [
'src/arp_table.c',
'src/bootp.c',
'src/cksum.c',
'src/dhcpv6.c',
'src/dnssearch.c',
'src/if.c',
'src/ip6_icmp.c',
'src/ip6_input.c',
'src/ip6_output.c',
'src/ip_icmp.c',
'src/ip_input.c',
'src/ip_output.c',
'src/mbuf.c',
'src/misc.c',
'src/ncsi.c',
'src/ndp_table.c',
'src/sbuf.c',
'src/slirp.c',
'src/socket.c',
'src/state.c',
'src/stream.c',
'src/tcp_input.c',
'src/tcp_output.c',
'src/tcp_subr.c',
'src/tcp_timer.c',
'src/tftp.c',
'src/udp.c',
'src/udp6.c',
'src/util.c',
'src/version.c',
'src/vmstate.c',
]
mapfile = 'src/libslirp.map'
vflag = []
vflag_test = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
if cc.has_link_argument('-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), 'src/libslirp.test.map'))
vflag += vflag_test
endif
if fuzzer_build
cargs += '-fsanitize-coverage=edge,indirect-calls,trace-cmp'
cargs += '-fsanitize=fuzzer-no-link,address'
cargs += '-fprofile-instr-generate'
cargs += '-fcoverage-mapping'
cargs += '-g'
cargs += '-DSLIRP_DEBUG'
vflag += '-fsanitize=fuzzer-no-link,address'
vflag += '-fsanitize-coverage=edge,indirect-calls,trace-cmp'
vflag += '-fprofile-instr-generate'
vflag += '-fcoverage-mapping'
endif
if fuzz_reproduce
cargs += '-DSLIRP_DEBUG'
cargs += '-g'
endif
install_devel = not meson.is_subproject()
configure_file(
input : 'src/libslirp-version.h.in',
output : 'libslirp-version.h',
install : install_devel,
install_dir : join_paths(get_option('includedir'), 'slirp'),
configuration : conf
)
if fuzzer_build or fuzz_reproduce
lib = static_library('slirp', sources,
c_args : cargs,
link_args : vflag,
link_depends : mapfile,
dependencies : [glib_dep, platform_deps],
)
else
lib = library('slirp', sources,
version : lt_version,
c_args : cargs,
link_args : vflag,
link_depends : mapfile,
dependencies : [glib_dep, platform_deps],
install : install_devel or get_option('default_library') == 'shared',
)
endif
pingtest = executable('pingtest', 'test/pingtest.c',
link_with: [ lib ],
c_args : cargs,
link_args : vflag,
include_directories: [ 'src' ],
dependencies : [ platform_deps ]
)
test('ping', pingtest)
ncsitest = executable('ncsitest', 'test/ncsitest.c',
link_with: [lib],
c_args : cargs,
link_args : vflag,
include_directories: ['src'],
dependencies: [glib_dep, platform_deps]
)
test('ncsi', ncsitest)
if install_devel
install_headers(['src/libslirp.h'], subdir : 'slirp')
pkg = import('pkgconfig')
pkg.generate(
version : version,
libraries : lib,
requires : [
'glib-2.0',
],
name : 'slirp',
description : 'User-space network stack',
filebase : 'slirp',
subdirs : 'slirp',
)
else
if get_option('default_library') == 'both'
lib = lib.get_static_lib()
endif
endif
libslirp_dep = declare_dependency(
link_with : lib,
include_directories : [include_directories('src'), include_directories('.')],
)
subdir('fuzzing')

Some files were not shown because too many files have changed in this diff Show More