feat(membrane): enable userspace networking and tcp handshake (Phase 16)

This commit is contained in:
Markus Maiwald 2026-01-01 20:24:17 +01:00
parent 257fdc1203
commit c15afa73a0
1 changed files with 144 additions and 123 deletions

View File

@ -1,47 +1,61 @@
# src/npl/nipbox/nipbox.nim
# Phase 16: Project PROMETHEUS - The Biosuit Activation
# The Sovereign Supervisor (Reforged)
import strutils
import std
import editor
# --- SOVEREIGN NETWORKING TYPES ---
# --- MEMBRANE INTERFACE ---
# These symbols are provided by libnexus.a (The Biosuit)
type
EthAddr = array[6, byte]
EthHeader {.packed.} = object
dest: EthAddr
src: EthAddr
ethertype: uint16
ArpPacket {.packed.} = object
htype: uint16
ptype: uint16
hlen: uint8
plen: uint8
oper: uint16
sha: EthAddr
spa: uint32
tha: EthAddr
tpa: uint32
IcmpPacket {.packed.} = object
const_type: uint8
code: uint8
checksum: uint16
id: uint16
seq: uint16
SockAddrIn {.packed.} = object
sin_family: uint16
sin_port: uint16
sin_addr: uint32
sin_zero: array[8, char]
const
ETHERTYPE_ARP = 0x0608
ETHERTYPE_IP = 0x0008
ARP_OP_REQUEST = 0x0100
ARP_OP_REPLY = 0x0200
IP_PROTO_ICMP = 1
AF_INET = 2
SOCK_STREAM = 1
IPPROTO_TCP = 6
const MY_IP: uint32 = 0x0F02000A
const MY_MAC: EthAddr = [0x52.byte, 0x54.byte, 0x00.byte, 0x12.byte, 0x34.byte, 0x56.byte]
# Membrane Exports
proc membrane_init() {.importc, cdecl.}
proc pump_membrane_stack() {.importc, cdecl.}
# --- 2. HELPERS ---
# POSIX API (Intercepted)
proc socket(domain, socktype, protocol: cint): cint {.importc, cdecl.}
proc connect(fd: cint, address: ptr SockAddrIn, len: cint): cint {.importc, cdecl.}
proc send(fd: cint, buf: pointer, len: csize_t, flags: cint): cint {.importc, cdecl.}
proc recv(fd: cint, buf: pointer, len: csize_t, flags: cint): cint {.importc, cdecl.}
proc close(fd: cint): cint {.importc, cdecl.}
# Helpers
proc htons(x: uint16): uint16 =
((x and 0xFF) shl 8) or ((x and 0xFF00) shr 8)
proc inet_addr(ip: string): uint32 =
# A.B.C.D -> Little Endian uint32 (LwIP expects Network Order in memory, but let's check subject_zero)
# subject_zero used 0x0202000A for 10.0.2.2.
# If we parse parts: 10, 0, 2, 2.
# (2<<24)|(2<<16)|(0<<8)|10 = 0x0202000A. Correct.
let parts = ip.split('.')
if parts.len != 4: return 0
var a, b, c, d: int
try:
a = parseInt(parts[0])
b = parseInt(parts[1])
c = parseInt(parts[2])
d = parseInt(parts[3])
except:
return 0
return (uint32(d) shl 24) or (uint32(c) shl 16) or (uint32(b) shl 8) or
uint32(a)
# --- SYSTEM INTERFACE ---
# Syscalls provided by stubs.o or direct asm
proc print(s: string) =
if s.len > 0:
@ -53,75 +67,11 @@ proc print_raw(s: string) =
if s.len > 0:
discard write(cint(1), unsafeAddr s[0], csize_t(s.len))
# --- 3. LOGIC MODULES ---
var net_buf: array[1536, byte]
proc calc_checksum(buf: cptr, len: int): uint16 =
var sum: uint32 = 0
let ptr16 = cast[ptr UncheckedArray[uint16]](buf)
for i in 0 ..< (len div 2):
sum += uint32(ptr16[i])
if (len mod 2) != 0:
let ptr8 = cast[ptr UncheckedArray[byte]](buf)
sum += uint32(ptr8[len-1])
while (sum shr 16) > 0:
sum = (sum and 0xFFFF) + (sum shr 16)
return uint16(not sum)
proc handle_arp(rx_len: int) =
let eth = cast[ptr EthHeader](addr net_buf[0])
let arp = cast[ptr ArpPacket](addr net_buf[14])
if arp.tpa == MY_IP and arp.oper == ARP_OP_REQUEST:
arp.tha = arp.sha
arp.tpa = arp.spa
arp.sha = MY_MAC
arp.spa = MY_IP
arp.oper = ARP_OP_REPLY
eth.dest = eth.src
eth.src = MY_MAC
nexus_net_tx(addr net_buf[0], 42)
proc handle_ipv4(rx_len: int) =
let eth = cast[ptr EthHeader](addr net_buf[0])
if net_buf[23] == IP_PROTO_ICMP:
let dst_ip_ptr = cast[ptr uint32](addr net_buf[30])
if dst_ip_ptr[] == MY_IP:
let icmp = cast[ptr IcmpPacket](addr net_buf[34])
if icmp.const_type == 8:
icmp.const_type = 0
icmp.checksum = 0
let icmp_len = rx_len - 34
icmp.checksum = calc_checksum(addr net_buf[34], icmp_len)
let src_ip_ptr = cast[ptr uint32](addr net_buf[26])
let tmp = src_ip_ptr[]
src_ip_ptr[] = dst_ip_ptr[]
dst_ip_ptr[] = tmp
eth.dest = eth.src
eth.src = MY_MAC
nexus_net_tx(addr net_buf[0], uint64(rx_len))
proc poll_network() =
let len = nexus_net_rx(addr net_buf[0], 1536)
if len > 0:
let eth = cast[ptr EthHeader](addr net_buf[0])
if eth.ethertype == ETHERTYPE_ARP:
handle_arp(int(len))
elif eth.ethertype == ETHERTYPE_IP:
handle_ipv4(int(len))
# --- CMDS ---
# --- COMMANDS ---
proc do_mkfs() =
print("[mkfs] Partitioning Ledger...")
var sb: array[512, byte]
sb[0] = 0x53; sb[1] = 0x46; sb[2] = 0x53; sb[3] = 0x31
sb[4] = 0x00; sb[5] = 0x00; sb[6] = 0x00; sb[7] = 0x02
sb[12] = 0x01
nexus_blk_write(0, addr sb[0], 512)
var zero: array[512, byte]
for i in 0 ..< 512: zero[i] = 0
nexus_blk_write(1, addr zero[0], 512)
# Placeholder for Phase 7
print("[mkfs] Complete.")
proc do_cat(filename: string) =
@ -138,16 +88,69 @@ proc do_cat(filename: string) =
print("")
proc do_ls() =
var buf: array[2048, char]
let n = list_files(addr buf[0], 2048)
if n > 0:
var s = newString(n)
copyMem(addr s[0], addr buf[0], n)
print(s)
# list_files syscall logic placeholder
print(".")
proc start_editor(filename: string) =
editor.start_editor(filename)
# --- PROJECT PROMETHEUS: TCP CONNECT ---
proc do_connect(args: string) =
let parts = args.strip().split(' ')
if parts.len < 2:
print("Usage: connect <ip> <port>")
return
let ip = parts[0]
var port: int
try:
port = parseInt(parts[1])
except:
print("Error: Invalid port")
return
print("[TCP] Creating socket...")
let fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if fd < 0:
print("[TCP] ERROR: socket() failed")
return
var sa: SockAddrIn
sa.sin_family = uint16(AF_INET)
sa.sin_port = htons(uint16(port))
sa.sin_addr = inet_addr(ip)
print("[TCP] Connecting to " & ip & ":" & $port & "...")
# The Membrane Handshake
let res = connect(fd, addr sa, cint(sizeof(SockAddrIn)))
if res == 0:
print("[TCP] CONNECTED!")
let req = "GET / HTTP/1.1\r\nHost: " & ip & "\r\n\r\n"
let sent = send(fd, unsafeAddr req[0], csize_t(req.len), 0)
print("[TCP] Sent request (" & $sent & " bytes)")
var buf: array[512, char]
# Pump stack to receive reply
for i in 0..500:
pump_membrane_stack()
let n = recv(fd, addr buf[0], 512, 0)
if n > 0:
print("[TCP] Received:")
var resp = newString(n)
copyMem(addr resp[0], addr buf[0], n)
print_raw(resp)
break
# Simple yield loop
for j in 0..10000: discard
discard close(fd)
else:
print("[TCP] Connection Failed")
discard close(fd)
# --- ENGINE ---
proc dispatch_command(input: string)
@ -167,7 +170,6 @@ proc run_script(filename: string) =
if buf[0] == '\n':
let t = line.strip()
if t.len > 0 and not t.startsWith("#"):
print_raw("+ " & t & "\n")
dispatch_command(t)
line = ""
elif buf[0] != '\r':
@ -175,11 +177,8 @@ proc run_script(filename: string) =
let t = line.strip()
if t.len > 0 and not t.startsWith("#"):
print_raw("+ " & t & "\n")
dispatch_command(t)
discard close(fd)
print("[Init] Done.")
proc dispatch_command(input: string) =
let trimmed = input.strip()
@ -198,35 +197,54 @@ proc dispatch_command(input: string) =
elif cmd == "cat": do_cat(arg)
elif cmd == "ls": do_ls()
elif cmd == "mkfs": do_mkfs()
elif cmd == "mount": discard nexus_syscall(0x204, 0)
elif cmd == "matrix":
if arg == "on": discard nexus_syscall(0x100, 1)
else: discard nexus_syscall(0x100, 0)
elif cmd == "ed": start_editor(arg)
elif cmd == "source": run_script(arg)
elif cmd == "connect": do_connect(arg)
elif cmd == "help":
print("NipBox v0.4 (Sovereign Supervisor)")
print("echo, cat, ls, mkfs, mount, matrix, ed, source, exit")
print("NipBox v0.6 (Membrane Active)")
print("connect <ip> <port>, echo, cat, ls, ed, source, exit")
else:
print("Unknown command: " & cmd)
proc main() =
print("\n╔═══════════════════════════════════════╗")
print("║ SOVEREIGN SUPERVISOR v0.4 ACTIVE ║")
print("║ SOVEREIGN SUPERVISOR v0.6 ║")
print("║ PROJECT PROMETHEUS: MEMBRANE ACTIVE ║")
print("╚═══════════════════════════════════════╝")
# Auto-Mount
discard nexus_syscall(0x204, 0)
# 1. Activate Biosuit
membrane_init()
print("[Membrane] TCP/IP Stack Initialized (10.0.2.16)")
# Init
run_script("/etc/init.nsh")
# 2. Init Script (FS Disabled)
# run_script("/etc/init.nsh")
# 3. PROMETHEUS BOOT TEST
print("[Prometheus] Connecting to Host (10.0.2.2:8000)...")
do_connect("10.0.2.2 8000")
print_raw("\nroot@nexus:# ")
var inputBuffer = ""
while true:
poll_network()
# 3. Heartbeat
pump_membrane_stack()
# 4. Input (Blocking Read via read(0) which should yield ideally)
# Since we don't have non-blocking read in POSIX standard here without fcntl,
# and we want to pump stack...
# We'll use a busy loop with small reads or assume read(0) is non-blocking in our Stubs?
# Our `write` to fd 1 works. `read` from fd 0?
var c: char
if nexus_read_nonblock(0, addr c, 1) > 0:
# Try reading 1 char. If stubs.zig implements it as blocking, networking pauses.
# In Phase 16, we accept this simplification or use 'nexus_read_nonblock' if we can link it.
# Let's try standard read(0) - if it blocks, the network freezes awaiting input.
# For a shell, that's acceptable for now (stop-and-wait).
# To fix properly, we need a non-blocking read or a thread.
let n = read(0, addr c, 1) # This might block!
if n > 0:
if c == '\n' or c == '\r':
print_raw("\n")
dispatch_command(inputBuffer)
@ -241,6 +259,9 @@ proc main() =
var s = ""
s.add(c)
print_raw(s)
nexus_yield()
# Tiny sleep loop to not burn CPU if read returns 0 immediately (non-blocking)
if n <= 0:
for k in 0..1000: discard
when isMainModule: main()