diff --git a/docs/roadmap.md b/docs/roadmap.md index c04bce5..252452e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -118,7 +118,7 @@ TCP/IP, so they are the right thing to build the socket layer's shape around. - [ ] `SCM_RIGHTS` for passing descriptors. Possibly unnecessary: an shm id is an integer and travels fine over a stream socket, so this waits until something actually needs to pass an open file -- [x] e1000 driver — `kernel/drivers/net/e1000.c`, from Intel's public manual +- [x] e1000 driver — `kernel/drivers/net/e1000/e1000.c`, from Intel's public manual 317453. MAC from EEPROM, transmit and receive rings, link detection. Run with `-netdev user,id=n0 -device e1000,netdev=n0`. **Verified to carry real traffic**: `echo n > /proc/sysrq` sends a @@ -133,28 +133,48 @@ TCP/IP, so they are the right thing to build the socket layer's shape around. netif). NO_SYS=1, so lwIP has no threads of its own and one kernel thread (`kernel/net/net.c`, started by `netinit`) polls the NIC and runs its timers. DHCP obtains an address at boot. - **Verified end to end**: `nettest 10.0.2.2 9000` opens a TCP connection - out of the guest, and the listener on the host receives - `HELLO-FROM-SCHOS` while the guest receives the host's reply -- so the + DHCP obtains an address at boot and `/proc/netif` reports it, along with + the MAC, the gateway and frame counts taken at the lwIP seam. + **Verified end to end**, by `bootcheck -Net` rather than by hand: the + guest opens a TCP connection out, the listener on the host receives + `HELLO-FROM-SCHOS`, and the guest receives the host's reply -- so the handshake, both data directions and the close all work, not just the driver. `kthread_create` was added for this and is generally useful. -- [ ] Was: not written from scratch -- decided 2026-08-08. Writing TCP is the one part of this where doing it yourself is - a bad trade: it is not the packet format that is hard, it is congestion - control, retransmission, window management and twenty years of accumulated - corner cases that only appear against a real peer. - **Preferred base: lwIP** (BSD-3, no advertising clause, verified). It was - written for exactly this situation -- a small OS with a raw Ethernet - driver -- and its port layer is a handful of files. - **If BSD instead: FreeBSD, not NetBSD.** NetBSD's `tcp_input.c` carries - the WIDE Project's 4-clause advertising clause; FreeBSD's has none. But - FreeBSD's `sys/netinet` is not a library -- it reaches into mbufs, - epoch(9), netisr, VNET and uma(9), so taking it means porting FreeBSD's - kernel infrastructure first. See docs/licensing.md §3. + The reasoning behind not writing it, kept because it is the reason to + resist doing so later as well: it is not the packet format that is hard, + it is congestion control, retransmission, window management and twenty + years of accumulated corner cases that only appear against a real peer. + lwIP was preferred over a BSD stack on structure rather than licence -- + FreeBSD's `sys/netinet` is BSD-3 too, but it is not a library: it reaches + into mbufs, epoch(9), netisr, VNET and uma(9), so taking it means porting + FreeBSD's kernel infrastructure first. NetBSD was ruled out on licence: + its `tcp_input.c` carries the WIDE Project's 4-clause advertising clause. + See docs/licensing.md §3. - [x] `AF_INET` on top of the same socket layer -- done, exactly as planned: `socknew` dispatches on the domain, `kernel/net/inet.c` implements the AF_INET side on lwIP's raw API, and no syscall changed shape. TCP and UDP - both work; `recvfrom`'s source address and a proper sleep/wakeup instead - of the current tick-poll are the two things still owed. + both work. +- [x] The two things AF_INET owed on the day it landed, both closed 2026-08-15. + `recvfrom` reports the source address -- which meant giving UDP a + datagram queue first, since the payloads shared TCP's byte ring and two + datagrams arriving together came back as one -- and `sendto` takes a + destination. Blocking sockets sleep on a counter bumped by the lwIP + callbacks instead of waking on every timer tick; `tcp_sent` had to be + registered for it, or a write blocked on a full send buffer would sleep + with nobody left to wake it. +- [x] `bootcheck -Net`, so none of the above is verified by hand any more. A + listener and a UDP responder run on the host, the guest is told to talk + to them, and both ends are asserted -- one side alone cannot tell a + working stack from one that completes a handshake and drops every byte. + 45 [ok] with it on. +- [ ] `accept()` does not report the peer's address, and there is no + `getsockname`/`getpeername`. Nothing has needed them yet; a server that + wants to log who connected will. +- [ ] Receive is still polled. The network thread looks at the NIC every timer + tick when the link is idle, so an arriving frame waits up to 10 ms before + anything sees it. Waking that thread from the e1000 interrupt is the real + fix and was deliberately not the first version: a receive path woken by an + interrupt is much harder to debug than one that is asked. ### Phase 3 — GPU