diff --git a/core/fastpath.nim b/core/fastpath.nim new file mode 100644 index 0000000..c59dddb --- /dev/null +++ b/core/fastpath.nim @@ -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]) diff --git a/core/kernel.nim b/core/kernel.nim index 685df55..2c23ce3 100644 --- a/core/kernel.nim +++ b/core/kernel.nim @@ -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)") diff --git a/libs/membrane/net_glue.nim b/libs/membrane/net_glue.nim index e0e13f6..46d3ca4 100644 --- a/libs/membrane/net_glue.nim +++ b/libs/membrane/net_glue.nim @@ -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");