226 lines
7.0 KiB
Nim
226 lines
7.0 KiB
Nim
# Rumpk LwIP Bridge
|
|
# FFI wrapper for LwIP static library
|
|
|
|
import ring
|
|
|
|
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
|
|
|
const
|
|
MAX_PACKET_SIZE = 1536
|
|
RING_SIZE = 256
|
|
|
|
type
|
|
LwipErr* = int8
|
|
|
|
const
|
|
ERR_OK* = 0
|
|
ERR_MEM* = -1
|
|
ERR_BUF* = -2
|
|
ERR_TIMEOUT* = -3
|
|
ERR_RTE* = -4
|
|
ERR_INPROGRESS* = -5
|
|
ERR_VAL* = -6
|
|
ERR_WOULDBLOCK* = -7
|
|
ERR_USE* = -8
|
|
ERR_ALREADY* = -9
|
|
ERR_ISCONN* = -10
|
|
ERR_CONN* = -11
|
|
ERR_IF* = -12
|
|
ERR_ABRT* = -13
|
|
ERR_RST* = -14
|
|
ERR_CLSD* = -15
|
|
ERR_ARG* = -16
|
|
|
|
type
|
|
PbufLayer* = cint
|
|
PbufType* = cint
|
|
|
|
Pbuf* {.importc: "struct pbuf", header: "lwip/pbuf.h",
|
|
incompleteStruct.} = object
|
|
next*: ptr Pbuf
|
|
payload*: pointer
|
|
tot_len*: uint16
|
|
len*: uint16
|
|
|
|
IpAddr* {.importc: "ip4_addr_t", header: "lwip/ip_addr.h".} = object
|
|
addr_val* {.importc: "addr".}: uint32
|
|
|
|
NetIf* {.importc: "struct netif", header: "lwip/netif.h",
|
|
incompleteStruct.} = object
|
|
next*: ptr NetIf
|
|
ip_addr*, netmask*, gw*: IpAddr
|
|
|
|
var
|
|
PBUF_RAW* {.importc: "PBUF_RAW", header: "lwip/pbuf.h", nodecl.}: cint
|
|
PBUF_RAM* {.importc: "PBUF_RAM", header: "lwip/pbuf.h", nodecl.}: cint
|
|
PBUF_POOL* {.importc: "PBUF_POOL", header: "lwip/pbuf.h", nodecl.}: cint
|
|
|
|
type
|
|
NetPacket* = object
|
|
len*: int
|
|
data*: array[MAX_PACKET_SIZE, byte]
|
|
|
|
var netRing*: RingBuffer[NetPacket, RING_SIZE]
|
|
var rumpk_netif*: NetIf
|
|
var ip, mask, gw: IpAddr
|
|
|
|
# FFI Procedures
|
|
proc lwip_init*() {.importc: "lwip_init", header: "lwip/init.h".}
|
|
|
|
proc netif_add*(netif: ptr NetIf; ipaddr, netmask, gw: ptr IpAddr; state: pointer;
|
|
init: proc(netif: ptr NetIf): LwipErr {.cdecl.};
|
|
input: proc(p: ptr Pbuf; netif: ptr NetIf): LwipErr {.cdecl.}): ptr NetIf {.importc: "netif_add",
|
|
header: "lwip/netif.h".}
|
|
|
|
proc netif_set_up*(netif: ptr NetIf) {.importc: "netif_set_up",
|
|
header: "lwip/netif.h".}
|
|
proc netif_set_link_up*(netif: ptr NetIf) {.importc: "netif_set_link_up",
|
|
header: "lwip/netif.h".}
|
|
proc netif_set_default*(netif: ptr NetIf) {.importc: "netif_set_default",
|
|
header: "lwip/netif.h".}
|
|
proc sys_check_timeouts() {.importc: "sys_check_timeouts",
|
|
header: "lwip/timeouts.h".}
|
|
|
|
proc pbuf_alloc*(layer: PbufLayer; length: uint16;
|
|
ptype: PbufType): ptr Pbuf {.importc: "pbuf_alloc", header: "lwip/pbuf.h".}
|
|
proc pbuf_free*(p: ptr Pbuf): uint8 {.importc: "pbuf_free",
|
|
header: "lwip/pbuf.h".}
|
|
|
|
proc ethernet_input*(p: ptr Pbuf; netif: ptr NetIf): LwipErr {.importc: "ethernet_input",
|
|
header: "netif/ethernet.h".}
|
|
|
|
# Sovereign Integration (HAL)
|
|
proc virtio_net_poll() {.importc: "virtio_net_poll", cdecl.}
|
|
proc virtio_net_send(data: pointer; len: csize_t) {.importc: "virtio_net_send", cdecl.}
|
|
|
|
# Architecture Agnostic IP Packing
|
|
func htonl(x: uint32): uint32 =
|
|
when cpuEndian == littleEndian:
|
|
(x shr 24) or ((x shr 8) and 0xff00) or ((x shl 8) and 0xff0000) or (x shl 24)
|
|
else: x
|
|
|
|
func packIp(a, b, c, d: uint8): uint32 =
|
|
uint32(a) shl 24 or uint32(b) shl 16 or uint32(c) shl 8 or uint32(d)
|
|
|
|
proc link_output(netif: ptr NetIf; p: ptr Pbuf): LwipErr {.cdecl.} =
|
|
## Callback from LwIP when a packet needs to be sent
|
|
if p != nil:
|
|
# Phase 7: We send the whole packet in one go.
|
|
virtio_net_send(p.payload, csize_t(p.tot_len))
|
|
return ERR_OK
|
|
return ERR_VAL
|
|
|
|
proc net_driver_init*(netif: ptr NetIf): LwipErr {.cdecl.} =
|
|
# Driver initialization hook
|
|
{.emit: """
|
|
((struct netif*)`netif`)->mtu = 1500;
|
|
((struct netif*)`netif`)->hwaddr_len = 6;
|
|
((struct netif*)`netif`)->hwaddr[0] = 0x52;
|
|
((struct netif*)`netif`)->hwaddr[1] = 0x54;
|
|
((struct netif*)`netif`)->hwaddr[2] = 0x00;
|
|
((struct netif*)`netif`)->hwaddr[3] = 0x12;
|
|
((struct netif*)`netif`)->hwaddr[4] = 0x34;
|
|
((struct netif*)`netif`)->hwaddr[5] = 0x56;
|
|
|
|
// NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_ETHERNET
|
|
((struct netif*)`netif`)->flags = 0x02 | 0x08 | 0x10 | 0x40;
|
|
|
|
// Wire Tx output
|
|
((struct netif*)`netif`)->linkoutput = `link_output`;
|
|
""".}
|
|
return ERR_OK
|
|
|
|
proc net_init*() =
|
|
## Initialize the networking subsystem
|
|
netRing.init()
|
|
lwip_init()
|
|
|
|
# QEMU User Networking Defaults (10.0.2.15)
|
|
ip.addr_val = htonl(packIp(10, 0, 2, 15))
|
|
mask.addr_val = htonl(packIp(255, 255, 255, 0))
|
|
gw.addr_val = htonl(packIp(10, 0, 2, 2))
|
|
|
|
# Register the Interface
|
|
discard netif_add(addr rumpk_netif, addr ip, addr mask, addr gw,
|
|
nil, net_driver_init,
|
|
cast[proc(p: ptr Pbuf;
|
|
netif: ptr NetIf): LwipErr {.cdecl.}](ethernet_input))
|
|
|
|
# Bring it UP
|
|
netif_set_up(addr rumpk_netif)
|
|
netif_set_link_up(addr rumpk_netif)
|
|
netif_set_default(addr rumpk_netif)
|
|
|
|
# Force Gratuitous ARP to announce our MAC to the network/SLIRP
|
|
proc etharp_gratuitous(netif: ptr NetIf) {.importc: "etharp_gratuitous",
|
|
header: "netif/etharp.h".}
|
|
etharp_gratuitous(addr rumpk_netif)
|
|
|
|
proc net_ingest_packet*(data: ptr byte; len: int): bool {.exportc.} =
|
|
## Ingest a raw ethernet frame from the hardware
|
|
var pkt: NetPacket
|
|
if len > MAX_PACKET_SIZE: return false
|
|
|
|
pkt.len = len
|
|
copyMem(addr pkt.data[0], data, len)
|
|
return netRing.push(pkt)
|
|
|
|
proc fiber_yield() =
|
|
# Don't use WFI - it blocks without interrupts configured
|
|
# Use simple spin-loop for now
|
|
for i in 0..100:
|
|
{.emit: "asm volatile(\"nop\");".}
|
|
|
|
proc net_loop_cycle*(netif: ptr NetIf) =
|
|
## One cycle of the networking logic (Poll + Drain + LwIP)
|
|
|
|
# 1. Poll Hardware
|
|
virtio_net_poll()
|
|
|
|
# 2. Drain the Ring
|
|
var work_done = false
|
|
while not netRing.isEmpty:
|
|
let (ok, pkt) = netRing.pop()
|
|
if ok:
|
|
work_done = true
|
|
let p = pbuf_alloc(PBUF_RAW, uint16(pkt.len), PBUF_POOL)
|
|
if p != nil:
|
|
copyMem(p.payload, unsafeAddr pkt.data[0], pkt.len)
|
|
let err = ethernet_input(p, netif)
|
|
if err != ERR_OK:
|
|
var msg = "[Net] ethernet_input failed\n"
|
|
console_write(addr msg[0], csize_t(msg.len))
|
|
discard pbuf_free(p)
|
|
else:
|
|
var msg = "[Net] pbuf_alloc FAILED (OOM?)\n"
|
|
console_write(addr msg[0], csize_t(msg.len))
|
|
|
|
# 3. LwIP Maintenance
|
|
sys_check_timeouts()
|
|
|
|
# 4. Periodic Activity (Every 5000 loops)
|
|
const PERIOD = 5000
|
|
var counter {.global.} = 0
|
|
counter += 1
|
|
if counter >= PERIOD:
|
|
counter = 0
|
|
# Send UDP Heartbeat to Gateway
|
|
var frame: array[64, byte]
|
|
# (Simplified frame construction for clarity)
|
|
for i in 0..63: frame[i] = 0
|
|
frame[0] = 0x52; frame[1] = 0x55; frame[2] = 0x0A; frame[3] = 0x00; frame[
|
|
4] = 0x02; frame[5] = 0x02 # Dst
|
|
frame[6] = 0x52; frame[7] = 0x54; frame[8] = 0x00; frame[9] = 0x12; frame[
|
|
10] = 0x34; frame[11] = 0x56 # Src
|
|
frame[12] = 0x08; frame[13] = 0x00 # IPv4
|
|
frame[14] = 0x45; frame[23] = 17 # UDP
|
|
frame[26] = 10; frame[27] = 0; frame[28] = 2; frame[29] = 15 # Src IP
|
|
frame[30] = 10; frame[31] = 0; frame[32] = 2; frame[33] = 2 # Dst IP
|
|
frame[34] = 0x1F; frame[35] = 0x90; frame[36] = 0x1F; frame[37] = 0x90 # Ports 8080
|
|
frame[42] = ord('H').byte; frame[43] = ord('I').byte
|
|
virtio_net_send(addr frame[0], 64)
|
|
|
|
# 5. Yield / Governor
|
|
if not work_done:
|
|
fiber_yield()
|