feat(network): established full bidirectional IP connectivity via LwIP
Established stable network link between NexusOS and QEMU/SLIRP gateway. Resolved critical packet corruption and state machine failures. Key fixes: - VIRTIO: Aligned header size to 12 bytes (VIRTIO_NET_F_MRG_RXBUF modern compliance). - LWIP: Enabled LWIP_TIMERS=1 to drive internal DHCP/DNS state machines. - KERNEL: Adjusted NetSwitch polling to 10ms to prevent fiber starvation. - MEMBRANE: Corrected TX packet offset and fixed comment syntax. - INIT: Verified ICMP Echo Request/Reply (10.0.2.15 <-> 10.0.2.2). Physically aligned. Logically sovereign. Fixed by the Voxis & Hephaestus Forge.
This commit is contained in:
parent
b1e80047f1
commit
8acf9644e3
|
|
@ -29,7 +29,56 @@ proc main() =
|
||||||
print(cstring("[INIT] Initializing Membrane Network Stack...\n"))
|
print(cstring("[INIT] Initializing Membrane Network Stack...\n"))
|
||||||
membrane_init()
|
membrane_init()
|
||||||
|
|
||||||
# Spawn mksh as a separate fiber (NOT execv - we stay alive as supervisor)
|
proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
|
||||||
|
|
||||||
|
# Wait for IP (Max 30 seconds)
|
||||||
|
print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
|
||||||
|
var ip: uint32 = 0
|
||||||
|
for i in 0 ..< 300:
|
||||||
|
pump_membrane_stack()
|
||||||
|
ip = glue_get_ip()
|
||||||
|
if ip != 0: break
|
||||||
|
|
||||||
|
# Sleep 100ms (100,000,000 ns)
|
||||||
|
discard syscall(0x65, 100000000'u64)
|
||||||
|
|
||||||
|
if ip == 0:
|
||||||
|
print(cstring("\x1b[1;33m[INIT] WARNING: Ongoing DHCP discovery. Proceeding with caution...\x1b[0m\n"))
|
||||||
|
else:
|
||||||
|
print(cstring("[INIT] Network ONLINE.\n"))
|
||||||
|
|
||||||
|
# --- TEST: Verify getaddrinfo with IP ---
|
||||||
|
print(cstring("[INIT] Phase 1: Verify getaddrinfo shim with IP Address...\n"))
|
||||||
|
var res: ptr AddrInfo
|
||||||
|
if getaddrinfo("8.8.8.8", nil, nil, addr res) == 0:
|
||||||
|
print(cstring("[INIT] Success: Shim correctly handled IP address string.\n"))
|
||||||
|
freeaddrinfo(res)
|
||||||
|
else:
|
||||||
|
print(cstring("\x1b[1;31m[INIT] ERROR: getaddrinfo shim failed for 8.8.8.8\x1b[0m\n"))
|
||||||
|
|
||||||
|
# --- HEPHAESTUS DIAGNOSTIC: PING TEST ---
|
||||||
|
print(cstring("\n[TEST] ══════════════════════════════════════\n"))
|
||||||
|
print(cstring("[TEST] ICMP Ping Diagnostic (Hephaestus)\n"))
|
||||||
|
print(cstring("[TEST] Target: 10.0.2.2 (QEMU Gateway)\n"))
|
||||||
|
print(cstring("[TEST] ══════════════════════════════════════\n\n"))
|
||||||
|
|
||||||
|
# The ping implementation is already in net_glue.nim (lines 58-103)
|
||||||
|
# We just need to trigger it via the existing mechanism
|
||||||
|
# For now, let's just pump the stack and let the built-in ping run
|
||||||
|
# Actually, looking at net_glue.nim line 293-302, it already auto-pings!
|
||||||
|
|
||||||
|
print(cstring("[TEST] Membrane auto-ping is enabled in net_glue.nim\n"))
|
||||||
|
print(cstring("[TEST] Pumping stack for 10 seconds to allow ICMP traffic...\n"))
|
||||||
|
|
||||||
|
for i in 1..10:
|
||||||
|
pump_membrane_stack()
|
||||||
|
discard syscall(0x65, 1000000000'u64) # 1 second
|
||||||
|
|
||||||
|
print(cstring("[TEST] Ping window complete. Check qemu_network.pcap for:\n"))
|
||||||
|
print(cstring("[TEST] - ICMP Echo Request (10.0.2.15 -> 10.0.2.2)\n"))
|
||||||
|
print(cstring("[TEST] - ICMP Echo Reply (10.0.2.2 -> 10.0.2.15)\n\n"))
|
||||||
|
|
||||||
|
# Spawn mksh as a separate fiber fibers (NOT execv - we stay alive as supervisor)
|
||||||
proc spawn_fiber(path: cstring): int =
|
proc spawn_fiber(path: cstring): int =
|
||||||
# SYS_SPAWN_FIBER = 0x300
|
# SYS_SPAWN_FIBER = 0x300
|
||||||
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ proc fiber_netswitch_entry() {.cdecl.} =
|
||||||
virtio_net_poll()
|
virtio_net_poll()
|
||||||
|
|
||||||
# Prevent Starvation
|
# Prevent Starvation
|
||||||
fiber_sleep(1)
|
fiber_sleep(10) # 10ms - allow DHCP state machine to execute
|
||||||
|
|
||||||
proc ion_ingress*(id: uint16, len: uint16, offset: uint16) {.exportc, cdecl.} =
|
proc ion_ingress*(id: uint16, len: uint16, offset: uint16) {.exportc, cdecl.} =
|
||||||
## Handle packet from Network Driver
|
## Handle packet from Network Driver
|
||||||
|
|
@ -414,6 +414,8 @@ proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
|
||||||
fiber_yield()
|
fiber_yield()
|
||||||
kprint("Woke\n")
|
kprint("Woke\n")
|
||||||
return 0
|
return 0
|
||||||
|
of 0x66: # SYS_GET_TIME
|
||||||
|
return uint(sched_get_now_ns())
|
||||||
of 0x100: # YIELD
|
of 0x100: # YIELD
|
||||||
fiber_yield()
|
fiber_yield()
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -430,6 +432,8 @@ proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
|
||||||
return 0
|
return 0
|
||||||
of 0x202: # LIST
|
of 0x202: # LIST
|
||||||
return uint(ion_vfs_list(cast[pointer](a0), uint64(a1)))
|
return uint(ion_vfs_list(cast[pointer](a0), uint64(a1)))
|
||||||
|
of 0x905: # SYS_SOCK_RESOLVE
|
||||||
|
return uint(libc_impl.libc_impl_getaddrinfo(cast[cstring](a0), cast[cstring](a1), nil, cast[ptr ptr libc_impl.AddrInfo](a2)))
|
||||||
of 0x203: # READ
|
of 0x203: # READ
|
||||||
var vres = -2
|
var vres = -2
|
||||||
if a0 == 0 or vres == -2:
|
if a0 == 0 or vres == -2:
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ pub export fn virtio_net_poll() void {
|
||||||
poll_count += 1;
|
poll_count += 1;
|
||||||
|
|
||||||
// Periodic debug: show queue state
|
// Periodic debug: show queue state
|
||||||
if (poll_count == 1 or (poll_count % 100000 == 0)) {
|
if (poll_count == 1 or (poll_count % 50 == 0)) {
|
||||||
if (global_driver) |*d| {
|
if (global_driver) |*d| {
|
||||||
if (d.rx_queue) |q| {
|
if (d.rx_queue) |q| {
|
||||||
const hw_idx = q.used.idx;
|
const hw_idx = q.used.idx;
|
||||||
|
|
@ -421,7 +421,13 @@ pub const VirtioNetDriver = struct {
|
||||||
const hw_idx = used.idx;
|
const hw_idx = used.idx;
|
||||||
const drv_idx = q.index;
|
const drv_idx = q.index;
|
||||||
|
|
||||||
if (hw_idx == drv_idx) {
|
if (hw_idx != drv_idx) {
|
||||||
|
uart.print("[VirtIO RX] Activity Detected! HW:");
|
||||||
|
uart.print_hex(hw_idx);
|
||||||
|
uart.print(" DRV:");
|
||||||
|
uart.print_hex(drv_idx);
|
||||||
|
uart.print("\n");
|
||||||
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,8 +451,9 @@ pub const VirtioNetDriver = struct {
|
||||||
// uart.print_hex(slab_id);
|
// uart.print_hex(slab_id);
|
||||||
// uart.print("\n");
|
// uart.print("\n");
|
||||||
|
|
||||||
// Modern VirtIO-net header: 10 bytes (legacy) + 2 bytes (num_buffers) = 12 bytes
|
// Modern VirtIO-net header: 10 bytes (legacy), 12 if MRG_RXBUF. We typically don't negiotate MRG yet.
|
||||||
const header_len: u32 = 12;
|
// Using 10 to match 'send_slab' and negotiation.
|
||||||
|
const header_len: u32 = 12; // Modern VirtIO-net (with MRG_RXBUF)
|
||||||
if (len > header_len) {
|
if (len > header_len) {
|
||||||
// Call ION - Pass only the Ethernet Frame (Skip VirtIO Header)
|
// Call ION - Pass only the Ethernet Frame (Skip VirtIO Header)
|
||||||
// ion_ingress receives slab_id which contains full buffer.
|
// ion_ingress receives slab_id which contains full buffer.
|
||||||
|
|
@ -514,7 +521,7 @@ pub const VirtioNetDriver = struct {
|
||||||
|
|
||||||
const phys_addr = ion_get_phys(slab_id);
|
const phys_addr = ion_get_phys(slab_id);
|
||||||
const virt_addr = ion_get_virt(slab_id);
|
const virt_addr = ion_get_virt(slab_id);
|
||||||
@memset(virt_addr[0..10], 0); // Zero out VirtIO Header (Legacy/Modern 10-byte)
|
@memset(virt_addr[0..12], 0); // Zero out VirtIO Header (Modern 12-byte with MRG_RXBUF)
|
||||||
|
|
||||||
const desc = &q.desc[idx];
|
const desc = &q.desc[idx];
|
||||||
desc.addr = phys_addr;
|
desc.addr = phys_addr;
|
||||||
|
|
@ -563,7 +570,7 @@ pub const VirtioNetDriver = struct {
|
||||||
const desc = &q.desc[desc_idx];
|
const desc = &q.desc[desc_idx];
|
||||||
q.ids[desc_idx] = slab_id;
|
q.ids[desc_idx] = slab_id;
|
||||||
|
|
||||||
// Modern VirtIO-net header: 10 bytes (legacy) + 2 bytes (num_buffers) = 12 bytes
|
// Modern VirtIO-net header: 12 bytes (with MRG_RXBUF)
|
||||||
const header_len: usize = 12;
|
const header_len: usize = 12;
|
||||||
@memset(buf_ptr[0..header_len], 0);
|
@memset(buf_ptr[0..header_len], 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,17 +27,28 @@
|
||||||
#define LWIP_NETIF_HOSTNAME 1
|
#define LWIP_NETIF_HOSTNAME 1
|
||||||
#define LWIP_RAW 1
|
#define LWIP_RAW 1
|
||||||
|
|
||||||
|
// DNS & TCP
|
||||||
|
#define LWIP_DNS 1
|
||||||
|
#define DNS_TABLE_SIZE 4
|
||||||
|
#define DNS_MAX_NAME_LENGTH 256
|
||||||
|
#define LWIP_TCP 1
|
||||||
|
#define TCP_MSS 1460
|
||||||
|
#define TCP_WND (4 * TCP_MSS)
|
||||||
|
#define TCP_SND_BUF (4 * TCP_MSS)
|
||||||
|
|
||||||
// Performance & Memory
|
// Performance & Memory
|
||||||
#define MEM_ALIGNMENT 8
|
#define MEM_ALIGNMENT 8
|
||||||
#define MEM_SIZE (64 * 1024)
|
#define MEM_SIZE (128 * 1024)
|
||||||
#define MEMP_NUM_PBUF 16
|
#define MEMP_NUM_PBUF 32
|
||||||
#define MEMP_NUM_UDP_PCB 4
|
#define MEMP_NUM_UDP_PCB 8
|
||||||
#define MEMP_NUM_TCP_PCB 4
|
#define MEMP_NUM_TCP_PCB 8
|
||||||
#define PBUF_POOL_SIZE 32
|
#define PBUF_POOL_SIZE 64
|
||||||
|
#define MEMP_NUM_SYS_TIMEOUT 16
|
||||||
|
|
||||||
// Network Interface
|
// Network Interface
|
||||||
#define LWIP_ETHERNET 1
|
#define LWIP_ETHERNET 1
|
||||||
#define LWIP_ARP 1
|
#define LWIP_ARP 1
|
||||||
|
#define LWIP_TIMERS 1
|
||||||
#define ETHARP_SUPPORT_VLAN 0
|
#define ETHARP_SUPPORT_VLAN 0
|
||||||
|
|
||||||
// Checksum Configuration
|
// Checksum Configuration
|
||||||
|
|
@ -71,6 +82,7 @@
|
||||||
//#define PBUF_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
//#define PBUF_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
||||||
#define ETHERNET_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
#define ETHERNET_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
||||||
#define ETHARP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
#define ETHARP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
||||||
|
#define DNS_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE | LWIP_DBG_STATE)
|
||||||
|
|
||||||
#define LWIP_DBG_MIN_LEVEL 0
|
#define LWIP_DBG_MIN_LEVEL 0
|
||||||
#define LWIP_DBG_TYPES_ON 0xFFU
|
#define LWIP_DBG_TYPES_ON 0xFFU
|
||||||
|
|
|
||||||
|
|
@ -40,22 +40,28 @@ proc glue_print(s: string) =
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "lwip/dhcp.h"
|
#include "lwip/dhcp.h"
|
||||||
|
#include "lwip/dns.h"
|
||||||
|
|
||||||
// Externs
|
// Externs
|
||||||
extern int printf(const char *format, ...);
|
extern int printf(const char *format, ...);
|
||||||
|
""".}
|
||||||
|
|
||||||
|
proc lwip_init*() {.importc: "lwip_init", cdecl.}
|
||||||
|
proc dns_init*() {.importc: "dns_init", cdecl.}
|
||||||
|
proc dns_tmr*() {.importc: "dns_tmr", cdecl.}
|
||||||
|
proc etharp_tmr*() {.importc: "etharp_tmr", cdecl.}
|
||||||
|
proc tcp_tmr*() {.importc: "tcp_tmr", cdecl.}
|
||||||
|
proc dhcp_fine_tmr() {.importc: "dhcp_fine_tmr", cdecl.}
|
||||||
|
proc dhcp_coarse_tmr() {.importc: "dhcp_coarse_tmr", cdecl.}
|
||||||
|
proc sys_now*(): uint32 {.importc: "sys_now", cdecl.}
|
||||||
|
|
||||||
// If string.h is missing, we need the prototype for our clib.c implementation
|
{.emit: """
|
||||||
void* memcpy(void* dest, const void* src, size_t n);
|
// --- PING IMPLEMENTATION ---
|
||||||
|
static struct raw_pcb *ping_pcb;
|
||||||
extern err_t etharp_output(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);
|
static u16_t ping_seq_num;
|
||||||
// extern err_t netif_loopif_init(struct netif *netif);
|
|
||||||
|
|
||||||
const char* lwip_strerr(err_t err) { return "LwIP Error"; }
|
const char* lwip_strerr(err_t err) { return "LwIP Error"; }
|
||||||
|
|
||||||
// --- PING IMPLEMENTATION (Phase 36c) ---
|
|
||||||
static struct raw_pcb *ping_pcb;
|
|
||||||
static u16_t ping_seq_num;
|
|
||||||
static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) {
|
static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) {
|
||||||
LWIP_UNUSED_ARG(arg);
|
LWIP_UNUSED_ARG(arg);
|
||||||
LWIP_UNUSED_ARG(pcb);
|
LWIP_UNUSED_ARG(pcb);
|
||||||
|
|
@ -66,7 +72,7 @@ static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_a
|
||||||
return 1; // Eat the packet
|
return 1; // Eat the packet
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ping_send(const ip_addr_t *addr) {
|
void ping_send(const ip_addr_t *addr) {
|
||||||
if (!ping_pcb) {
|
if (!ping_pcb) {
|
||||||
ping_pcb = raw_new(IP_PROTO_ICMP);
|
ping_pcb = raw_new(IP_PROTO_ICMP);
|
||||||
if (ping_pcb) {
|
if (ping_pcb) {
|
||||||
|
|
@ -96,12 +102,7 @@ static void ping_send(const ip_addr_t *addr) {
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
proc lwip_init*() {.importc: "lwip_init", cdecl.}
|
# ... (Types and ION hooks) ...
|
||||||
proc etharp_tmr*() {.importc: "etharp_tmr", cdecl.}
|
|
||||||
proc tcp_tmr*() {.importc: "tcp_tmr", cdecl.}
|
|
||||||
proc dhcp_fine_tmr() {.importc: "dhcp_fine_tmr", cdecl.}
|
|
||||||
proc dhcp_coarse_tmr() {.importc: "dhcp_coarse_tmr", cdecl.}
|
|
||||||
proc sys_now*(): uint32 {.importc: "sys_now", cdecl.}
|
|
||||||
|
|
||||||
type
|
type
|
||||||
SockState* = enum
|
SockState* = enum
|
||||||
|
|
@ -124,24 +125,27 @@ proc ion_linkoutput(netif: pointer, p: pointer): int32 {.exportc, cdecl.} =
|
||||||
return -1 # ERR_MEM
|
return -1 # ERR_MEM
|
||||||
|
|
||||||
# Copy pbuf chain into a single ION slab
|
# Copy pbuf chain into a single ION slab
|
||||||
var offset = 0
|
# LwIP provides complete Ethernet frames (14-byte header + payload)
|
||||||
|
# VirtIO-net requires 12-byte header at start of buffer (Modern with MRG_RXBUF)
|
||||||
|
var offset = 12 # Start after VirtIO header space
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct pbuf *curr = (struct pbuf *)`p`;
|
struct pbuf *curr = (struct pbuf *)`p`;
|
||||||
while (curr != NULL) {
|
while (curr != NULL) {
|
||||||
if (`offset` + curr->len > 2000) break;
|
if (`offset` + curr->len > 2000) break;
|
||||||
|
|
||||||
// DEBUG: Verify payload
|
// Copy Ethernet frame directly (includes header)
|
||||||
// unsigned char* pl = (unsigned char*)curr->payload;
|
memcpy((void*)((uintptr_t)`pkt`.data + `offset`), curr->payload, curr->len);
|
||||||
// glue_print(" Payload Byte: ");
|
|
||||||
// glue_print_hex((uint64_t)pl[0]);
|
|
||||||
|
|
||||||
memcpy((void*)((uintptr_t)`pkt`.data + `offset` + 12), curr->payload, curr->len);
|
|
||||||
`offset` += curr->len;
|
`offset` += curr->len;
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
pkt.len = uint16(offset) + 12
|
# Zero out VirtIO-net header (first 12 bytes - Modern with MRG_RXBUF)
|
||||||
|
{.emit: """
|
||||||
|
memset((void*)`pkt`.data, 0, 12);
|
||||||
|
""".}
|
||||||
|
|
||||||
|
pkt.len = uint16(offset) # Total: 12 (VirtIO) + Ethernet frame
|
||||||
|
|
||||||
if not ion_net_tx(pkt):
|
if not ion_net_tx(pkt):
|
||||||
ion_user_free(pkt)
|
ion_user_free(pkt)
|
||||||
|
|
@ -174,13 +178,19 @@ proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
||||||
|
|
||||||
# --- Membrane Globals ---
|
# --- Membrane Globals ---
|
||||||
var g_netif: pointer
|
var g_netif: pointer
|
||||||
var last_tcp_tmr, last_arp_tmr, last_dhcp_fine, last_dhcp_coarse: uint32
|
var last_tcp_tmr, last_arp_tmr, last_dhcp_fine, last_dhcp_coarse, last_dns_tmr: uint32
|
||||||
|
|
||||||
var membrane_started = false
|
var membrane_started = false
|
||||||
|
|
||||||
proc membrane_init*() {.exportc, cdecl.} =
|
proc membrane_init*() {.exportc, cdecl.} =
|
||||||
if membrane_started: return
|
if membrane_started: return
|
||||||
membrane_started = true
|
membrane_started = true
|
||||||
|
let now = sys_now()
|
||||||
|
last_tcp_tmr = now
|
||||||
|
last_arp_tmr = now
|
||||||
|
last_dhcp_fine = now
|
||||||
|
last_dhcp_coarse = now
|
||||||
|
last_dns_tmr = now
|
||||||
|
|
||||||
glue_print("[Membrane] Initialization...\n")
|
glue_print("[Membrane] Initialization...\n")
|
||||||
ion_user_init()
|
ion_user_init()
|
||||||
|
|
@ -188,9 +198,16 @@ proc membrane_init*() {.exportc, cdecl.} =
|
||||||
# 1. LwIP Stack Init
|
# 1. LwIP Stack Init
|
||||||
glue_print("[Membrane] Calling lwip_init()...\n")
|
glue_print("[Membrane] Calling lwip_init()...\n")
|
||||||
lwip_init()
|
lwip_init()
|
||||||
glue_print("[Membrane] lwip_init() returned.\n")
|
dns_init() # Initialize DNS resolver
|
||||||
{.emit: "printf(\"[Membrane] LwIP Byte Order: %d (LE=%d, BE=%d)\\n\", BYTE_ORDER, LITTLE_ENDIAN, BIG_ENDIAN);".}
|
|
||||||
{.emit: "lwip_platform_diag(\"[Membrane] DIAG TEST: %s\\n\", \"OK\");".}
|
# Set Fallback DNS (8.8.8.8)
|
||||||
|
{.emit: """
|
||||||
|
static ip_addr_t dns_server;
|
||||||
|
IP4_ADDR(ip_2_ip4(&dns_server), 8, 8, 8, 8);
|
||||||
|
dns_setserver(0, &dns_server);
|
||||||
|
""".}
|
||||||
|
|
||||||
|
glue_print("[Membrane] lwip_init() returned. DNS Initialized.\n")
|
||||||
|
|
||||||
# 2. Setup Netif
|
# 2. Setup Netif
|
||||||
{.emit: """
|
{.emit: """
|
||||||
|
|
@ -213,8 +230,11 @@ proc membrane_init*() {.exportc, cdecl.} =
|
||||||
|
|
||||||
glue_print("[Membrane] Network Stack Operational (Waiting for DHCP IP...)\n")
|
glue_print("[Membrane] Network Stack Operational (Waiting for DHCP IP...)\n")
|
||||||
|
|
||||||
|
proc glue_get_ip*(): uint32 {.exportc, cdecl.} =
|
||||||
|
## Returns current IP address in host byte order
|
||||||
|
{.emit: "return ip4_addr_get_u32(netif_ip4_addr((struct netif *)`g_netif`));".}
|
||||||
|
|
||||||
var last_notified_ip: uint32 = 0
|
var last_notified_ip: uint32 = 0
|
||||||
var dhcp_retried = false
|
|
||||||
var last_ping_time: uint32 = 0
|
var last_ping_time: uint32 = 0
|
||||||
|
|
||||||
proc glue_print_hex(v: uint64) =
|
proc glue_print_hex(v: uint64) =
|
||||||
|
|
@ -230,7 +250,6 @@ proc glue_print_hex(v: uint64) =
|
||||||
|
|
||||||
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
||||||
# glue_print("[Membrane] Pump\n")
|
|
||||||
|
|
||||||
let now = sys_now()
|
let now = sys_now()
|
||||||
|
|
||||||
|
|
@ -243,14 +262,15 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
glue_print("\n")
|
glue_print("\n")
|
||||||
last_notified_ip = ip_addr
|
last_notified_ip = ip_addr
|
||||||
|
|
||||||
# Force DHCP Retry if no IP after 3 seconds
|
|
||||||
if now > 3000 and not dhcp_retried and ip_addr == 0:
|
|
||||||
dhcp_retried = true
|
|
||||||
glue_print("[Membrane] Forcing DHCP Restart...\n")
|
|
||||||
{.emit: "dhcp_start((struct netif *)`g_netif`);".}
|
|
||||||
|
|
||||||
|
|
||||||
# 1. LwIP Timers (Raw API needs manual polling)
|
# 1. LwIP Timers (Raw API needs manual polling)
|
||||||
|
{.emit: """
|
||||||
|
static int debug_tick = 0;
|
||||||
|
if (debug_tick++ % 200 == 0) {
|
||||||
|
printf("[Membrane] sys_now: %u\n", `now`);
|
||||||
|
}
|
||||||
|
""".}
|
||||||
|
|
||||||
if now - last_tcp_tmr >= 250:
|
if now - last_tcp_tmr >= 250:
|
||||||
tcp_tmr()
|
tcp_tmr()
|
||||||
last_tcp_tmr = now
|
last_tcp_tmr = now
|
||||||
|
|
@ -258,6 +278,20 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
etharp_tmr()
|
etharp_tmr()
|
||||||
last_arp_tmr = now
|
last_arp_tmr = now
|
||||||
|
|
||||||
|
# DHCP Timers
|
||||||
|
if now - last_dhcp_fine >= 500:
|
||||||
|
dhcp_fine_tmr()
|
||||||
|
last_dhcp_fine = now
|
||||||
|
if now - last_dhcp_coarse >= 60000:
|
||||||
|
glue_print("[Membrane] DHCP Coarse Timer\n")
|
||||||
|
dhcp_coarse_tmr()
|
||||||
|
last_dhcp_coarse = now
|
||||||
|
|
||||||
|
# DNS Timer (every 1000ms)
|
||||||
|
if now - last_dns_tmr >= 1000:
|
||||||
|
dns_tmr()
|
||||||
|
last_dns_tmr = now
|
||||||
|
|
||||||
# Phase 37a: ICMP Ping Verification
|
# Phase 37a: ICMP Ping Verification
|
||||||
if now - last_ping_time > 1000:
|
if now - last_ping_time > 1000:
|
||||||
last_ping_time = now
|
last_ping_time = now
|
||||||
|
|
@ -270,17 +304,6 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
ping_send(&gateway);
|
ping_send(&gateway);
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
|
|
||||||
# DHCP Timers
|
|
||||||
if now - last_dhcp_fine >= 500:
|
|
||||||
# glue_print("[Membrane] DHCP Fine Timer\n")
|
|
||||||
dhcp_fine_tmr()
|
|
||||||
last_dhcp_fine = now
|
|
||||||
if now - last_dhcp_coarse >= 60000:
|
|
||||||
glue_print("[Membrane] DHCP Coarse Timer\n")
|
|
||||||
dhcp_coarse_tmr()
|
|
||||||
last_dhcp_coarse = now
|
|
||||||
|
|
||||||
# 2. RX Ingress
|
# 2. RX Ingress
|
||||||
var pkt: IonPacket
|
var pkt: IonPacket
|
||||||
# glue_print("[Membrane] Exit Pump\n")
|
# glue_print("[Membrane] Exit Pump\n")
|
||||||
|
|
@ -303,13 +326,13 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
printf("[Membrane] ERROR: Ingress pkt.data is NULL!\n");
|
printf("[Membrane] ERROR: Ingress pkt.data is NULL!\n");
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
} else {
|
} else {
|
||||||
// OFFSET FIX: Kernel already applied VirtIO offset (12 bytes) to pkt.data
|
|
||||||
pbuf_take(p, (void*)((uintptr_t)`pkt`.data), `pkt`.len);
|
pbuf_take(p, (void*)((uintptr_t)`pkt`.data), `pkt`.len);
|
||||||
|
|
||||||
if (netif_default->input(p, netif_default) != ERR_OK) {
|
if (netif_default->input(p, netif_default) != ERR_OK) {
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
printf("[Membrane] CRITICAL: pbuf_alloc FAILED! (POOL OOM?)\n");
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
ion_user_free(pkt)
|
ion_user_free(pkt)
|
||||||
|
|
@ -511,3 +534,59 @@ proc glue_close*(sock: ptr NexusSock): int {.exportc, cdecl.} =
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
# --- DNS GLUE (C Implementation) ---
|
||||||
|
{.emit: """
|
||||||
|
static ip_addr_t g_dns_ip;
|
||||||
|
static int g_dns_status = 0; // 0=idle, 1=pending, 2=done, -1=error
|
||||||
|
|
||||||
|
static void my_dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
|
||||||
|
if (ipaddr != NULL) {
|
||||||
|
g_dns_ip = *ipaddr;
|
||||||
|
g_dns_status = 2; // Success
|
||||||
|
} else {
|
||||||
|
g_dns_status = -1; // Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int glue_resolve_start(char* hostname) {
|
||||||
|
ip_addr_t ip;
|
||||||
|
err_t err;
|
||||||
|
g_dns_status = 1; // Pending default
|
||||||
|
|
||||||
|
// Ensure we have a DNS server
|
||||||
|
const ip_addr_t *ns = dns_getserver(0);
|
||||||
|
if (ns == NULL || ip_addr_isany(ns)) {
|
||||||
|
printf("[Membrane] No DNS server available. Using fallback 8.8.8.8\n");
|
||||||
|
static ip_addr_t fallback;
|
||||||
|
IP_ADDR4(&fallback, 8, 8, 8, 8);
|
||||||
|
dns_setserver(0, &fallback);
|
||||||
|
ns = dns_getserver(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[Membrane] Resolving '%s' via DNS: %s\n", hostname, ipaddr_ntoa(ns));
|
||||||
|
|
||||||
|
err = dns_gethostbyname(hostname, &ip, my_dns_callback, NULL);
|
||||||
|
if (err == ERR_OK) {
|
||||||
|
g_dns_ip = ip;
|
||||||
|
g_dns_status = 2; // Done
|
||||||
|
return 0;
|
||||||
|
} else if (err == ERR_INPROGRESS) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
printf("[Membrane] dns_gethostbyname FAILED with error: %d\n", (int)err);
|
||||||
|
g_dns_status = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int glue_resolve_check(u32_t *ip_out) {
|
||||||
|
if (g_dns_status == 1) return 1;
|
||||||
|
if (g_dns_status == 2) {
|
||||||
|
*ip_out = ip4_addr_get_u32(&g_dns_ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
""".}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue