feat(net): Fast Path/Zero-Copy Bypass & Network Stack Documentation
Implemented Fast Path filter for UDP/9999 UTCP tunnel traffic, bypassing LwIP stack. Added zero-copy header stripping in fastpath.nim. Documented full network stack architecture in docs/NETWORK_STACK.md. Verified ICMP ping and LwIP graft functionality.
This commit is contained in:
parent
4c91aa7f14
commit
b480f14bb5
|
|
@ -0,0 +1,98 @@
|
|||
# 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.
|
||||
|
||||
## Fast Path Bypass (SPEC-700)
|
||||
##
|
||||
## Intercepts UTCP tunnel traffic (UDP/9999) before LwIP processing.
|
||||
## Zero-copy header stripping via pointer arithmetic.
|
||||
|
||||
# Constants
|
||||
const
|
||||
UTCP_TUNNEL_PORT* = 9999'u16
|
||||
ETHERTYPE_IPV4* = 0x0800'u16
|
||||
IPPROTO_UDP* = 17'u8
|
||||
|
||||
# Header sizes
|
||||
ETH_HEADER_LEN* = 14
|
||||
IP_HEADER_LEN* = 20
|
||||
UDP_HEADER_LEN* = 8
|
||||
TUNNEL_OVERHEAD* = ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN # 42 bytes
|
||||
|
||||
# MTU safety
|
||||
MAX_UTCP_FRAME* = 1400'u16 # Safe for PPPoE/VPN
|
||||
|
||||
proc kprint(s: cstring) {.importc, cdecl.}
|
||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||
|
||||
# --- Fast Path Detection ---
|
||||
|
||||
proc is_utcp_tunnel*(data: ptr UncheckedArray[byte], len: uint16): bool {.exportc, cdecl.} =
|
||||
## Check if packet is a UTCP tunnel packet (UDP port 9999)
|
||||
## Returns true if packet should bypass LwIP
|
||||
|
||||
# Minimum size check: ETH(14) + IP(20) + UDP(8) = 42 bytes
|
||||
if len < TUNNEL_OVERHEAD:
|
||||
return false
|
||||
|
||||
# Check EtherType (big-endian at offset 12-13)
|
||||
let eth_type = (uint16(data[12]) shl 8) or uint16(data[13])
|
||||
if eth_type != ETHERTYPE_IPV4:
|
||||
return false
|
||||
|
||||
# Check IP Protocol (offset 23 in frame = offset 9 in IP header)
|
||||
let ip_proto = data[23]
|
||||
if ip_proto != IPPROTO_UDP:
|
||||
return false
|
||||
|
||||
# Check UDP destination port (big-endian at offset 36-37)
|
||||
# ETH(14) + IP(20) + UDP dst port offset(2) = 36
|
||||
let dst_port = (uint16(data[36]) shl 8) or uint16(data[37])
|
||||
|
||||
return dst_port == UTCP_TUNNEL_PORT
|
||||
|
||||
|
||||
proc strip_tunnel_headers*(data: ptr UncheckedArray[byte], len: var uint16): ptr UncheckedArray[byte] {.exportc, cdecl.} =
|
||||
## Strip ETH+IP+UDP headers from tunnel packet (zero-copy)
|
||||
## Returns pointer to UTCP header, adjusts length
|
||||
##
|
||||
## SAFETY: Caller must ensure len >= TUNNEL_OVERHEAD
|
||||
|
||||
if len < TUNNEL_OVERHEAD:
|
||||
return nil
|
||||
|
||||
# Zero-copy: just advance pointer
|
||||
let utcp_data = cast[ptr UncheckedArray[byte]](
|
||||
cast[uint64](data) + TUNNEL_OVERHEAD
|
||||
)
|
||||
len = len - TUNNEL_OVERHEAD
|
||||
|
||||
return utcp_data
|
||||
|
||||
|
||||
proc check_mtu*(len: uint16): bool =
|
||||
## Check if UTCP frame exceeds safe MTU
|
||||
return len <= MAX_UTCP_FRAME
|
||||
|
||||
|
||||
# --- Source Address Extraction (for response routing) ---
|
||||
|
||||
type
|
||||
TunnelSource* = object
|
||||
ip*: uint32 # Source IP (network byte order)
|
||||
port*: uint16 # Source port
|
||||
|
||||
proc extract_tunnel_source*(data: ptr UncheckedArray[byte]): TunnelSource =
|
||||
## Extract source IP and port from tunnel packet for response routing
|
||||
|
||||
# Source IP at ETH(14) + IP src offset(12) = 26
|
||||
result.ip = (uint32(data[26]) shl 24) or
|
||||
(uint32(data[27]) shl 16) or
|
||||
(uint32(data[28]) shl 8) or
|
||||
uint32(data[29])
|
||||
|
||||
# Source port at ETH(14) + IP(20) + UDP src offset(0) = 34
|
||||
result.port = (uint16(data[34]) shl 8) or uint16(data[35])
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
# Nexus Sovereign Core: Kernel Implementation
|
||||
# target Bravo: Complete Build Unification
|
||||
|
||||
import ring, fiber, ion, sched, pty, cspace, ontology, channels
|
||||
import ring, fiber, ion, sched, pty, cspace, ontology, channels, fastpath
|
||||
import fs/vfs, fs/tar, fs/sfs
|
||||
import loader/elf
|
||||
import ../libs/membrane/term
|
||||
|
|
@ -318,21 +318,36 @@ proc fiber_netswitch_entry() {.cdecl.} =
|
|||
|
||||
kprintln("[NetSwitch] Channels verified. Sovereignty confirmed.")
|
||||
|
||||
# Fast Path imports (SPEC-700)
|
||||
proc is_utcp_tunnel(data: ptr UncheckedArray[byte], len: uint16): bool {.importc, cdecl.}
|
||||
proc strip_tunnel_headers(data: ptr UncheckedArray[byte], len: var uint16): ptr UncheckedArray[byte] {.importc, cdecl.}
|
||||
|
||||
while true:
|
||||
var pkt: IonPacket
|
||||
|
||||
# INGRESS: Driver -> NetSwitch -> Subject (chan_net_rx)
|
||||
# INGRESS: Driver -> NetSwitch -> Fast Path Filter
|
||||
if chan_netswitch_rx.recv(pkt):
|
||||
if not chan_net_rx.send(pkt):
|
||||
# kprintln("[NetSwitch] Dropped Ingress (Drop)")
|
||||
ion_free_raw(pkt.id)
|
||||
# else:
|
||||
# kprintln("[NetSwitch] Forwarded Ingress")
|
||||
# SPEC-700: Fast Path Bypass for UTCP Tunnel (UDP/9999)
|
||||
if is_utcp_tunnel(pkt.data, pkt.len):
|
||||
# FAST PATH: Bypass LwIP entirely
|
||||
var utcp_len = pkt.len
|
||||
let utcp_data = strip_tunnel_headers(pkt.data, utcp_len)
|
||||
if utcp_data != nil:
|
||||
# TODO(UTCP): Route to UTCP handler when implemented
|
||||
# For now, log and drop
|
||||
kprintln("[FastPath] UTCP Tunnel packet detected - UTCP handler pending")
|
||||
ion_free_raw(pkt.id)
|
||||
else:
|
||||
kprintln("[FastPath] Strip failed - dropping")
|
||||
ion_free_raw(pkt.id)
|
||||
else:
|
||||
# SLOW PATH: Route to LwIP via chan_net_rx
|
||||
if not chan_net_rx.send(pkt):
|
||||
ion_free_raw(pkt.id)
|
||||
|
||||
# EGRESS: Subject (chan_net_tx) -> NetSwitch -> Driver (ion_tx_push)
|
||||
if chan_net_tx.recv(pkt):
|
||||
kprintln("[NetSwitch] Forwarding Egress")
|
||||
# uart_print("[NetSwitch] Forwarding Egress\n")
|
||||
var res = ion_tx_push(pkt)
|
||||
if not res: kprintln("[NetSwitch] Drop (TX Full)")
|
||||
|
||||
|
|
|
|||
|
|
@ -216,8 +216,6 @@ proc membrane_init*() {.exportc, cdecl.} =
|
|||
var last_notified_ip: uint32 = 0
|
||||
var dhcp_retried = false
|
||||
var last_ping_time: uint32 = 0
|
||||
var looped_ping_done = false
|
||||
var gateway_ping_count = 0
|
||||
|
||||
proc glue_print_hex(v: uint64) =
|
||||
const hex_chars = "0123456789ABCDEF"
|
||||
|
|
@ -232,14 +230,9 @@ proc glue_print_hex(v: uint64) =
|
|||
|
||||
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
||||
# glue_print("[Membrane] Pump\n")
|
||||
|
||||
let now = sys_now()
|
||||
# if (now mod 1000) < 50:
|
||||
# glue_print(".")
|
||||
|
||||
# glue_print("[Membrane] Time: ")
|
||||
# glue_print_hex(uint64(now))
|
||||
# glue_print("\n")
|
||||
|
||||
# 3. Check for IP (Avoid continuous Nim string allocation/leak)
|
||||
var ip_addr: uint32
|
||||
|
|
@ -250,8 +243,6 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
glue_print("\n")
|
||||
last_notified_ip = ip_addr
|
||||
|
||||
# Force DHCP Retry if no IP after 3 seconds
|
||||
|
||||
# Force DHCP Retry if no IP after 3 seconds
|
||||
if now > 3000 and not dhcp_retried and ip_addr == 0:
|
||||
dhcp_retried = true
|
||||
|
|
@ -267,13 +258,17 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
etharp_tmr()
|
||||
last_arp_tmr = now
|
||||
|
||||
# Phase 36c: Ping Automation (Disabled/Silent)
|
||||
if now - last_ping_time > 5000:
|
||||
# Phase 37a: ICMP Ping Verification
|
||||
if now - last_ping_time > 1000:
|
||||
last_ping_time = now
|
||||
|
||||
if ip_addr != 0:
|
||||
# Trigger periodic actions here
|
||||
discard
|
||||
# {.emit: "ip_addr_t t; IP4_ADDR(&t, 10,0,2,2); ping_send(&t);".}
|
||||
glue_print("[Membrane] PING: Sending ICMP Echo...\n")
|
||||
{.emit: """
|
||||
ip_addr_t gateway;
|
||||
IP4_ADDR(&gateway, 10, 0, 2, 2);
|
||||
ping_send(&gateway);
|
||||
""".}
|
||||
|
||||
|
||||
# DHCP Timers
|
||||
|
|
@ -288,8 +283,9 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
|
||||
# 2. RX Ingress
|
||||
var pkt: IonPacket
|
||||
# glue_print("[Membrane] Exit Pump\n")
|
||||
while ion_net_rx(addr pkt):
|
||||
glue_print("[Membrane] Ingress Packet\n")
|
||||
# glue_print("[Membrane] Ingress Packet\n")
|
||||
# DEBUG: Hex dump first 32 bytes (Disabled for Ping Test)
|
||||
# {.emit: """
|
||||
# printf("[Membrane] RX Hex Dump (first 32 bytes):\n");
|
||||
|
|
|
|||
Loading…
Reference in New Issue