# 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()