rumpk/core/netswitch.nim

180 lines
5.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.
## Rumpk Layer 1: Software Network Switch
# core/rumpk/core/netswitch.nim
# Phase 36.2: The Traffic Cop (Network Membrane Layer 2 Switch)
#
# Responsibilities:
# - Poll VirtIO-Net hardware for incoming packets
# - Route RX packets to s_net_rx ring (Kernel -> Userland)
# - Drain s_net_tx ring and transmit via VirtIO-Net (Userland -> Kernel)
# - Never yield during active traffic (War Mode latency optimization)
{.push stackTrace: off, lineTrace: off.}
import ion
# Forward declare fiber_yield to avoid circular import
proc fiber_yield*() {.importc, cdecl.}
proc fiber_sleep*(ms: int) {.importc, cdecl.}
# HAL Imports
proc virtio_net_poll() {.importc, cdecl.}
proc virtio_net_send(data: pointer, len: uint32) {.importc, cdecl.}
# Logging
proc kprintln(s: cstring) {.importc, cdecl.}
proc kprint(s: cstring) {.importc, cdecl.}
proc kprint_hex(v: uint64) {.importc, cdecl.}
proc get_now_ns(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
# Project LibWeb: LWF Adapter (Zig FFI)
proc lwf_validate(data: pointer, len: uint16): uint8 {.importc, cdecl.}
const ETHERTYPE_LWF = 0x4C57'u16 # "LW" — Libertaria Wire Frame
# Membrane Infrastructure (LwIP Glue)
proc membrane_init*() {.importc, cdecl.}
proc pump_membrane_stack*() {.importc, cdecl.}
var netswitch_initialized: bool = false
proc netswitch_init*() =
## Initialize network channels and populate SysTable
## MUST be called before userland starts!
ion_init_network()
kprintln("[NetSwitch] Network Rings Initialized")
netswitch_initialized = true
proc netswitch_attach_systable*(sys: ptr SysTable) =
## Attach network ring pointers to SysTable for userland access
sys.s_net_rx = chan_net_rx.ring
sys.s_net_tx = chan_net_tx.ring
kprintln("[NetSwitch] SysTable Rings Attached")
type
EthHeader* {.packed.} = object
dst*: array[6, byte]
src*: array[6, byte]
ethertype*: uint16 # Big Endian
proc swap16(v: uint16): uint16 =
((v and 0xFF) shl 8) or (v shr 8)
proc netswitch_process_packet(pkt: IonPacket): bool =
## Layer 2 Demultiplexer
if pkt.len < 14:
ion_free(pkt)
return false
let eth = cast[ptr EthHeader](pkt.data)
let etype = swap16(eth.ethertype)
case etype:
of 0x0800, 0x0806, 0x86DD: # IPv4, ARP, IPv6
# NOTE(LibWeb): IPv6 is the first-class citizen for sovereign mesh.
# Most chapter nodes sit behind consumer NAT — IPv6 provides
# end-to-end addressability without traversal hacks.
# IPv4 is the fallback, not the default. Membrane/Transport
# layer enforces preference order (IPv6 → IPv4).
# Route to Legacy/LwIP Membrane
if not chan_net_rx.send(pkt):
# Ring full (Backpressure)
ion_free(pkt)
return false
return true
of 0x88B5: # Sovereign UTCP (SPEC-700)
# TODO: Route to dedicated UTCP channel
# kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
ion_free(pkt)
return true # Handled (dropped)
of ETHERTYPE_LWF: # Project LibWeb: Libertaria Wire Frame
# Validate LWF magic before routing (Fast Drop)
let lwf_data = cast[pointer](cast[uint64](pkt.data) + 14) # Skip Eth header
let lwf_len = if pkt.len > 14: uint16(pkt.len - 14) else: 0'u16
if lwf_len >= 88 and lwf_validate(lwf_data, lwf_len) == 1:
# Valid LWF — route to dedicated LWF channel
if not chan_lwf_rx.send(pkt):
ion_free(pkt) # Backpressure
return false
return true
else:
# Invalid LWF frame — drop
ion_free(pkt)
return false
else:
# Drop unknown EtherTypes (Security/Sovereignty)
ion_free(pkt)
return false
proc fiber_netswitch_entry*() {.cdecl.} =
kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
var rx_activity: bool = false
var tx_activity: bool = false
var rx_count: uint64 = 0
var tx_count: uint64 = 0
var last_stat_print: uint64 = 0
while true:
rx_activity = false
tx_activity = false
# 1. Drive the hardware poll (fills chan_netswitch_rx)
virtio_net_poll()
# [Cleaned] Driven by Userland now
# 2. Consume from the Driver -> Switch internal ring
var raw_pkt: IonPacket
while chan_netswitch_rx.recv(raw_pkt):
if netswitch_process_packet(raw_pkt):
rx_activity = true
inc rx_count
# 3. TX PATH: Userland -> Hardware (Legacy/LwIP)
var tx_pkt: IonPacket
while chan_net_tx.recv(tx_pkt):
if tx_pkt.data != nil and tx_pkt.len > 0:
virtio_net_send(cast[pointer](tx_pkt.data), uint32(tx_pkt.len))
inc tx_count
ion_free(tx_pkt)
tx_activity = true
# 3b. TX PATH: LWF Egress (Project LibWeb)
var lwf_pkt: IonPacket
while chan_lwf_tx.recv(lwf_pkt):
if lwf_pkt.data != nil and lwf_pkt.len > 0:
virtio_net_send(cast[pointer](lwf_pkt.data), uint32(lwf_pkt.len))
inc tx_count
ion_free(lwf_pkt)
tx_activity = true
# 4. Periodically print stats
let now = get_now_ns()
if now - last_stat_print > 5000000000'u64: # 5 seconds
kprint("[NetSwitch] STATS - RX: ")
kprint_hex(rx_count)
kprint(" TX: ")
kprint_hex(tx_count)
kprintln("")
last_stat_print = now
# 5. Yield Strategy
if rx_activity or tx_activity:
# War Mode: High priority processing
continue
else:
fiber_sleep(10)
{.pop.}