Compare commits

..
Author SHA1 Message Date
t3hn3rd d3c164e8af Merge branch 'feature/vtop-fix' into feature/tcp
continuous-integration/drone/push Build is passing
2026-03-03 19:10:38 +00:00
t3hn3rd 5e9d0004d7 feature: implement TCP (RFC 793) protocol driver
continuous-integration/drone/push Build is passing
Add full TCP/IP stack implementation at L4 alongside UDP and ICMP.

Core Protocol Engine:
- TCP types in nettypes.pas (header, TCB, state enum, error codes,
  callbacks, OOO entry, socket structures)
- 3-way handshake (active & passive OPEN)
- Reliable data transfer with ACK and sequence number tracking
- Graceful close (FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT)
- RST send/receive handling
- Retransmission with exponential backoff (max 5 attempts)
- Pseudo-header based checksum (RFC 793)

Server Support:
- LISTEN → SYN_RECEIVED → ESTABLISHED passive open path
- Listen backlog enforcement (configurable per-socket)
- accept() to retrieve established child connections

Robustness & Hardening:
- Delayed ACK timer (~200ms)
- Nagle's algorithm (buffer small sends while unACKed data pending)
- Jacobson/Karels RTT estimation with adaptive RTO
- Congestion control (slow start / congestion avoidance)
- Zero-window probing with exponential backoff
- MSS option negotiation in SYN/SYN-ACK (Kind=2, Len=4)
- Keep-alive (30s idle, 10s interval, 5 probes)
- Out-of-order segment reassembly (4-slot buffer)

Terminal & Testing:
- tcpconnect: active open + send "Hello, World!"
- tcplisten: passive open with syslog output
- tcphttp: HTTP/1.0 GET client

Also add push_trace() instrumentation to net, eth2, arp, ipv4,
icmp, udp, and dhcp entry points for call tracing, and register
tcp in net.init.
2026-03-03 10:55:50 +00:00
27 changed files with 2389 additions and 3530 deletions
+8 -22
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# compile_lvgl.sh — Download LVGL v9.2 source and compile into lib/liblvgl.a # compile_lvgl.sh — Download LVGL v9.2 source and compile into lib/liblvgl.a
# Clone & compile in /tmp (fast container-local fs). # Clone & compile in /tmp (fast container-local fs).
# Cache liblvgl.a on host mount (/code/lvgl/) to skip rebuild. # Copy source + objects to /lvgl (host mount) for debugging.
set -e set -e
LVGL_VERSION="v9.2.2" LVGL_VERSION="v9.2.2"
@@ -10,7 +10,6 @@ LVGL_DIR="/tmp/lvgl"
OBJ_DIR="/tmp/lvgl_obj" OBJ_DIR="/tmp/lvgl_obj"
CONF_DIR="$(pwd)/lvglh" CONF_DIR="$(pwd)/lvglh"
OUT_DIR="$(pwd)/lib" OUT_DIR="$(pwd)/lib"
CACHE_DIR="/code/lvgl"
CC="gcc" CC="gcc"
CFLAGS="-m32 -march=i686 -ffreestanding \ CFLAGS="-m32 -march=i686 -ffreestanding \
@@ -27,17 +26,6 @@ echo " "
echo "Compiling LVGL..." echo "Compiling LVGL..."
echo " " echo " "
# If cached liblvgl.a exists on host mount, just copy it and skip everything
if [ -f "${CACHE_DIR}/liblvgl.a" ]; then
echo "Found cached liblvgl.a in ${CACHE_DIR}, copying to ${OUT_DIR}..."
cp "${CACHE_DIR}/liblvgl.a" "${OUT_DIR}/liblvgl.a"
echo "(Delete lvgl/liblvgl.a to force a full rebuild.)"
echo " "
echo "LVGL compilation complete (cached)."
echo " "
exit 0
fi
# Clone LVGL into /tmp (container-local) # Clone LVGL into /tmp (container-local)
rm -rf "$LVGL_DIR" rm -rf "$LVGL_DIR"
echo "Downloading LVGL ${LVGL_VERSION}..." echo "Downloading LVGL ${LVGL_VERSION}..."
@@ -117,16 +105,14 @@ else
echo "No custom LVGL files in lvglh/." echo "No custom LVGL files in lvglh/."
fi fi
# Cache liblvgl.a and source to host mount for next build # Copy source + objects to /lvgl host mount for debugging
echo "Caching LVGL to host mount..." echo "Copying LVGL source and objects to host mount..."
mkdir -p "$CACHE_DIR" mkdir /code/lvgl 2>/dev/null || true
rm -rf ${CACHE_DIR}/* ${CACHE_DIR}/.[!.]* ${CACHE_DIR}/..?* 2>/dev/null || true rm -rf /code/lvgl/* /code/lvgl/.[!.]* /code/lvgl/..?* 2>/dev/null || true
cp "${OUT_DIR}/liblvgl.a" "${CACHE_DIR}/liblvgl.a" cp -a "$LVGL_DIR/src" /code/lvgl/src
cp -a "$LVGL_DIR/src" "${CACHE_DIR}/src" cp -a "$OBJ_DIR" /code/lvgl/obj
cp -a "$LVGL_DIR"/*.h "${CACHE_DIR}/" 2>/dev/null || true
cp -a "$OBJ_DIR" "${CACHE_DIR}/obj"
echo "Done." echo "Done."
echo " " echo " "
echo "LVGL compilation complete." echo "LVGL compilation complete."
echo " " echo " "
+1 -1
View File
@@ -4,4 +4,4 @@ echo "======================="
echo " " echo " "
echo "Compiling FPC Sources..." echo "Compiling FPC Sources..."
echo " " echo " "
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/include/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -100,8 +100,8 @@ end;
procedure send(p_data : void; p_len : uint16); procedure send(p_data : void; p_len : uint16);
begin begin
//push_trace('net.send'); push_trace('net.send');
//writeToLogLn('L1: net.send'); writeToLogLn('L1/NET: send');
if CBSend <> nil then CBSend(p_data, p_len); if CBSend <> nil then CBSend(p_data, p_len);
pop_trace; pop_trace;
end; end;
@@ -111,8 +111,7 @@ var
context : PPacketContext; context : PPacketContext;
begin begin
//push_trace('net.recv'); push_trace('net.recv');
//writeToLogLn('L1: net.recv');
context:= newPacketContext; context:= newPacketContext;
if CBNext <> nil then CBNext(p_data, p_len, context); if CBNext <> nil then CBNext(p_data, p_len, context);
freePacketContext(context); freePacketContext(context);
@@ -129,6 +128,7 @@ end;
procedure init; procedure init;
begin begin
push_trace('net.init'); push_trace('net.init');
writeToLogLn('L1/NET: init');
//l2 //l2
eth2.register; eth2.register;
//l3 //l3
@@ -136,6 +136,7 @@ begin
ipv4.register; ipv4.register;
//l4 //l4
icmp.register; icmp.register;
tcp.register;
udp.register; udp.register;
//l5 //l5
dhcp.register; dhcp.register;
+4 -1
View File
@@ -46,6 +46,7 @@ var
procedure registerTypePromisc(eType : uint16; RecvCB : TRecvCallback); procedure registerTypePromisc(eType : uint16; RecvCB : TRecvCallback);
begin begin
push_trace('eth2.registerTypePromisc');
register; register;
if EthTypes[eType] = nil then EthTypes[eType]:= RecvCB; if EthTypes[eType] = nil then EthTypes[eType]:= RecvCB;
Promisc[eType]:= true; Promisc[eType]:= true;
@@ -53,6 +54,7 @@ end;
procedure registerType(eType : uint16; RecvCB : TRecvCallback); procedure registerType(eType : uint16; RecvCB : TRecvCallback);
begin begin
push_trace('eth2.registerType');
register; register;
if EthTypes[eType] = nil then EthTypes[eType]:= RecvCB; if EthTypes[eType] = nil then EthTypes[eType]:= RecvCB;
end; end;
@@ -93,7 +95,7 @@ var
buf : puint8; buf : puint8;
begin begin
//writeToLogLn(' L2: eth2.recv'); push_trace('eth2.recv');
buf:= puint8(p_data); buf:= puint8(p_data);
Header:= PEthernetHeader(buf); Header:= PEthernetHeader(buf);
@@ -120,6 +122,7 @@ var
begin begin
push_trace('eth2.register'); push_trace('eth2.register');
if not Registered then begin if not Registered then begin
writeToLogLn(' L2/ETH: register');
for i:=0 to 65535 do begin for i:=0 to 65535 do begin
EthTypes[i]:= nil; EthTypes[i]:= nil;
Promisc[i]:= false; Promisc[i]:= false;
+9
View File
@@ -96,6 +96,7 @@ var
hSize, pSize : uint8; hSize, pSize : uint8;
begin begin
push_trace('arp.send');
if p_context <> nil then begin if p_context <> nil then begin
buf:= kalloc(sizeof(TARPHeader)); buf:= kalloc(sizeof(TARPHeader));
hdr:= PARPHeader(buf); hdr:= PARPHeader(buf);
@@ -135,6 +136,8 @@ var
context : PPacketContext; context : PPacketContext;
begin begin
push_trace('arp.sendGratuitous');
writeToLogLn(' L3/ARP: sendGratuitous');
context:= newPacketContext; context:= newPacketContext;
CopyIPv4(@getIPv4Config^.Address[0], @context^.IP.Destination[0]); CopyIPv4(@getIPv4Config^.Address[0], @context^.IP.Destination[0]);
CopyIPv4(@getIPv4Config^.Address[0], @context^.IP.Source[0]); CopyIPv4(@getIPv4Config^.Address[0], @context^.IP.Source[0]);
@@ -150,6 +153,7 @@ var
CacheRecord : PARPCacheRecord; CacheRecord : PARPCacheRecord;
begin begin
push_trace('arp.sendRequestGateway');
context:= newPacketContext; context:= newPacketContext;
CacheRecord:= findCacheRecordByIP(@getIPv4Config^.Gateway[0]); CacheRecord:= findCacheRecordByIP(@getIPv4Config^.Gateway[0]);
if CacheRecord <> nil then begin if CacheRecord <> nil then begin
@@ -168,6 +172,8 @@ var
CacheRecord : PARPCacheRecord; CacheRecord : PARPCacheRecord;
begin begin
push_trace('arp.sendRequest');
writeToLogLn(' L3/ARP: sendRequest');
context:= newPacketContext; context:= newPacketContext;
CopyIPv4(ip, @context^.IP.Destination[0]); CopyIPv4(ip, @context^.IP.Destination[0]);
CopyIPv4(@getIPv4Config^.Address[0], @context^.IP.Source[0]); CopyIPv4(@getIPv4Config^.Address[0], @context^.IP.Source[0]);
@@ -184,6 +190,7 @@ var
CacheRecord : PARPCacheRecord; CacheRecord : PARPCacheRecord;
begin begin
push_trace('arp.resolveIP');
CacheRecord:= findCacheRecordByIP(ip); CacheRecord:= findCacheRecordByIP(ip);
resolveIP:= nil; resolveIP:= nil;
if CacheRecord = nil then begin if CacheRecord = nil then begin
@@ -203,6 +210,7 @@ var
context : PPacketContext; context : PPacketContext;
begin begin
push_trace('arp.recv');
{ Get our converted Header } { Get our converted Header }
Header:= PARPHeader(p_data); Header:= PARPHeader(p_data);
AHeader.Hardware_Type:= (Header^.Hardware_Type_Hi SHL 8) + Header^.Hardware_Type_Lo; AHeader.Hardware_Type:= (Header^.Hardware_Type_Hi SHL 8) + Header^.Hardware_Type_Lo;
@@ -298,6 +306,7 @@ procedure register;
begin begin
push_trace('arp.register'); push_trace('arp.register');
if not Registered then begin if not Registered then begin
writeToLogLn(' L3/ARP: register');
Cache:= LL_New(sizeof(TARPCacheRecord)); Cache:= LL_New(sizeof(TARPCacheRecord));
eth2.registerTypePromisc($0806, @recv); eth2.registerTypePromisc($0806, @recv);
stdio.registerCommand('ARP', @terminal_command_arp, 'Get ARP Table.'); stdio.registerCommand('ARP', @terminal_command_arp, 'Get ARP Table.');
+7
View File
@@ -46,7 +46,9 @@ var
function getIPv4Config : PIPv4Configuration; function getIPv4Config : PIPv4Configuration;
begin begin
push_trace('ipv4.getIPv4Config');
getIPv4Config:= @Config; getIPv4Config:= @Config;
pop_trace;
end; end;
procedure send(p_data : void; p_len : uint16; p_context : PPacketContext); procedure send(p_data : void; p_len : uint16; p_context : PPacketContext);
@@ -57,6 +59,7 @@ var
buffer : void; buffer : void;
begin begin
push_trace('ipv4.send');
inc(CurrentID); inc(CurrentID);
Header.version:= 4; Header.version:= 4;
Header.header_len:= 5; Header.header_len:= 5;
@@ -84,6 +87,7 @@ begin
memcpy(uint32(p_data), uint32(Buffer) + (Header.header_len * 4), p_len); memcpy(uint32(p_data), uint32(Buffer) + (Header.header_len * 4), p_len);
eth2.send(Buffer, (Header.header_len * 4) + p_len, $0800, p_context); eth2.send(Buffer, (Header.header_len * 4) + p_len, $0800, p_context);
kfree(Buffer); kfree(Buffer);
pop_trace;
end; end;
procedure recv(p_data : void; p_len : uint16; p_context : PPacketContext); procedure recv(p_data : void; p_len : uint16; p_context : PPacketContext);
@@ -95,6 +99,7 @@ var
len : uint16; len : uint16;
begin begin
push_trace('ipv4.recv');
Header:= PIPV4Header(p_data); Header:= PIPV4Header(p_data);
AHeader.version:= Header^.version; AHeader.version:= Header^.version;
AHeader.header_len:= Header^.header_len; AHeader.header_len:= Header^.header_len;
@@ -195,6 +200,7 @@ var
begin begin
push_trace('ipv4.register'); push_trace('ipv4.register');
if not Registered then begin if not Registered then begin
writeToLogLn(' L3/IPv4: register');
for i:=0 to 255 do begin for i:=0 to 255 do begin
Protocols[i]:= nil; Protocols[i]:= nil;
end; end;
@@ -213,6 +219,7 @@ end;
procedure registerProtocol(Protocol_ID : uint8; recv_callback : TRecvCallback); procedure registerProtocol(Protocol_ID : uint8; recv_callback : TRecvCallback);
begin begin
push_trace('ipv4.registerProtocol');
register; register;
if Protocols[Protocol_ID] = nil then Protocols[Protocol_ID]:= recv_callback; if Protocols[Protocol_ID] = nil then Protocols[Protocol_ID]:= recv_callback;
pop_trace; pop_trace;
+8 -1
View File
@@ -23,7 +23,7 @@ interface
uses uses
bios_data_area, bios_data_area,
lmemorymanager, lmemorymanager, tracer,
net, nettypes, netutils, ipv4, arp, util; net, nettypes, netutils, ipv4, arp, util;
type type
@@ -55,6 +55,7 @@ var
i : uint8; i : uint8;
begin begin
push_trace('icmp.nextInactiveHandler');
nextInactiveHandler:= 0; nextInactiveHandler:= 0;
for i:=1 to 255 do begin for i:=1 to 255 do begin
if not Handlers[i].Active then begin if not Handlers[i].Active then begin
@@ -80,6 +81,8 @@ var
Size : uint32; Size : uint32;
begin begin
push_trace('icmp.sendICMPRequest');
writeToLogLn(' L4/ICMP: sendICMPRequest');
handle:= nextInactiveHandler; handle:= nextInactiveHandler;
Handlers[handle].Active:= true; Handlers[handle].Active:= true;
Handlers[handle].OnReply:= OnRep; Handlers[handle].OnReply:= OnRep;
@@ -135,6 +138,8 @@ var
Handle : uint8; Handle : uint8;
begin begin
push_trace('icmp.recv');
writeToLogLn(' L4/ICMP: recv');
Header:= PICMPHeader(p_data); Header:= PICMPHeader(p_data);
//writehexlnWND(Header^.ICMP_Type, getTerminalHWND); //writehexlnWND(Header^.ICMP_Type, getTerminalHWND);
case Header^.ICMP_Type of case Header^.ICMP_Type of
@@ -243,6 +248,8 @@ var
i : uint32; i : uint32;
begin begin
push_trace('icmp.register');
writeToLogLn(' L4/ICMP: register');
for i:=0 to 255 do begin for i:=0 to 255 do begin
Handlers[i].Active:= false; Handlers[i].Active:= false;
Handlers[i].OnError:= nil; Handlers[i].OnError:= nil;
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -25,7 +25,7 @@ uses
lmemorymanager, lmemorymanager,
nettypes, netutils, nettypes, netutils,
ipv4, net, ipv4, net,
util; tracer, util;
procedure register(); procedure register();
function bind(bindContext : PUDPBindContext) : TUDPError; function bind(bindContext : PUDPBindContext) : TUDPError;
@@ -71,6 +71,7 @@ begin
nullend:= true; nullend:= true;
end; end;
pseudoBuffer:= kalloc(sizeof(TUDPPseudoHeader) + pseudoSize); pseudoBuffer:= kalloc(sizeof(TUDPPseudoHeader) + pseudoSize);
push_trace('udp.CalculateChecksum');
pseudoBuffer8:= puint8(pseudoBuffer); pseudoBuffer8:= puint8(pseudoBuffer);
pseudoBuffer16:= puint16(pseudoBuffer); pseudoBuffer16:= puint16(pseudoBuffer);
pseudoBuffer32:= puint32(pseudoBuffer); pseudoBuffer32:= puint32(pseudoBuffer);
@@ -118,6 +119,7 @@ var
begin begin
push_trace('udp.send');
if udpContext <> nil then begin if udpContext <> nil then begin
size:= p_len + sizeof(TUDPHeader); size:= p_len + sizeof(TUDPHeader);
buffer:= kalloc(size); buffer:= kalloc(size);
@@ -152,6 +154,7 @@ var
context : PUDPBindContext; context : PUDPBindContext;
begin begin
push_trace('udp.bind');
result:= tueGenericError; result:= tueGenericError;
if bindContext <> nil then begin if bindContext <> nil then begin
if Ports[bindContext^.port] = nil then begin if Ports[bindContext^.port] = nil then begin
@@ -174,6 +177,7 @@ var
context : PUDPBindContext; context : PUDPBindContext;
begin begin
push_trace('udp.unbind');
result:= tueGenericError; result:= tueGenericError;
if bindContext <> nil then begin if bindContext <> nil then begin
context:= Ports[bindContext^.port]; context:= Ports[bindContext^.port];
@@ -203,6 +207,7 @@ var
size : uint16; size : uint16;
begin begin
push_trace('udp.ProcessPacket');
header:= PUDPHeader(p_data); header:= PUDPHeader(p_data);
if Ports[switchendian16(header^.DstPort)] <> nil then begin if Ports[switchendian16(header^.DstPort)] <> nil then begin
context:= PUDPPacketContext(kalloc(sizeof(TUDPPacketContext))); context:= PUDPPacketContext(kalloc(sizeof(TUDPPacketContext)));
@@ -245,6 +250,8 @@ var
Checksum : uint32; Checksum : uint32;
begin begin
push_trace('udp.register');
writeToLogLn(' L4/UDP: register');
for i:=0 to 65535 do begin for i:=0 to 65535 do begin
Ports[i]:= nil; Ports[i]:= nil;
end; end;
+6 -3
View File
@@ -415,7 +415,7 @@ end;
procedure processPacket_NAK(Header : PDHCPHeader; Options : PDHCPOptions); procedure processPacket_NAK(Header : PDHCPHeader; Options : PDHCPOptions);
begin begin
syslog.logln('DHCP', 'Process NAK.'); writeToLogLn(' L5/DHCP: Process NAK.');
{ Server provided a NAK, NULL configuration ready for next DISCOVER/Request } { Server provided a NAK, NULL configuration ready for next DISCOVER/Request }
nullConfiguration(); nullConfiguration();
end; end;
@@ -429,7 +429,7 @@ var
cfgopt : void; cfgopt : void;
begin begin
syslog.logln('DHCP', 'Process ACK.'); writeToLogLn(' L5/DHCP: Process ACK.');
getIPv4Config^.UP:= false; getIPv4Config^.UP:= false;
//Copy new address //Copy new address
@@ -484,7 +484,7 @@ Var
begin begin
syslog.logln('DHCP', 'Process OFFER.'); syslog.logln('DHCP', 'Process OFFER.');
writeToLogLn(' L5/DHCP: Process OFFER.');
{ Check the Transaction ID matches our stored ID, discard if not. } { Check the Transaction ID matches our stored ID, discard if not. }
if Header^.Transaction_ID = Configuration^.Transaction then begin if Header^.Transaction_ID = Configuration^.Transaction then begin
syslog.logln('DHCP', 'XID Match'); syslog.logln('DHCP', 'XID Match');
@@ -566,6 +566,7 @@ var
begin begin
tracer.push_trace('dhcp.processPacket.enter'); tracer.push_trace('dhcp.processPacket.enter');
writeToLogLn(' L5/DHCP: processPacket');
syslog.logln('DHCP','processPacket'); syslog.logln('DHCP','processPacket');
{ Give access to header values & process to correct endianness. } { Give access to header values & process to correct endianness. }
@@ -636,6 +637,7 @@ var
begin begin
tracer.push_trace('dhcp.DHCPDiscover.begin'); tracer.push_trace('dhcp.DHCPDiscover.begin');
writeToLogLn(' L5/DHCP: DHCPDiscover');
{ Ensure we have a socket bound. } { Ensure we have a socket bound. }
if Socket <> nil then begin if Socket <> nil then begin
{ Clear any current configuration } { Clear any current configuration }
@@ -711,6 +713,7 @@ var
begin begin
tracer.push_trace('dhcp.register'); tracer.push_trace('dhcp.register');
writeToLogLn(' L5/DHCP: register');
syslog.logln('DHCP', 'Register begin.'); syslog.logln('DHCP', 'Register begin.');
{ Kalloc our Configuration Data } { Kalloc our Configuration Data }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

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