156 lines
4.4 KiB
Nim
156 lines
4.4 KiB
Nim
# SPDX-License-Identifier: LSL-1.0
|
|
# Copyright (c) 2026 Markus Maiwald
|
|
# Stewardship: Self Sovereign Society Foundation
|
|
#
|
|
# This file is part of the Nexus Sovereign Core.
|
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
|
|
# SPEC-093: UTCP Protocol Implementation
|
|
# Sovereign Transport for Intra-Cluster Communication
|
|
|
|
# Import C decls for kernel logging
|
|
proc kprint(s: cstring) {.importc, cdecl.}
|
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
|
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
|
|
|
# --- Protocol Constants ---
|
|
|
|
const
|
|
ETHERTYPE_UTCP* = 0x88B5'u16
|
|
|
|
# Flags
|
|
UTCP_FLAG_SYN* = 0x01'u8
|
|
UTCP_FLAG_ACK* = 0x02'u8
|
|
UTCP_FLAG_NACK* = 0x04'u8
|
|
UTCP_FLAG_FIN* = 0x08'u8
|
|
UTCP_FLAG_DATA* = 0x10'u8
|
|
|
|
# --- Types ---
|
|
|
|
type
|
|
CellID* = object
|
|
## 128-bit SipHash result representing a Node Identity
|
|
lo*: uint64
|
|
hi*: uint64
|
|
|
|
UtcpHeader* {.packed.} = object
|
|
## 32-Byte Fixed Header (SPEC-093)
|
|
eth_type*: uint16 # 0x88B5 (Big Endian)
|
|
flags*: uint8
|
|
reserved*: uint8
|
|
target_id*: CellID # 16 bytes
|
|
sender_id*: CellID # 16 bytes
|
|
seq_num*: uint64 # 8 bytes (Big Endian)
|
|
payload_len*: uint16 # 2 bytes (Big Endian)
|
|
# Total = 46 bytes.
|
|
# 46 bytes + 14 byte Eth header = 60 bytes minimum frame size.
|
|
|
|
UtcpState* = enum
|
|
CLOSED, LISTEN, SYN_SENT, SYN_RCVD, ESTABLISHED, FIN_WAIT
|
|
|
|
UtcpControlBlock* = object
|
|
state*: UtcpState
|
|
local_id*: CellID
|
|
remote_id*: CellID
|
|
local_seq*: uint64
|
|
remote_seq*: uint64
|
|
last_ack*: uint64
|
|
|
|
const MAX_CONNECTIONS = 16
|
|
var utcp_pcb_table: array[MAX_CONNECTIONS, UtcpControlBlock]
|
|
|
|
# --- Helper Functions ---
|
|
|
|
proc ntohs(n: uint16): uint16 {.inline.} =
|
|
return (n shr 8) or (n shl 8)
|
|
|
|
proc ntohll(n: uint64): uint64 {.inline.} =
|
|
var b = cast[array[8, byte]](n)
|
|
# Reverse bytes
|
|
return (uint64(b[0]) shl 56) or (uint64(b[1]) shl 48) or
|
|
(uint64(b[2]) shl 40) or (uint64(b[3]) shl 32) or
|
|
(uint64(b[4]) shl 24) or (uint64(b[5]) shl 16) or
|
|
(uint64(b[6]) shl 8) or uint64(b[7])
|
|
|
|
proc htonll(n: uint64): uint64 {.inline.} =
|
|
return ntohll(n) # Symmetric
|
|
|
|
proc cellid_eq(a, b: CellID): bool =
|
|
return a.lo == b.lo and a.hi == b.hi
|
|
|
|
proc utcp_find_pcb(remote_id: CellID): ptr UtcpControlBlock =
|
|
for i in 0 ..< MAX_CONNECTIONS:
|
|
if utcp_pcb_table[i].state != CLOSED and cellid_eq(utcp_pcb_table[i].remote_id, remote_id):
|
|
return addr utcp_pcb_table[i]
|
|
return nil
|
|
|
|
proc utcp_alloc_pcb(): ptr UtcpControlBlock =
|
|
for i in 0 ..< MAX_CONNECTIONS:
|
|
if utcp_pcb_table[i].state == CLOSED:
|
|
return addr utcp_pcb_table[i]
|
|
return nil
|
|
|
|
# --- Logic ---
|
|
|
|
proc utcp_handle_packet*(data: ptr UncheckedArray[byte], len: uint16) {.exportc, cdecl.} =
|
|
## Handle raw UTCP frame (stripped of UDP/IP headers if tunnelled)
|
|
|
|
if len < uint16(sizeof(UtcpHeader)):
|
|
kprintln("[UTCP] Drop: Frame too short")
|
|
return
|
|
|
|
let header = cast[ptr UtcpHeader](data)
|
|
|
|
# Validate Magic
|
|
if ntohs(header.eth_type) != ETHERTYPE_UTCP:
|
|
# Allow 0x88B5 for now, but log if mismatch
|
|
discard
|
|
|
|
let seq_num = ntohll(header.seq_num)
|
|
let flags = header.flags
|
|
|
|
# Log Packet
|
|
kprint("[UTCP] RX Seq="); kprint_hex(seq_num);
|
|
kprint(" Flags="); kprint_hex(uint64(flags)); kprintln("")
|
|
|
|
# State Machine
|
|
var pcb = utcp_find_pcb(header.sender_id)
|
|
|
|
if pcb == nil:
|
|
# New Connection?
|
|
if (flags and UTCP_FLAG_SYN) != 0:
|
|
kprintln("[UTCP] New SYN received")
|
|
pcb = utcp_alloc_pcb()
|
|
if pcb != nil:
|
|
pcb.state = SYN_RCVD
|
|
pcb.remote_id = header.sender_id
|
|
pcb.local_id = header.target_id
|
|
pcb.remote_seq = seq_num
|
|
pcb.local_seq = 1000 # Randomize?
|
|
kprintln("[UTCP] State -> SYN_RCVD. Sending SYN-ACK (TODO)")
|
|
# TODO: Send SYN-ACK
|
|
else:
|
|
kprintln("[UTCP] Drop: Table full")
|
|
else:
|
|
kprintln("[UTCP] Drop: Packet for unknown connection")
|
|
return
|
|
|
|
else:
|
|
# Existing Connection
|
|
kprint("[UTCP] Match PCB. State="); kprint_hex(uint64(pcb.state)); kprintln("")
|
|
|
|
case pcb.state:
|
|
of SYN_RCVD:
|
|
if (flags and UTCP_FLAG_ACK) != 0:
|
|
pcb.state = ESTABLISHED
|
|
kprintln("[UTCP] State -> ESTABLISHED")
|
|
of ESTABLISHED:
|
|
if (flags and UTCP_FLAG_DATA) != 0:
|
|
kprintln("[UTCP] Data received")
|
|
# TODO: Enqueue data
|
|
elif (flags and UTCP_FLAG_FIN) != 0:
|
|
pcb.state = CLOSED # Simplify for now
|
|
kprintln("[UTCP] Connection-Teardown (FIN)")
|
|
else:
|
|
discard
|