From 50a98c40481c6d1df267f4631228e9a4d107fe74 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 16 Mar 2014 13:43:59 +0800 Subject: [PATCH] Tools/NetTest - Add a runner to test networking stack --- .gitignore | 3 + Tools/NetTest/Makefile | 4 +- Tools/NetTest/include/nettest.h | 2 + Tools/NetTest/main.c | 46 ++++++--- Tools/NetTest/mode_cmdline.c | 27 ++++++ Tools/NetTest/nic.c | 29 +++++- Tools/NetTest/tap.c | 34 ++++++- Tools/NetTest/vfs_shim.c | 93 +++--------------- Tools/NetTest_Runner/Makefile | 32 +++++++ Tools/NetTest_Runner/arp.c | 77 +++++++++++++++ Tools/NetTest_Runner/include/arp.h | 16 ++++ Tools/NetTest_Runner/include/common.h | 36 +++++++ Tools/NetTest_Runner/include/ip.h | 24 +++++ Tools/NetTest_Runner/include/link.h | 16 ++++ Tools/NetTest_Runner/include/net.h | 21 ++++ Tools/NetTest_Runner/include/stack.h | 21 ++++ Tools/NetTest_Runner/include/tcp.h | 20 ++++ Tools/NetTest_Runner/include/test.h | 16 ++++ Tools/NetTest_Runner/include/tests.h | 15 +++ Tools/NetTest_Runner/ip.c | 105 ++++++++++++++++++++ Tools/NetTest_Runner/link.c | 49 ++++++++++ Tools/NetTest_Runner/main.c | 121 +++++++++++++++++++++++ Tools/NetTest_Runner/net.c | 123 ++++++++++++++++++++++++ Tools/NetTest_Runner/stack.c | 132 ++++++++++++++++++++++++++ Tools/NetTest_Runner/tcp.c | 74 +++++++++++++++ Tools/NetTest_Runner/test_arp.c | 39 ++++++++ Tools/NetTest_Runner/test_tcp.c | 26 +++++ Tools/nativelib/Makefile | 6 +- Tools/nativelib/include/acess.h | 26 ++++- Tools/nativelib/include/threads_int.h | 5 +- Tools/nativelib/logging.c | 25 ++++- Tools/nativelib/threads.c | 94 +++++++++--------- Tools/nativelib/threads_int.c | 50 ++++++---- Tools/nativelib/time.c | 12 +++ 34 files changed, 1247 insertions(+), 172 deletions(-) create mode 100644 Tools/NetTest/mode_cmdline.c create mode 100644 Tools/NetTest_Runner/Makefile create mode 100644 Tools/NetTest_Runner/arp.c create mode 100644 Tools/NetTest_Runner/include/arp.h create mode 100644 Tools/NetTest_Runner/include/common.h create mode 100644 Tools/NetTest_Runner/include/ip.h create mode 100644 Tools/NetTest_Runner/include/link.h create mode 100644 Tools/NetTest_Runner/include/net.h create mode 100644 Tools/NetTest_Runner/include/stack.h create mode 100644 Tools/NetTest_Runner/include/tcp.h create mode 100644 Tools/NetTest_Runner/include/test.h create mode 100644 Tools/NetTest_Runner/include/tests.h create mode 100644 Tools/NetTest_Runner/ip.c create mode 100644 Tools/NetTest_Runner/link.c create mode 100644 Tools/NetTest_Runner/main.c create mode 100644 Tools/NetTest_Runner/net.c create mode 100644 Tools/NetTest_Runner/stack.c create mode 100644 Tools/NetTest_Runner/tcp.c create mode 100644 Tools/NetTest_Runner/test_arp.c create mode 100644 Tools/NetTest_Runner/test_tcp.c diff --git a/.gitignore b/.gitignore index a40bb698..d9d0812e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,10 @@ Tools/*/obj/ Tools/*/Makefile.BuildNum Tools/disktool Tools/nettest +Tools/nettest_runner Tools/img2sif +Tools/stdout.txt +Tools/stderr.txt *.udi UDI/drivers/*/bin diff --git a/Tools/NetTest/Makefile b/Tools/NetTest/Makefile index 4473e734..c7118d22 100644 --- a/Tools/NetTest/Makefile +++ b/Tools/NetTest/Makefile @@ -18,14 +18,14 @@ K_OBJ += vfs/acls.o vfs/io.o vfs/fs/devfs.o # Modules MODULES := IPStack # Local kernel soruces (same as above, but located in same directory as Makefile) -L_OBJ = vfs_shim.o nic.o tcpclient.o tcpserver.o helpers.o +L_OBJ = vfs_shim.o nic.o tcpclient.o tcpserver.o helpers.o mode_cmdline.o # Native Sources (compiled as usual) N_OBJ = main.o tap.o # Compilation Options CFLAGS := -Wall -std=gnu99 -g -Werror -O0 -pthread CPPFLAGS := -I include/ -I ../nativelib/include -K_CPPFLAGS := -I $(KERNEL_SRC)include -I $(MODULE_SRC) +K_CPPFLAGS := -I $(KERNEL_SRC)include -I $(MODULE_SRC) -I ../../Usermode/Libraries/ld-acess.so_src/include_exp/ LDFLAGS += -Wl,--defsym,__buildnum=$(BUILD_NUM) -g -L .. -lpthread -lnativelib BUILDINFO_OBJ := obj/$(TARGET)/buildinfo.o diff --git a/Tools/NetTest/include/nettest.h b/Tools/NetTest/include/nettest.h index 090f5d4e..47059934 100644 --- a/Tools/NetTest/include/nettest.h +++ b/Tools/NetTest/include/nettest.h @@ -15,12 +15,14 @@ extern int NativeNic_AddDev(char *Desc); extern int NetTest_AddAddress(const char *SetAddrString); extern void *NetTest_OpenTap(const char *Name); +extern void *NetTest_OpenUnix(const char *Name); extern size_t NetTest_WritePacket(void *Handle, size_t Size, const void *Data); extern size_t NetTest_ReadPacket(void *Handle, size_t MaxSize, void *Data); extern size_t NetTest_WriteStdout(const void *Data, size_t Size); extern void NetTest_Suite_Netcat(const char *Addr, int Port); +extern void NetTest_Suite_Cmdline(void); extern int Net_ParseAddress(const char *String, void *Addr); extern int Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port); diff --git a/Tools/NetTest/main.c b/Tools/NetTest/main.c index 95e70ab0..2aba0607 100644 --- a/Tools/NetTest/main.c +++ b/Tools/NetTest/main.c @@ -22,12 +22,16 @@ void PrintUsage(const char *ProgramName) fprintf(stderr, "\n"); fprintf(stderr, "Options:\n" - "-dev :\n" + "-dev ::\n" "-ip ,/\n" - "-route ,,\n" + "-route /,\n" "\n" - "Suites:\n" + "Commands:\n" "netcat \n" + "\n" + "Device Types:\n" + "tun - Linux TUN/TAP device (takes an optional name)\n" + "unix - Unix named pipe (datagram)\n" ); } @@ -51,9 +55,10 @@ int main(int argc, char *argv[]) for( int i = 1; i < argc; i ++ ) { - if( argv[i][0] != '-' ) + char *arg = argv[i];; + if( arg[0] != '-' ) { - if( strcmp(argv[i], "netcat") == 0 ) + if( strcmp(arg, "netcat") == 0 ) { if( argc-i != 3 ) { Log_Error("NetTest", "'netcat' "); @@ -64,34 +69,45 @@ int main(int argc, char *argv[]) NetTest_Suite_Netcat(argv[i+1], strtol(argv[i+2], NULL, 0)); i += 2; } + else if( strcmp(arg, "cmdline") == 0 ) + { + NetTest_Suite_Cmdline(); + } else { - Log_Error("NetTest", "Unknown suite name '%s'", argv[i]); + Log_Error("NetTest", "Unknown suite name '%s'", arg); PrintUsage(argv[0]); } } else { - if( strcmp(argv[i], "-dev") == 0 ) + if( strcmp(arg, "--help") == 0 ) + { + PrintUsage(0); + return 0; + } + else if( strcmp(arg, "-dev") == 0 ) { - if( ++i == argc ) { PrintUsage(argv[0]); return 1; } - rv = NativeNic_AddDev(argv[i]); + if( i+1 == argc ) { PrintUsage(argv[0]); return 1; } + rv = NativeNic_AddDev(argv[i+1]); if( rv ) { - Log_Error("NetTest", "Failed to add device %s", argv[i]); + Log_Error("NetTest", "Failed to add device %s", argv[i+1]); return -1; } + i ++; } - else if( strcmp(argv[i], "-ip") == 0 ) + else if( strcmp(arg, "-ip") == 0 ) { - if( ++i == argc ) { PrintUsage(argv[0]); return 1; } - // TODO: parse argument and poke ipstack - if( NetTest_AddAddress(argv[i]) ) { + if( i+1 == argc ) { PrintUsage(argv[0]); return 1; } + // Parse argument and poke ipstack + if( NetTest_AddAddress(argv[i+1]) ) { return -1; } + i ++; } else { - Log_Error("NetTest", "Unknown argument '%s'", argv[i]); + Log_Error("NetTest", "Unknown argument '%s'", arg); PrintUsage(argv[0]); return -1; } diff --git a/Tools/NetTest/mode_cmdline.c b/Tools/NetTest/mode_cmdline.c new file mode 100644 index 00000000..5a175172 --- /dev/null +++ b/Tools/NetTest/mode_cmdline.c @@ -0,0 +1,27 @@ +/* + */ +#include +#include +#include + +// === CODE === +void NetTest_Suite_Cmdline(void) +{ + char line[BUFSIZ]; + while( fgets(line, sizeof(line)-1, stdin) ) + { + const char *cmd = strtok(line, " "); + if(!cmd) + continue; + if( strcmp(cmd, "exit") == 0 ) { + return ; + } + else if( strcmp(cmd, "request") == 0 ) { + //const char *addr = strtok(NULL, " "); + + } + else { + fprintf(stderr, "ERROR: Unknown command '%s'\n", cmd); + } + } +} diff --git a/Tools/NetTest/nic.c b/Tools/NetTest/nic.c index 4effc007..b8429fa9 100644 --- a/Tools/NetTest/nic.c +++ b/Tools/NetTest/nic.c @@ -20,8 +20,8 @@ tIPStackBuffer *NativeNic_WaitForPacket(void *Ptr); // === GLOBALS === tIPStack_AdapterType gNativeNIC_AdapterType = { .Name = "NativeNIC", - .Type = 0, // TODO: Differentiate differnet wire protos and speeds - .Flags = 0, // TODO: IP checksum offloading, MAC checksum offloading etc + .Type = 0, + .Flags = 0, .SendPacket = NativeNic_SendPacket, .WaitForPacket = NativeNic_WaitForPacket }; @@ -30,12 +30,30 @@ tIPStack_AdapterType gNativeNIC_AdapterType = { int NativeNic_AddDev(char *DevDesc) { Uint8 macaddr[6]; + // Parse descriptor into mac address and path + // 123456789ABC:tun:ifname + // 123456789ABC:unix:/path + // 123456789ABC:file:/path + // 123456789ABC:tcp:host_desc char *colonpos = strchr(DevDesc, ':'); if( !colonpos ) return -1; if( UnHex(macaddr, 6, DevDesc) != 6 ) return -1; - void *ptr = NetTest_OpenTap(colonpos+1); + + char *class = colonpos + 1; + colonpos = strchr(class, ':'); + void *ptr; + if( strncmp(class, "tun:", 4) == 0 ) { + ptr = NetTest_OpenTap(colonpos+1); + } + else if( strncmp(class, "unix:", 5) == 0 ) { + ptr = NetTest_OpenUnix(colonpos+1); + } + else { + Log_Warning("NIC", "Unknown opener '%.*s'", colonpos-class, class); + ptr = NULL; + } if( !ptr ) return 1; IPStack_Adapter_Add(&gNativeNIC_AdapterType, ptr, macaddr); @@ -57,6 +75,9 @@ tIPStackBuffer *NativeNic_WaitForPacket(void *Ptr) tIPStackBuffer *ret = IPStack_Buffer_CreateBuffer(1); IPStack_Buffer_AppendSubBuffer(ret, len, 0, buf, NativeNic_int_FreePacket, Ptr); + + Debug_HexDump("NativeNic: RX", buf, len); + return ret; } @@ -71,6 +92,8 @@ int NativeNic_SendPacket(void *Ptr, tIPStackBuffer *Buffer) // Serialise into stack char buf[len]; IPStack_Buffer_GetData(Buffer, buf, len); + + Debug_HexDump("NativeNic: TX", buf, len); NetTest_WritePacket(Ptr, len, buf); return 0; diff --git a/Tools/NetTest/tap.c b/Tools/NetTest/tap.c index ebacf163..1531619b 100644 --- a/Tools/NetTest/tap.c +++ b/Tools/NetTest/tap.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include // === CODE === void *NetTest_OpenTap(const char *Name) @@ -49,9 +51,39 @@ void *NetTest_OpenTap(const char *Name) return (void*)(intptr_t)fd; } +void *NetTest_OpenUnix(const char *Path) +{ + int fd = socket(AF_UNIX, SOCK_DGRAM, 0); + struct sockaddr_un sa = {AF_UNIX, ""}; + struct sockaddr_un sa_local = {AF_UNIX, ""}; + strcpy(sa.sun_path, Path); + if( connect(fd, (struct sockaddr*)&sa, sizeof(sa)) ) { + perror("NetTest_OpenUnix - connect"); + close(fd); + return NULL; + } + if( bind(fd, (struct sockaddr*)&sa_local, sizeof(sa)) ) { + perror("NetTest_OpenUnix - bind"); + close(fd); + return NULL; + } + + { + char somenulls[] = { 0,0,0,0,0,0, 0,0,0,0,0, 0,0}; + write(fd, somenulls, sizeof(somenulls)); + } + + return (void*)(intptr_t)fd; +} + size_t NetTest_WritePacket(void *Handle, size_t Size, const void *Data) { - return write( (intptr_t)Handle, Data, Size); + int ret = write( (intptr_t)Handle, Data, Size); + if( ret < 0 ) { + perror("NetTest_WritePacket - write"); + return 0; + } + return ret; } size_t NetTest_ReadPacket(void *Handle, size_t MaxSize, void *Data) diff --git a/Tools/NetTest/vfs_shim.c b/Tools/NetTest/vfs_shim.c index 97ac4db1..6d420646 100644 --- a/Tools/NetTest/vfs_shim.c +++ b/Tools/NetTest/vfs_shim.c @@ -10,84 +10,6 @@ #include // === CODE === -int VFS_SelectNode(tVFS_Node *Node, int Type, tTime *Timeout, const char *Name) -{ - tThread *us = Proc_GetCurThread(); - - int ret = 0; - - Threads_ClearEvent(THREAD_EVENT_VFS); - - if( Type & VFS_SELECT_READ ) { - Node->ReadThreads = (void*)us; - if(Node->DataAvaliable) ret |= VFS_SELECT_READ; - } - if( Type & VFS_SELECT_WRITE ) { - Node->WriteThreads = (void*)us; - if(!Node->BufferFull) ret |= VFS_SELECT_WRITE; - } - if( Type & VFS_SELECT_ERROR ) { - Node->ErrorThreads = (void*)us; - if(Node->ErrorOccurred) ret |= VFS_SELECT_ERROR; - } - - if( !ret ) - { - // TODO: Timeout - Threads_WaitEvents(THREAD_EVENT_VFS); - } - - if( Type & VFS_SELECT_READ ) { - Node->ReadThreads = NULL; - if(Node->DataAvaliable) ret |= VFS_SELECT_READ; - } - if( Type & VFS_SELECT_WRITE ) { - Node->WriteThreads = NULL; - if(!Node->BufferFull) ret |= VFS_SELECT_WRITE; - } - if( Type & VFS_SELECT_ERROR ) { - Node->ErrorThreads = NULL; - if(Node->ErrorOccurred) ret |= VFS_SELECT_ERROR; - } - return ret; -} - -int VFS_MarkAvaliable(tVFS_Node *Node, BOOL bAvail) -{ - Node->DataAvaliable = bAvail; - if( Node->DataAvaliable && Node->ReadThreads ) - Threads_PostEvent( (void*)Node->ReadThreads, THREAD_EVENT_VFS ); - return 0; -} - -int VFS_MarkError(tVFS_Node *Node, BOOL bError) -{ - Node->ErrorOccurred = bError; - if( Node->ErrorOccurred && Node->ErrorThreads ) - Threads_PostEvent( (void*)Node->ErrorThreads, THREAD_EVENT_VFS ); - return 0; -} - -int VFS_MarkFull(tVFS_Node *Node, BOOL bError) -{ - Node->BufferFull = bError; - if( !Node->BufferFull && Node->WriteThreads ) - Threads_PostEvent( (void*)Node->WriteThreads, THREAD_EVENT_VFS ); - return 0; -} - - -#if 0 -int VFS_Open(const char *Path, Uint Flags) -{ - return -1; -} - -void VFS_Close(int FD) -{ -} -#endif - int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode) { const int maxfd = *Threads_GetMaxFD(); @@ -122,3 +44,18 @@ tVFS_Handle *VFS_GetHandle(int FD) return &handles[FD]; } + +int VFS_SetHandle(int FD, tVFS_Node *Node, int Mode) +{ + const int maxfd = *Threads_GetMaxFD(); + tVFS_Handle *handles = *Threads_GetHandlesPtr(); + if( !handles ) + return -1; + + if( FD < 0 || FD >= maxfd ) + return -1; + + handles[FD].Node = Node; + handles[FD].Mode = Mode; + return FD; +} diff --git a/Tools/NetTest_Runner/Makefile b/Tools/NetTest_Runner/Makefile new file mode 100644 index 00000000..e9575793 --- /dev/null +++ b/Tools/NetTest_Runner/Makefile @@ -0,0 +1,32 @@ + +OBJ := main.o net.o stack.o +OBJ += tcp.o +OBJ += ip.o arp.o +OBJ += link.o +OBJ += test_arp.o test_tcp.o +BIN := ../nettest_runner + +CFLAGS := -Wall -std=c99 +CPPFLAGS := -Iinclude +LIBS := + +OBJ := $(OBJ:%=obj/%) + +.PHONY: all clean + +all: $(BIN) + +clean: + $(RM) $(OBJ) $(BIN) + +$(BIN): $(OBJ) + @echo [CC] -o $@ + @$(CC) $(LINKFLAGS) -o $@ $(OBJ) $(LIBS) + +obj/%.o: %.c + @mkdir -p $(dir $@) + @echo [CC] -c -o $@ + @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< -MMD -MP + +-include $(OBJ:%.o=%.d) + diff --git a/Tools/NetTest_Runner/arp.c b/Tools/NetTest_Runner/arp.c new file mode 100644 index 00000000..308f83ee --- /dev/null +++ b/Tools/NetTest_Runner/arp.c @@ -0,0 +1,77 @@ +/* + */ +#include "arp.h" +#include "net.h" +#include +#include +#include "test.h" +#include "tests.h" + +// === CODE === +void ARP_SendRequest(int IfNum, const void *IPv4Addr) +{ + const uint8_t *addr = IPv4Addr; + uint8_t pkt[] = { + // Ethernet + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + HOST_MAC, + 0x08,0x06, + // ARP + 0x00,0x01, 0x08,0x00, + 6,4, 0,1, + HOST_MAC, + HOST_IP, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + addr[0],addr[1],addr[2],addr[3], + }; + Net_Send(IfNum, sizeof(pkt), pkt); +} + +void ARP_SendResponse(int IfNum, const void *IPv4Addr, const void *MacAddr) +{ + +} + +bool ARP_Pkt_IsResponse(size_t Len, const void *Packet, const void *ExpectedIP, const void *ExpectedMac) +{ + const uint8_t *pkt8 = Packet; + if( Len < 2*6+2 ) { + TEST_WARN("Undersized"); + return false; + } + // Ethernet ARP id + if( pkt8[12+0] != 0x08 || pkt8[12+1] != 0x06 ) { + TEST_WARN("Ethernet tag %02x %02x != 08 06", pkt8[12+0], pkt8[12+1]); + return false; + } + + // ARP HWtype/ProtoType + if( pkt8[14+0] != 0x00 || pkt8[14+1] != 0x01 || pkt8[14+2] != 0x08 || pkt8[14+3] != 0x00 ) { + TEST_WARN("ARP Types %02x %02x %02x %02x != 00 01 08 00", + pkt8[14+0], pkt8[14+1], pkt8[14+2], pkt8[14+3]); + return false; + } + // ARP Sizes (HW/Proto) and operation (Response) + if( pkt8[14+4] != 6 || pkt8[14+5] != 4 || pkt8[14+6] != 0 || pkt8[14+7] != 2 ) { + TEST_WARN("Sizes+op %02x %02x %02x %02x != 06 04 00 02", + pkt8[14+4], pkt8[14+5], pkt8[14+6], pkt8[14+7]); + return false; + } + + if( memcmp(pkt8+14+8, ExpectedMac, 6) != 0 ) + return false; + if( memcmp(pkt8+14+14, ExpectedIP, 4) != 0 ) + return false; + if( memcmp(pkt8+14+18, (char[]){HOST_MAC}, 6) != 0 ) + return false; + if( memcmp(pkt8+14+24, (char[]){HOST_IP}, 4) != 0 ) + return false; + + return true; +} + +bool ARP_Pkt_IsRequest(size_t Len, const void *Packet, const void *ExpectedIP) +{ + return false; +} + diff --git a/Tools/NetTest_Runner/include/arp.h b/Tools/NetTest_Runner/include/arp.h new file mode 100644 index 00000000..e9a9beac --- /dev/null +++ b/Tools/NetTest_Runner/include/arp.h @@ -0,0 +1,16 @@ +/* + */ +#ifndef _ARP_H_ +#define _ARP_H_ + +#include +#include + + +extern void ARP_SendRequest(int IfNum, const void *IPv4Addr); +extern void ARP_SendResponse(int IfNum, const void *IPv4Addr, const void *MacAddr); +extern bool ARP_Pkt_IsResponse(size_t Len, const void *Packet, const void *ExpectedIP, const void *ExpectedMac); +extern bool ARP_Pkt_IsRequest(size_t Len, const void *Packet, const void *ExpectedIP); + +#endif + diff --git a/Tools/NetTest_Runner/include/common.h b/Tools/NetTest_Runner/include/common.h new file mode 100644 index 00000000..6c102652 --- /dev/null +++ b/Tools/NetTest_Runner/include/common.h @@ -0,0 +1,36 @@ +/* + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include + +#define HOST_IP_STR "10.1.1.2" +#define HOST_IP 0x0A,0x01,0x01,0x02 +#define HOST_MAC 0x54,0x56,0x00,0x12,0x34,0x11 + +#define TEST_IP 0x0A,0x01,0x01,0x01 +#define TEST_MAC 0x54,0x56,0x00,0x12,0x34,0x56 + +#define BLOB(bytes) (const char[]){bytes} + +#if BIG_ENDIAN +# define htons(x) ((uint16_t)(x)) +# define htonl(x) ((uint32_t)(x)) +#else +static inline uint16_t htons(uint16_t x) { + return (x >> 8) | (x << 8); +} +static inline uint32_t htonl(uint32_t x) { + return ((x >> 24) & 0xFF) << 0 + |((x >> 16) & 0xFF) << 8 + |((x >> 8) & 0xFF) << 16 + |((x >> 0) & 0xFF) << 24; +} +#endif +#define ntohs(x) htons(x) +#define ntohl(x) htonl(x) + + +#endif + diff --git a/Tools/NetTest_Runner/include/ip.h b/Tools/NetTest_Runner/include/ip.h new file mode 100644 index 00000000..6f0eb152 --- /dev/null +++ b/Tools/NetTest_Runner/include/ip.h @@ -0,0 +1,24 @@ +/* + * ip.h + * - IP-layer Test Wrapper (v5/v6) + */ +#ifndef _IP_H_ +#define _IP_H_ + +#include +#include +#include + +#define IPPROTO_TCP 6 + +#define IP_CHECKSUM_START 0xFFFF + +extern uint16_t IP_Checksum(uint16_t Prev, size_t Length, const void *Data); + +extern void IP_Send(int IfNum, int AF, const void *Src, const void *Dst, uint8_t proto, + int BufCount, size_t BufLens[], const void *Bufs[]); + +extern bool IP_Pkt_Check(size_t len, const void *data, size_t *ofs, int AF, const void *Src, const void *Dst, uint8_t proto); + +#endif + diff --git a/Tools/NetTest_Runner/include/link.h b/Tools/NetTest_Runner/include/link.h new file mode 100644 index 00000000..28efc500 --- /dev/null +++ b/Tools/NetTest_Runner/include/link.h @@ -0,0 +1,16 @@ +/* + */ +#ifndef _LINK_H_ +#define _LINK_H_ + +#define ETHER_PROTO_IPV4 0x0800 + +#include +#include + +extern void Link_Send(int IfNum, const void *Src, const void *Dst, uint16_t Proto, int BufCount, size_t BufLens[], const void *Bufs[]); + +extern bool Link_Pkt_Check(size_t len, const void *data, size_t *ofs, const void *Src, const void *Dst, uint16_t Proto); + +#endif + diff --git a/Tools/NetTest_Runner/include/net.h b/Tools/NetTest_Runner/include/net.h new file mode 100644 index 00000000..e30ef935 --- /dev/null +++ b/Tools/NetTest_Runner/include/net.h @@ -0,0 +1,21 @@ +/* + * net.h + * - Network interface IO + */ +#ifndef _NET_H_ +#define _NET_H_ + +#include +#include + +#define MTU 1520 + +extern int Net_Open(int IfNum, const char *Path); +extern void Net_Close(int IfNum); + + +extern size_t Net_Receive(int IfNum, size_t MaxLen, void *DestBuf, unsigned int Timeout); +extern void Net_Send(int IfNum, size_t Length, const void *Buf); + +#endif + diff --git a/Tools/NetTest_Runner/include/stack.h b/Tools/NetTest_Runner/include/stack.h new file mode 100644 index 00000000..40c92d64 --- /dev/null +++ b/Tools/NetTest_Runner/include/stack.h @@ -0,0 +1,21 @@ +/* + * stack.h + * - Functions for interacting with the IPStack program + */ +#ifndef _STACK_H_ +#define _STACK_H_ + +#include + +extern void Stack_AddDevice(const char *Ident, const void *MacAddr); +extern void Stack_AddInterface(const char *Name, int AddrType, const void *Addr, int MaskBits); +extern void Stack_AddRoute(int Type, const void *Network, int MaskBits, const void *NextHop); + +extern void Stack_AddArg(const char *Fmt, ...); +extern int Stack_Start(const char *Subcommand); +extern void Stack_Kill(void); + +extern int Stack_SendCommand(const char *CommandString); + +#endif + diff --git a/Tools/NetTest_Runner/include/tcp.h b/Tools/NetTest_Runner/include/tcp.h new file mode 100644 index 00000000..a96d921a --- /dev/null +++ b/Tools/NetTest_Runner/include/tcp.h @@ -0,0 +1,20 @@ +/* + * tcp.h + * - TCP Test Wrapper + */ +#ifndef _TCP_H_ +#define _TCP_H_ + +#include +#include +#include + +#define TCP_FIN 0x01 +#define TCP_SYN 0x02 +#define TCP_RST 0x04 + +extern void TCP_Send(int IF, int AF, const void *IP, short sport, short dport, uint32_t seq, uint32_t ack, uint8_t flags, uint16_t window, size_t data_len, const void *data); +extern bool TCP_Pkt_Check(size_t len, const void *data, size_t *ofs, int AF, const void *IP, short sport, short dport, uint8_t flags); + +#endif + diff --git a/Tools/NetTest_Runner/include/test.h b/Tools/NetTest_Runner/include/test.h new file mode 100644 index 00000000..7a325860 --- /dev/null +++ b/Tools/NetTest_Runner/include/test.h @@ -0,0 +1,16 @@ +/* + */ +#ifndef _TEST_H_ +#define _TEST_H_ + +#define TEST_SETNAME(name) test_setname(name) +#define TEST_ASSERT(cnd) do{if(!(cnd)) {test_assertion_fail(__FILE__,__LINE__,"%s",#cnd);return false;}}while(0) +#define TEST_ASSERT_REL(a,r,b) do{long long a_val=(a),b_val=(b);if(!(a_val r b_val)) {test_assertion_fail(__FILE__,__LINE__,"%s(0x%llx)%s%s(%llx)",#a,a_val,#r,#b,b_val);return false;}}while(0) +#define TEST_WARN(msg...) test_message(__FILE__,__LINE__,msg) + +extern void test_setname(const char *name); +extern void test_message(const char *filename, int line, const char *msg, ...); +extern void test_assertion_fail(const char *filename, int line, const char *test, ...); + +#endif + diff --git a/Tools/NetTest_Runner/include/tests.h b/Tools/NetTest_Runner/include/tests.h new file mode 100644 index 00000000..bb167228 --- /dev/null +++ b/Tools/NetTest_Runner/include/tests.h @@ -0,0 +1,15 @@ +/* + * tests.h + * - List of tests used by main.c + */ +#ifndef _TESTS_H_ +#define _TESTS_H_ + +#include "common.h" +#include + +extern bool Test_ARP_Basic(void); +extern bool Test_TCP_Basic(void); + +#endif + diff --git a/Tools/NetTest_Runner/ip.c b/Tools/NetTest_Runner/ip.c new file mode 100644 index 00000000..bebcf739 --- /dev/null +++ b/Tools/NetTest_Runner/ip.c @@ -0,0 +1,105 @@ +/* + */ +#include "common.h" +#include "ip.h" +#include "link.h" +#include +#include +#include "test.h" + +// === STRUCTURES === +typedef struct { + uint8_t VerLen; + uint8_t DiffServices; + uint16_t TotalLength; + uint16_t Identifcation; + uint16_t FragmentInfo; // rsvd:dont:more:[13]offset/8 + uint8_t TTL; + uint8_t Protocol; + uint16_t HeaderChecksum; + uint8_t SrcAddr[4]; + uint8_t DstAddr[4]; + uint8_t Options[]; +} __attribute__((packed)) tIPv4Hdr; + +// === CODE === +uint16_t IP_Checksum(uint16_t Prev, size_t Length, const void *Data) +{ + const uint16_t *words = Data; + uint32_t ret = ~Prev; + for( int i = 0; i < Length/2; i ++ ) + { + ret += ntohs(*words); + words ++; + } + if( Length & 1 ) + ret += ntohs(*(uint8_t*)words); + + while( ret >> 16 ) + ret = (ret & 0xFFFF) + (ret >> 16); + + return ~ret; +} + +void IP_Send(int IfNum, int AF, const void *Src, const void *Dst, uint8_t proto, + int BufCount, size_t BufLens[], const void *Bufs[]) +{ + size_t total_length = 0; + + size_t out_buflens[BufCount+1]; + const void *out_bufs[BufCount+1]; + + for( int i = 0; i < BufCount; i ++ ) + { + total_length += BufLens[i]; + out_buflens[1+i] = BufLens[i]; + out_bufs[1+i] = Bufs[i]; + } + + if( AF == 4 ) + { + tIPv4Hdr hdr; + hdr.VerLen = (4 << 4) | (sizeof(hdr)/4); + hdr.DiffServices = 0; + hdr.TotalLength = htons(sizeof(hdr) + total_length); + hdr.Identifcation = 0; // TODO: WTF is this? + hdr.FragmentInfo = htons(0); + hdr.TTL = 18; // TODO: Progammable TTL + hdr.Protocol = proto; + hdr.HeaderChecksum = 0; + memcpy(hdr.SrcAddr, Src, 4); + memcpy(hdr.DstAddr, Dst, 4); + + hdr.HeaderChecksum = htons( IP_Checksum(IP_CHECKSUM_START, sizeof(hdr), &hdr) ); + + out_buflens[0] = sizeof(hdr); + out_bufs[0] = &hdr; + + // TODO: Don't hard-code MAC addresses + Link_Send(IfNum, BLOB(HOST_MAC), BLOB(TEST_MAC), ETHER_PROTO_IPV4, + 1+BufCount, out_buflens, out_bufs); + } + else { + TEST_WARN("Invalid AF(%i) in IP_Send", AF); + } +} + +bool IP_Pkt_Check(size_t len, const void *data, size_t *ofs_out, int AF, const void *Src, const void *Dst, uint8_t proto) +{ + size_t ofs; + if( AF == 4 ) { + if( !Link_Pkt_Check(len, data, &ofs, BLOB(TEST_MAC), BLOB(HOST_MAC), ETHER_PROTO_IPV4) ) + return false; + + tIPv4Hdr hdr; + memcpy(&hdr, (const uint8_t*)data + ofs, sizeof(hdr)); + TEST_ASSERT_REL(hdr.VerLen >> 4, ==, 4); + + return false; + } + else { + TEST_WARN("Invalid AF(%i) in IP_Pkt_Check", AF); + return false; + } +} + diff --git a/Tools/NetTest_Runner/link.c b/Tools/NetTest_Runner/link.c new file mode 100644 index 00000000..53dfde54 --- /dev/null +++ b/Tools/NetTest_Runner/link.c @@ -0,0 +1,49 @@ +/* + */ +#include "common.h" +#include "net.h" +#include "link.h" +#include "test.h" +#include +#include + +// === CODE === +void Link_Send(int IfNum, const void *Src, const void *Dst, uint16_t Proto, + int BufCount, size_t BufLens[], const void *Bufs[]) +{ + size_t total_len = 6+6+2; + for( int i = 0; i < BufCount; i ++ ) + total_len += BufLens[i]; + uint8_t *data = malloc(total_len); + + uint8_t *pos = data; + memcpy(pos, Dst, 6); pos += 6; + memcpy(pos, Src, 6); pos += 6; + *(uint16_t*)pos = htons(Proto); pos += 2; + + for( int i = 0; i < BufCount; i ++ ) + { + memcpy(pos, Bufs[i], BufLens[i]); + pos += BufLens[i]; + } + + Net_Send(IfNum, total_len, data); + + free(data); +} + +bool Link_Pkt_Check(size_t len, const void *data, size_t *ofs_out, + const void *Src, const void *Dst, uint16_t Proto) +{ + const uint8_t *data8 = data; + TEST_ASSERT_REL(len, >=, 6+6+2); + + if(Src) TEST_ASSERT( memcmp(data8+0, Src, 6) == 0 ); + if(Dst) TEST_ASSERT( memcmp(data8+6, Dst, 6) == 0 ); + + TEST_ASSERT_REL( ntohs(*(uint16_t*)(data8+12)), ==, Proto ); + + *ofs_out = 6+6+2; + return true; +} + diff --git a/Tools/NetTest_Runner/main.c b/Tools/NetTest_Runner/main.c new file mode 100644 index 00000000..83ab6530 --- /dev/null +++ b/Tools/NetTest_Runner/main.c @@ -0,0 +1,121 @@ +/* + */ +#include +#include +#include +#include "net.h" +#include "stack.h" +#include "tests.h" +#include "test.h" +#include +#include + +// === PROTOTYPES === + int ParseCommandline(int argc, char *argv[]); + +// === GLOBALS === +const char *gsTestName; + +// === CODE === +int main(int argc, char *argv[]) +{ + if( ParseCommandline(argc, argv) ) + return 1; + + typedef bool t_test(void); + t_test *tests[] = { + Test_ARP_Basic, + Test_TCP_Basic, + NULL + }; + + // TODO: Move to stack.c + FILE *fp; + fp = fopen("stdout.txt", "w"); fclose(fp); + fp = fopen("stderr.txt", "w"); fclose(fp); + + for(int i = 0; tests[i]; i ++ ) + { + Net_Open(0, "/tmp/acess2net"); + + Stack_AddDevice("/tmp/acess2net", (char[]){TEST_MAC}); + Stack_AddInterface("eth0", 4, (const char[]){TEST_IP}, 24); + Stack_AddRoute(4, "\0\0\0\0", 0, (const char[]){HOST_IP}); + if( Stack_Start("cmdline") ) + goto teardown; + + if( Net_Receive(0, 1, &argc, 1000) == 0 ) + goto teardown; + + if( tests[i]() ) + printf("%s: PASS\n", gsTestName); + else + printf("%s: FAIL\n", gsTestName); + + teardown: + Stack_Kill(); + Net_Close(0); + unlink("/tmp/acess2net"); + } + + return 0; +} + +void PrintUsage(const char *ProgName) +{ + fprintf(stderr, "Usage: %s\n", ProgName); +} + +int ParseCommandline(int argc, char *argv[]) +{ + const char *progname = argv[0]; + for( int i = 1; i < argc; i ++ ) + { + const char *arg = argv[i]; + if( arg[0] != '-' ) { + // bare args + } + else if( arg[1] != '-' ) { + // short args + } + else { + // long args + if( strcmp(arg, "--help") == 0 ) { + PrintUsage(progname); + exit(0); + } + else { + fprintf(stderr, "Unknown option: %s\n", arg); + PrintUsage(progname); + return 1; + } + } + } + return 0; +} + +void test_setname(const char *name) +{ + gsTestName = name; +} + +void test_message(const char *filename, int line, const char *msg, ...) +{ + fprintf(stderr, "%s:%i [%s] - ", filename, line, gsTestName); + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void test_assertion_fail(const char *filename, int line, const char *fmt, ...) +{ + fprintf(stderr, "%s:%i [%s] - ASSERT FAIL ", filename, line, gsTestName); + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + diff --git a/Tools/NetTest_Runner/net.c b/Tools/NetTest_Runner/net.c new file mode 100644 index 00000000..05c4ca45 --- /dev/null +++ b/Tools/NetTest_Runner/net.c @@ -0,0 +1,123 @@ +/* + */ +#include +#include +#include +#include +#include +#include +#include +#include "net.h" + + +#define CONNECT_TIMEOUT 10*1000 +#define MAX_IFS 4 + +typedef struct { + int FD; + socklen_t addrlen; + struct sockaddr_un addr; +} tIf; + +// === PROTOTYPES === + int Net_int_Open(const char *Path); + +// === GLOBALS === +tIf gaInterfaces[MAX_IFS]; + +// === CODE === +int Net_Open(int IfNum, const char *Path) +{ + if( IfNum >= MAX_IFS ) + return 1; + + if(gaInterfaces[IfNum].FD != 0) return 1; + gaInterfaces[IfNum].addrlen = sizeof(gaInterfaces[IfNum].addr); + gaInterfaces[IfNum].FD = Net_int_Open(Path); + return 0; +} + +int Net_int_Open(const char *Path) +{ + int fd = socket(AF_UNIX, SOCK_DGRAM, 0); + struct sockaddr_un sa = {AF_UNIX, ""}; + strcpy(sa.sun_path, Path); + unlink(Path); + if( bind(fd, (struct sockaddr*)&sa, sizeof(sa)) ) { + perror("NetTest_OpenUnix - bind"); + close(fd); + return -1; + } + + return fd; +} + +void Net_Close(int IfNum) +{ + assert(IfNum < MAX_IFS); + close(gaInterfaces[IfNum].FD); + gaInterfaces[IfNum].FD = 0; +} + +bool WaitOnFD(int FD, bool Write, unsigned int Timeout) +{ + fd_set fds; + FD_ZERO(&fds); + FD_SET(FD, &fds); + struct timeval timeout; + timeout.tv_sec = Timeout/1000; + timeout.tv_usec = (Timeout%1000) * 1000; + + if( Write ) + select(FD+1, NULL, &fds, NULL, &timeout); + else + select(FD+1, &fds, NULL, NULL, &timeout); + return FD_ISSET(FD, &fds); +} + +bool Net_int_EnsureConnected(int IfNum) +{ + assert(IfNum < MAX_IFS); + #if 0 + if( gaInterface_Clients[IfNum] == 0 ) + { + //if( !WaitOnFD(gaInterface_Servers[IfNum], CONNECT_TIMEOUT) ) + //{ + // fprintf(stderr, "ERROR: Client has not connected"); + // return false; + //} + gaInterface_Clients[IfNum] = accept(gaInterface_Servers[IfNum], NULL, NULL); + if( gaInterface_Clients[IfNum] < 0 ) { + perror("Net_int_EnsureConnected - accept"); + return false; + } + } + #endif + return true; +} + +size_t Net_Receive(int IfNum, size_t MaxLen, void *DestBuf, unsigned int Timeout) +{ + assert(IfNum < MAX_IFS); + tIf *If = &gaInterfaces[IfNum]; + + if( Net_int_EnsureConnected(IfNum) && WaitOnFD(If->FD, false, Timeout) ) + { + return recvfrom(If->FD, DestBuf, MaxLen, 0, &If->addr, &If->addrlen); + } + return 0; +} + +void Net_Send(int IfNum, size_t Length, const void *Buf) +{ + assert(IfNum < MAX_IFS); + tIf *If = &gaInterfaces[IfNum]; + + if( !WaitOnFD(If->FD, true, CONNECT_TIMEOUT) ) + return ; + int rv = sendto(If->FD, Buf, Length, 0, &If->addr, If->addrlen); + if( rv < 0 ) + perror("Net_Send - send"); +} + + diff --git a/Tools/NetTest_Runner/stack.c b/Tools/NetTest_Runner/stack.c new file mode 100644 index 00000000..c359d848 --- /dev/null +++ b/Tools/NetTest_Runner/stack.c @@ -0,0 +1,132 @@ +/* + */ +#define _POSIX_C_SOURCE 200809L +#include "stack.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_ARGS 16 + +extern char **environ; + +// === GLOBALS === +pid_t giStack_PID; + int giStack_InFD = -1; + int giNumStackArgs = 1; +char *gasStackArgs[MAX_ARGS] = {"nettest",NULL}; + +// === CODE === +void Stack_AddDevice(const char *Ident, const void *MacAddr) +{ + Stack_AddArg("-dev"); + Stack_AddArg("%02x%02x%02x%02x%02x%02x:unix:%s", + ((uint8_t*)MacAddr)[0], + ((uint8_t*)MacAddr)[1], + ((uint8_t*)MacAddr)[2], + ((uint8_t*)MacAddr)[3], + ((uint8_t*)MacAddr)[4], + ((uint8_t*)MacAddr)[5], + Ident); +} + +void Stack_AddInterface(const char *Name, int AddrType, const void *Addr, int MaskBits) +{ + Stack_AddArg("-ip"); + switch(AddrType) + { + case 4: + Stack_AddArg("%s,%i.%i.%i.%i/%i", Name, + ((uint8_t*)Addr)[0], + ((uint8_t*)Addr)[1], + ((uint8_t*)Addr)[2], + ((uint8_t*)Addr)[3], + MaskBits + ); + break; + default: + assert(AddrType == 4); + exit(1); + } +} + +void Stack_AddRoute(int Type, const void *Network, int MaskBits, const void *NextHop) +{ +} + +void Stack_AddArg(const char *Fmt, ...) +{ + va_list args; + va_start(args, Fmt); + size_t len = vsnprintf(NULL, 0, Fmt, args); + va_end(args); + char *arg = malloc(len+1); + va_start(args, Fmt); + vsnprintf(arg, len+1, Fmt, args); + va_end(args); + gasStackArgs[giNumStackArgs++] = arg; +} + +void sigchld_handler(int signum) +{ + int status; + wait(&status); + fprintf(stderr, "FAILURE: Child exited (%i)\n", status); +} + +int Stack_Start(const char *Subcommand) +{ + Stack_AddArg(Subcommand); + + for( int i = 3; i < 16; i ++ ) + fcntl(i, F_SETFD, FD_CLOEXEC); + + //signal(SIGCHLD, sigchld_handler); + + int fds_in[2]; + pipe(fds_in); + giStack_InFD = fds_in[1]; + fcntl(giStack_InFD, F_SETFD, FD_CLOEXEC); + + FILE *fp; + fp = fopen("stdout.txt", "a"); fprintf(fp, "--- Startup\n"); fclose(fp); + fp = fopen("stderr.txt", "a"); fprintf(fp, "--- Startup\n"); fclose(fp); + + posix_spawn_file_actions_t fa; + posix_spawn_file_actions_init(&fa); + posix_spawn_file_actions_adddup2(&fa, fds_in[0], 0); + posix_spawn_file_actions_addopen(&fa, 1, "stdout.txt", O_CREAT|O_APPEND|O_WRONLY, 0644); + posix_spawn_file_actions_addopen(&fa, 2, "stderr.txt", O_CREAT|O_APPEND|O_WRONLY, 0644); + + int rv = posix_spawn(&giStack_PID, "./nettest", &fa, NULL, gasStackArgs, environ); + if(rv) { + giStack_PID = 0; + fprintf(stderr, "posix_spawn failed: %s", strerror(rv)); + return 1; + } + + return 0; +} +void Stack_Kill(void) +{ + if( giStack_PID ) + { + kill(giStack_PID, SIGTERM); + giStack_PID = 0; + } +} + +int Stack_SendCommand(const char *CommandString) +{ + write(giStack_InFD, CommandString, strlen(CommandString)); + write(giStack_InFD, "\n", 1); + return 0; +} + diff --git a/Tools/NetTest_Runner/tcp.c b/Tools/NetTest_Runner/tcp.c new file mode 100644 index 00000000..56c55717 --- /dev/null +++ b/Tools/NetTest_Runner/tcp.c @@ -0,0 +1,74 @@ +/* + */ +#include "common.h" +#include "tcp.h" +#include "ip.h" +#include "test.h" // TEST_ASSERT +#include + +typedef struct { + uint16_t SPort; + uint16_t DPort; + uint32_t Seq; + uint32_t Ack; + uint8_t DataOfs; + uint8_t Flags; + uint16_t Window; + uint16_t Checksum; + uint16_t UrgPtr; +} __attribute__((packed)) tTCPHeader; + +// === CODE === +void TCP_Send(int IF, int AF, const void *IP, short sport, short dport, + uint32_t seq, uint32_t ack, uint8_t flags, uint16_t window, + size_t data_len, const void *data + ) +{ + tTCPHeader hdr; + hdr.SPort = htons(sport); + hdr.DPort = htons(dport); + hdr.Seq = htonl(seq); + hdr.Ack = htonl(ack); + hdr.DataOfs = sizeof(hdr)/4; + hdr.Flags = flags; + hdr.Window = htons(window); + hdr.Checksum = htons(0); + hdr.UrgPtr = htons(0); + + uint16_t checksum = IP_CHECKSUM_START; + checksum = IP_Checksum(checksum, sizeof(hdr), &hdr); + checksum = IP_Checksum(checksum, data_len, data); + hdr.Checksum = htons( checksum ); + + size_t buflens[] = {sizeof(hdr), data_len}; + const void *bufs[] = {&hdr, data}; + IP_Send(IF, AF, BLOB(HOST_IP), IP, IPPROTO_TCP, 2, buflens, bufs); +} + +bool TCP_Pkt_Check(size_t len, const void *data, size_t *out_ofs, int AF, const void *IP, short sport, short dport, + uint8_t flags) +{ + size_t ofs; + if( !IP_Pkt_Check(len, data, &ofs, AF, IP, BLOB(HOST_IP), IPPROTO_TCP) ) + return false; + + tTCPHeader hdr; + TEST_ASSERT_REL(len - ofs, >=, sizeof(hdr)); + memcpy(&hdr, (char*)data + ofs, sizeof(hdr)); + + TEST_ASSERT_REL( ntohs(hdr.SPort), ==, sport ); + TEST_ASSERT_REL( ntohs(hdr.DPort), ==, dport ); + // TODO: Checks on Seq/Ack + TEST_ASSERT_REL( hdr.Flags, ==, flags); + + uint16_t real_cksum = htons(hdr.Checksum); + hdr.Checksum = 0; + uint16_t calc_cksum = IP_CHECKSUM_START; + calc_cksum = IP_Checksum(calc_cksum, sizeof(hdr), &hdr); + calc_cksum = IP_Checksum(calc_cksum, len - ofs - sizeof(hdr), (char*)data+ofs+sizeof(hdr)); + TEST_ASSERT_REL( real_cksum, ==, calc_cksum ); + + *out_ofs = ofs + sizeof(hdr); + return true; +} + diff --git a/Tools/NetTest_Runner/test_arp.c b/Tools/NetTest_Runner/test_arp.c new file mode 100644 index 00000000..e97e2c35 --- /dev/null +++ b/Tools/NetTest_Runner/test_arp.c @@ -0,0 +1,39 @@ +/* + */ +#include "test.h" +#include "tests.h" +#include "net.h" +#include "stack.h" +#include "arp.h" + +bool Test_ARP_Basic(void) +{ + TEST_SETNAME(__func__); + size_t rxlen; + char rxbuf[MTU]; + + // Request test machine's IP + ARP_SendRequest(0, BLOB(TEST_IP)); + TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) ); + TEST_ASSERT( ARP_Pkt_IsResponse(rxlen, rxbuf, BLOB(TEST_IP), BLOB(TEST_MAC)) ); + + // Request host machine's IP + ARP_SendRequest(0, HOST_IP_STR); + TEST_ASSERT( Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) == 0 ); + + #if 0 + // Ask test machine to request our IP + Stack_SendCommand("arprequest "HOST_IP_STR); + TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) ); + TEST_ASSERT( ARP_Pkt_IsRequest(rxlen, rxbuf, HOST_IP) ); + + // Respond + ARP_SendResponse(0, HOST_IP, HOST_MAC); + + // Ask test machine to request our IP again (expecting nothing) + Stack_SendCommand("arprequest "HOST_IP_STR); + TEST_ASSERT( !Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) ); + #endif + + return true; +} diff --git a/Tools/NetTest_Runner/test_tcp.c b/Tools/NetTest_Runner/test_tcp.c new file mode 100644 index 00000000..a19c85b2 --- /dev/null +++ b/Tools/NetTest_Runner/test_tcp.c @@ -0,0 +1,26 @@ +/* + */ +#include "test.h" +#include "tests.h" +#include "net.h" +#include "stack.h" +#include "arp.h" +#include "tcp.h" + +bool Test_TCP_Basic(void) +{ + TEST_SETNAME(__func__); + size_t rxlen, ofs; + char rxbuf[MTU]; + + // 1. Test connection to closed port + uint32_t seq_tx = 0x1000; + uint32_t seq_exp = 0; + TCP_Send(0, 4, BLOB(TEST_IP), 1234, 80, seq_tx, seq_exp, TCP_SYN, 0x1000, 0, NULL); + + TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) ); + TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, 4, BLOB(TEST_IP), 80, 1234, TCP_RST) ); + TEST_ASSERT_REL(ofs, ==, rxlen); + + return true; +} diff --git a/Tools/nativelib/Makefile b/Tools/nativelib/Makefile index 7b78667f..1a50d881 100644 --- a/Tools/nativelib/Makefile +++ b/Tools/nativelib/Makefile @@ -7,11 +7,11 @@ LOBJ := threads.o time.o dummy_iocache.o # mutex.o rwlock.o semaphore.o KOBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/io.o vfs/dir.o -KOBJ += vfs/nodecache.o vfs/mount.o vfs/memfile.o # vfs/select.o +KOBJ += vfs/nodecache.o vfs/mount.o vfs/memfile.o vfs/select.o KOBJ += vfs/fs/root.o vfs/fs/devfs.o KOBJ += drv/proc.o -KOBJ += mutex.o rwlock.o semaphore.o -KOBJ += utf16.o +KOBJ += mutex.o rwlock.o semaphore.o events.o +KOBJ += utf16.o lib.o NOBJ := $(NOBJ:%.o=obj/%.o) LOBJ := $(LOBJ:%.o=obj/%.o) diff --git a/Tools/nativelib/include/acess.h b/Tools/nativelib/include/acess.h index b8bd4df2..f133788d 100644 --- a/Tools/nativelib/include/acess.h +++ b/Tools/nativelib/include/acess.h @@ -13,8 +13,6 @@ #define STR(x) #x #define EXPAND_STR(x) STR(x) -#define ASSERT(x) do{}while(0) - extern char __buildnum[]; #define BUILD_NUM ((int)(Uint)&__buildnum) extern const char gsGitHash[]; @@ -25,6 +23,7 @@ extern const char gsBuildInfo[]; #define BITS 32 #define NULL ((void*)0) #include +#include typedef uintptr_t Uint; //typedef unsigned int size_t; @@ -49,6 +48,7 @@ typedef uint32_t tPAddr; typedef uint32_t tUID; typedef uint32_t tGID; typedef uint32_t tTID; +typedef uint32_t tPGID; typedef int64_t tTime; extern tTime now(void); @@ -71,7 +71,27 @@ extern void free(void *buffer); extern char *strdup(const char *str); #include -#include +//#include +#include + +extern void Debug_TraceEnter(const char *Function, const char *Format, ...); +extern void Debug_TraceLog(const char *Function, const char *Format, ...); +extern void Debug_TraceLeave(const char *Function, char Type, ...); +#undef ENTER +#undef LOG +#undef LEAVE +#undef LEAVE_RET +#if DEBUG +# define ENTER(str, v...) Debug_TraceEnter(__func__, str, ##v) +# define LOG(fmt, v...) Debug_TraceLog(__func__, fmt, ##v) +# define LEAVE(t, v...) Debug_TraceLeave(__func__, t, ##v) +# define LEAVE_RET(t,v) do{LEAVE('-');return v;}while(0) +#else +# define ENTER(...) do{}while(0) +# define LOG(...) do{}while(0) +# define LEAVE(...) do{}while(0) +# define LEAVE_RET(t,v) do{return v;}while(0) +#endif // Threads extern void **Threads_GetHandlesPtr(void); diff --git a/Tools/nativelib/include/threads_int.h b/Tools/nativelib/include/threads_int.h index 1b4ddac4..7bd91c17 100644 --- a/Tools/nativelib/include/threads_int.h +++ b/Tools/nativelib/include/threads_int.h @@ -49,7 +49,7 @@ struct sThread tShortSpinlock IsLocked; - uint32_t PendingEvents; + uint32_t EventState; uint32_t WaitingEvents; tThreadIntSem *WaitSemaphore; // pthreads @@ -102,10 +102,13 @@ extern tShortSpinlock glThreadListLock; extern int Threads_int_CreateThread(struct sThread *Thread); extern int Threads_int_ThreadingEnabled(void); + extern tThread *Proc_GetCurThread(void); extern tThread *Threads_RemActive(void); extern void Threads_AddActive(tThread *Thread); extern void Threads_int_WaitForStatusEnd(enum eThreadStatus Status); +extern int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock); +extern void Semaphore_ForceWake(tThread *Thread); extern tThreadIntMutex *Threads_int_MutexCreate(void); extern void Threads_int_MutexDestroy(tThreadIntMutex *Mutex); diff --git a/Tools/nativelib/logging.c b/Tools/nativelib/logging.c index 16e6f70e..e9925ac1 100644 --- a/Tools/nativelib/logging.c +++ b/Tools/nativelib/logging.c @@ -19,16 +19,23 @@ extern int Threads_GetTID(); #define LOGHDR(col,type) fprintf(stderr, "\e["col"m[%-8.8s]"type"%2i ", Ident, Threads_GetTID()) #define LOGTAIL() fprintf(stderr, "\e[0m\n") -#define PUTERR(col,type) {\ +#define LOG_LOCK_ACQUIRE() do{ \ if(!gbThreadInLog) SHORTLOCK(&glDebugLock); \ gbThreadInLog ++; \ +}while(0) +#define LOG_LOCK_RELEASE() do {\ + gbThreadInLog --; \ + if(!gbThreadInLog) SHORTREL(&glDebugLock); \ +} while(0) + +#define PUTERR(col,type) {\ + LOG_LOCK_ACQUIRE(); \ LOGHDR(col,type);\ va_list args; va_start(args, Message);\ vfprintf(stderr, Message, args);\ va_end(args);\ LOGTAIL();\ - gbThreadInLog --; \ - if(!gbThreadInLog) SHORTREL(&glDebugLock); \ + LOG_LOCK_RELEASE(); \ } // === GLOBALS === @@ -66,11 +73,16 @@ void Log(const char *Message, ...) { const char *Ident = ""; PUTERR("37", "L") } +void Debug(const char *Message, ...) { + const char *Ident = ""; + PUTERR("38", "D") +} void Debug_HexDump(const char *Prefix, const void *Data, size_t Length) { const uint8_t *data = Data; size_t ofs; + LOG_LOCK_ACQUIRE(); fprintf(stderr, "[HexDump ]d %s: %i bytes\n", Prefix, (int)Length); for( ofs = 0; ofs + 16 <= Length; ofs += 16 ) { @@ -91,6 +103,7 @@ void Debug_HexDump(const char *Prefix, const void *Data, size_t Length) fprintf(stderr, " %02x", data[ofs%16]); } fprintf(stderr, "\n"); + LOG_LOCK_RELEASE(); } int giDebug_TraceLevel = 0; @@ -98,6 +111,7 @@ void Debug_HexDump(const char *Prefix, const void *Data, size_t Length) void Debug_TraceEnter(const char *Function, const char *Format, ...) { const char *Ident = "Trace"; + LOG_LOCK_ACQUIRE(); LOGHDR("37","T"); for( int i = 0; i < giDebug_TraceLevel; i ++ ) fprintf(stderr, " "); @@ -152,11 +166,13 @@ void Debug_TraceEnter(const char *Function, const char *Format, ...) fprintf(stderr, ")"); LOGTAIL(); giDebug_TraceLevel ++; + LOG_LOCK_RELEASE(); } void Debug_TraceLog(const char *Function, const char *Format, ...) { const char *Ident = "Trace"; + LOG_LOCK_ACQUIRE(); LOGHDR("37","T"); for( int i = 0; i < giDebug_TraceLevel; i ++ ) @@ -170,6 +186,7 @@ void Debug_TraceLog(const char *Function, const char *Format, ...) va_end(args); LOGTAIL(); + LOG_LOCK_RELEASE(); } void Debug_TraceLeave(const char *Function, char Type, ...) @@ -179,6 +196,7 @@ void Debug_TraceLeave(const char *Function, char Type, ...) } const char *Ident = "Trace"; + LOG_LOCK_ACQUIRE(); LOGHDR("37","T"); va_list args; @@ -220,5 +238,6 @@ void Debug_TraceLeave(const char *Function, char Type, ...) va_end(args); LOGTAIL(); + LOG_LOCK_RELEASE(); } diff --git a/Tools/nativelib/threads.c b/Tools/nativelib/threads.c index 9a4bfdfb..cc508fbf 100644 --- a/Tools/nativelib/threads.c +++ b/Tools/nativelib/threads.c @@ -8,6 +8,7 @@ #include #include #include +#include // === PROTOTYPES === void Threads_int_Init(void) __attribute__((constructor(101))); @@ -33,43 +34,6 @@ tThread *Proc_GetCurThread(void) return lpThreads_This; } -void Threads_PostEvent(tThread *Thread, Uint32 Events) -{ - if( !Thread ) { - // nope.avi - return ; - } - SHORTLOCK( &Thread->IsLocked ); - Thread->PendingEvents |= Events; - if( Thread->WaitingEvents & Events ) - Threads_int_SemSignal(Thread->WaitSemaphore); - SHORTREL( &Thread->IsLocked ); -} - -Uint32 Threads_WaitEvents(Uint32 Events) -{ - if( !Threads_int_ThreadingEnabled() ) { - Log_Notice("Threads", "_WaitEvents: Threading disabled"); - return 0; - } - lpThreads_This->WaitingEvents = Events; - Threads_int_SemWaitAll(lpThreads_This->WaitSemaphore); - lpThreads_This->WaitingEvents = 0; - Uint32 rv = lpThreads_This->PendingEvents; - return rv; -} - -void Threads_ClearEvent(Uint32 Mask) -{ - if( !Threads_int_ThreadingEnabled() ) { - Log_Notice("Threads", "_ClearEvent: Threading disabled"); - return ; - } - SHORTLOCK(&lpThreads_This->IsLocked); - lpThreads_This->PendingEvents &= ~Mask; - SHORTREL(&lpThreads_This->IsLocked); -} - tUID Threads_GetUID(void) { return 0; } tGID Threads_GetGID(void) { return 0; } @@ -91,12 +55,6 @@ void Threads_Sleep(void) Log_Warning("Threads", "Threads_Sleep shouldn't be used"); } -void Threads_int_WaitForStatusEnd(enum eThreadStatus Status) -{ - while( lpThreads_This->Status != Status ) - Threads_int_SemWaitAll(lpThreads_This->WaitSemaphore); -} - int Threads_SetName(const char *Name) { if( !lpThreads_This ) @@ -178,3 +136,53 @@ struct sThread *Proc_SpawnWorker(void (*Fcn)(void*), void *Data) } } +void Threads_int_WaitForStatusEnd(enum eThreadStatus Status) +{ + tThread *us = Proc_GetCurThread(); + assert(Status != THREAD_STAT_ACTIVE); + assert(Status != THREAD_STAT_DEAD); + LOG("%i(%s) - %i", us->TID, us->ThreadName, Status); + while( us->Status == Status ) + { + Threads_int_SemWaitAll(us->WaitSemaphore); + if( us->Status == Status ) + Log_Warning("Threads", "Thread %p(%i %s) rescheduled while in %s state", + us, us->TID, us->ThreadName, casTHREAD_STAT[Status]); + } +} + +int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock) +{ + tThread *us = Proc_GetCurThread(); + us->Next = NULL; + // - Mark as sleeping + us->Status = Status; + us->WaitPointer = Ptr; + us->RetStatus = Num; // Use RetStatus as a temp variable + + // - Add to waiting + if( ListTail ) { + if(*ListTail) { + (*ListTail)->Next = us; + } + else { + *ListHead = us; + } + *ListTail = us; + } + else if(ListHead) { + us->Next = *ListHead; + *ListHead = us; + } + else { + // nothing + } + + if( Lock ) { + SHORTREL( Lock ); + } + Threads_int_WaitForStatusEnd(Status); + us->WaitPointer = NULL; + return us->RetStatus; +} + diff --git a/Tools/nativelib/threads_int.c b/Tools/nativelib/threads_int.c index b2ad9f66..8ab9b341 100644 --- a/Tools/nativelib/threads_int.c +++ b/Tools/nativelib/threads_int.c @@ -4,6 +4,9 @@ * * threads_int.c * - Internal threading functions + * + * POSIX Mutex/Semaphore management + * Wait state */ #include #include @@ -28,7 +31,7 @@ int Threads_int_ThreadingEnabled(void) tThreadIntMutex *Threads_int_MutexCreate(void) { - if( pthread_mutex_init ) + if( Threads_int_ThreadingEnabled() ) { tThreadIntMutex *ret = malloc(sizeof(pthread_mutex_t)); pthread_mutex_init( (void*)ret, NULL ); @@ -45,7 +48,7 @@ void Threads_int_MutexLock(tThreadIntMutex *Mutex) if( !Mutex ) { return ; } - if( pthread_mutex_lock ) + if( Threads_int_ThreadingEnabled() ) { pthread_mutex_lock( (void*)Mutex ); } @@ -63,7 +66,7 @@ void Threads_int_MutexRelease(tThreadIntMutex *Mutex) return ; } - if( pthread_mutex_unlock ) + if( Threads_int_ThreadingEnabled() ) { pthread_mutex_unlock( (void*)Mutex ); } @@ -77,7 +80,7 @@ void Threads_int_MutexRelease(tThreadIntMutex *Mutex) tThreadIntSem *Threads_int_SemCreate(void) { - if( sem_init ) + if( Threads_int_ThreadingEnabled() ) { tThreadIntSem *ret = malloc(sizeof(sem_t)); sem_init( (void*)ret, 0, 0 ); @@ -91,7 +94,7 @@ tThreadIntSem *Threads_int_SemCreate(void) void Threads_int_SemSignal(tThreadIntSem *Sem) { - if( sem_post ) + if( Threads_int_ThreadingEnabled() ) { sem_post( (void*)Sem ); } @@ -103,7 +106,7 @@ void Threads_int_SemSignal(tThreadIntSem *Sem) void Threads_int_SemWaitAll(tThreadIntSem *Sem) { - if( sem_wait ) + if( Threads_int_ThreadingEnabled() ) { // TODO: Handle multiples sem_wait( (void*)Sem ); @@ -129,7 +132,7 @@ void *Threads_int_ThreadRoot(void *ThreadPtr) int Threads_int_CreateThread(tThread *Thread) { - if( pthread_create ) + if( Threads_int_ThreadingEnabled() ) { pthread_t *pthread = malloc(sizeof(pthread_t)); Thread->ThreadHandle = pthread; @@ -144,12 +147,7 @@ int Threads_int_CreateThread(tThread *Thread) void SHORTLOCK(tShortSpinlock *Lock) { - if( !pthread_mutex_init ) - { - if(*Lock) Log_KernelPanic("---", "Double short lock"); - *Lock = (void*)1; - } - else + if( Threads_int_ThreadingEnabled() ) { if( !*Lock ) { *Lock = malloc(sizeof(pthread_mutex_t)); @@ -159,24 +157,36 @@ void SHORTLOCK(tShortSpinlock *Lock) pthread_mutex_lock(*Lock); // printf("%p: SHORTLOCK held %p\n", lpThreads_This, __builtin_return_address(0)); } + else + { + if(*Lock) Log_KernelPanic("---", "Double short lock"); + *Lock = (void*)1; + } } void SHORTREL(tShortSpinlock *Lock) { - if( !pthread_mutex_init ) + if( Threads_int_ThreadingEnabled() ) { - if(!*Lock) Log_Notice("---", "Short release when not held"); - *Lock = NULL; + pthread_mutex_unlock(*Lock); +// printf("%p: SHORTLOCK rel\n", lpThreads_This); } else { - pthread_mutex_unlock(*Lock); -// printf("%p: SHORTLOCK rel\n", lpThreads_This); + if(!*Lock) Log_Notice("---", "Short release when not held"); + *Lock = NULL; } } int CPU_HAS_LOCK(tShortSpinlock *Lock) { - return 0; + if( Threads_int_ThreadingEnabled() ) + { + Log_KernelPanic("---", "TODO: CPU_HAS_LOCK with threading enabled"); + return 0; + } + else + { + return 0; + } } - diff --git a/Tools/nativelib/time.c b/Tools/nativelib/time.c index f2f18d82..6f941f26 100644 --- a/Tools/nativelib/time.c +++ b/Tools/nativelib/time.c @@ -7,6 +7,7 @@ */ #include #include +#include // === CODE === tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument) @@ -37,6 +38,17 @@ void Time_FreeTimer(tTimer *Timer) } +void Time_ScheduleEvent(int Delay) +{ + Log_Warning("Time", "TODO: EVENT_TIMER in %ims", Delay); +} + +void Time_Delay(int Delay) +{ + Time_ScheduleEvent(Delay); + Threads_WaitEvents(THREAD_EVENT_TIMER); +} + Sint64 now(void) { // TODO: Translate UNIX time into Acess time -- 2.20.1