rumpk/core/ion/ion_switch.nim

122 lines
3.6 KiB
Nim

# ION Soft-Switch
# Sovereign Dispatch Logic
import memory
import ../ring
import ../net # For LwIP fallback
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
type
FlowType* = enum
FLOW_LWIP # The Unix Path
FLOW_DIRECT # The ION Path
# A Subscription to the Bus
NetFlow* = object
port*: uint16
ring*: ptr RingBuffer[IonPacket, 256] # Direct injection ring
fType*: FlowType
var
# The Lookup Table (Simple Array for Phase 7)
# Index = Port Number.
flow_table: array[65536, ptr NetFlow]
proc ion_register*(port: uint16, flow: ptr NetFlow) =
flow_table[port] = flow
proc parse_dest_port(data: ptr UncheckedArray[byte]): uint16 =
# Ethernet Type at 12. IP Protocol at 23. Dest Port at 36.
if data[12] == 0x08 and data[13] == 0x00:
# IPv4
let proto = data[23]
if proto == 17: # UDP
let dport = (uint16(data[36]) shl 8) or uint16(data[37])
return dport
return 0
proc dispatch_packet(data: ptr UncheckedArray[byte], len: int, id: uint16) =
let dest_port = parse_dest_port(data)
let flow = if dest_port != 0: flow_table[dest_port] else: nil
if dest_port == 8080:
var msg = "[ION SW] 8080 Hit! RingPtr="
console_write(addr msg[0], csize_t(msg.len))
# Simple address check (first 8 bytes of ptr)
if flow != nil:
var hex_str = "ADR\n"
console_write(addr hex_str[0], 4)
var pkt: IonPacket
pkt.id = id
pkt.len = uint16(len)
pkt.data = data
if flow != nil and flow.fType == FLOW_DIRECT:
if flow.ring[].push(pkt):
var msg = "[ION SW] Pushed to Ring!\n"
console_write(addr msg[0], csize_t(msg.len))
else:
var msg = "[ION SW] Ring FULL!\n"
console_write(addr msg[0], csize_t(msg.len))
ion_free_raw(id)
else:
discard net_ingest_packet(cast[ptr byte](pkt.data), int(pkt.len))
ion_free_raw(id)
proc ion_ingress*(id: uint16, len: uint16) {.exportc.} =
## The Hot Path. Called by VirtIO Interrupt/Poller.
## Takes a Slab ID that has been filled by NIC or local TX.
const VIRTIO_NET_HDR_SIZE = 10
let slab_base = ion_get_virt(id)
if len <= VIRTIO_NET_HDR_SIZE:
# Packet too small (just header or less)
ion_free_raw(id)
return
# Adjust for Headroom (Zero-Copy Pointer Math)
# eth_frame points to the Ethernet Header, skipping VirtIO Header
let eth_frame = cast[ptr UncheckedArray[byte]](cast[uint](slab_base) + VIRTIO_NET_HDR_SIZE)
let eth_len = int(len) - VIRTIO_NET_HDR_SIZE
# DEBUG: Print RX packet info (Full Eth Header)
var prefix = "[ION] RX: "
console_write(addr prefix[0], csize_t(prefix.len))
const hexDigits = "0123456789ABCDEF"
for i in 0..<14:
let b = eth_frame[i]
var hex: array[3, char]
hex[0] = hexDigits[int(b shr 4)]
hex[1] = hexDigits[int(b and 0xF)]
hex[2] = if i == 5 or i == 11: '|' else: ' '
if i == 13: hex[2] = '\n'
console_write(addr hex[0], 3)
dispatch_packet(eth_frame, eth_len, id)
proc ion_egress*(pkt: IonPacket) {.exportc.} =
## The Fast Path TX (Standard Interface)
if not ion_tx_push(pkt):
# Backpressure / Ring Full -> Drop
var msg = "[ION SW] TX Ring FULL! Dropping.\n"
console_write(addr msg[0], csize_t(msg.len))
ion_free(pkt)
proc ion_egress_to_port*(port: uint16, pkt: IonPacket) {.exportc.} =
## Route a packet to a specific ION Port.
## Port 0 is reserved for Kernel Console.
if port == 0:
when defined(is_kernel):
console_write(pkt.data, csize_t(pkt.len))
ion_free(pkt)
else:
# In the Membrane, Port 0 egresses to the Bus
ion_egress(pkt)
else:
# Future: Internal routing via flow_table
ion_egress(pkt)