tcp: extract the multi-connection multiplexer as a pure Stack
The connection table, listener set, four-tuple routing, server-accept on
a SYN to a listened port, and ephemeral-port allocation were all in the
Eio adapter (net.ml), reading the clock ambiently -- the multiplexer, the
one part of the stack with no pure home. Only one RFC 9293 connection was
modelled purely (Connection); the layer above it lived at the I/O
boundary, untestable without a socket.
Add Tcp.Stack: the pure multiplexer over Connection. incoming/connect/
send/close/timer take ~now and return a new stack, the segments to frame
(grouped per connection), and events. It holds the table as a Map, the
listeners as a Set, no mutable state and no I/O, exactly like Connection.
net.ml is now the thin adapter: it threads a Stack.t ref on one owner
fiber, keeps the per-connection Eio resources (receive stream, backpressure
condition, establishment promise) in a side table, and emits the transition
and event probes at the boundary (where the pure core must not). Behaviour
is unchanged: the 12 adapter tests, the netstack crams and the bench all
pass. A new pure test drives a full handshake and data transfer between two
Stacks with no I/O.
Also realign the opam manifests with the post-split library graph
(nox-ip-eio/nox-udp-eio/nox-ethernet-eio are real deps; nox-icmp/nox-arp/
nox-ethernet are test-only) and drop the now-dead nox-icmp from the core.