Repository navigation
net: a stream's local address is netstack's, not QEMU's 10.0.2.15 - #1147
Conversation
Review r1 of #1147 at 9070b61ArchitectureThe ownership is right. [tcp]'s tuple owns a connection's local address. netstack puts it in its own wire types ( What I checked and found sound:
FindingsBLOCKER 1 —
BLOCKER 2 — BLOCKER 3 — SUGGESTION 1 —
SUGGESTION 2 — SUGGESTION 3 — No test fails if the SUGGESTION 4 (PR description, orchestrator's) — "Checks (head 9070b61)" covers controls and a failover run made at 9f4d00502 plus uncommitted edits. State that revision and why the later changes don't invalidate it, as the handoff does for host CI. SEND BACK |
…tstack's too A connected datagram socket answered its bind's address, 0.0.0.0 for a wildcard, where Linux answers the address its connect's route sends from. [udp] already fixes that address at connect (Binding::Connected's local). The shard's udp_peer now gives it as UdpPeer, and netstack's PEER answer (socket::Peer) carries it beside the peer. The SDK's socket::local asks PEER for a datagram socket, so every holder sees the address whichever holder connected; an answer naming no host address is refused. std's UdpSocket::local_addr and libc's getsockname return it. The PEER answer carries it rather than CONNECT's: a socket's connection is every holder's, and only the connecting holder reads a CONNECT answer. - The stream end's check and the datagram one share net::unicast, which says what its doc says through Ipv4Addr's own predicates. - stream::Endpoint names an address and port throughout the stream end. - stream_handles answers a connect as from 192.0.2.1 and asserts std's local_addr reads it, and answers one with the unspecified address and asserts the connect is refused, so the check at open is held. - link_failover also connects a datagram socket over each link and asserts its local address is that link's lease. The stream's port-0 assert is gone: the SDK refuses port 0 before an end exists. - The ramp issue's account of std is in the past tense, and a node test comment that claimed what nothing checks is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std's TcpStream::local_addr and libc's getsockname answered QEMU's user network address with the stream's port, whatever the machine leased. Since #1122 osbench names a connection's link by that address, so on the T14 osbench_lan refused its group: "no netstack link holds 10.0.2.15". netstack's connect and accept answers and its ADOPT now carry the address the stream left from with its port: the node's StreamEvent::Connected, Accepted and export give the connection's local Endpoint, the tuple [tcp] holds. The SDK takes it only as a stream's end can be, a unicast address and a port; a connect or accept answer naming another is refused as a protocol error, and an ADOPT naming one as malformed. stream::endpoints returns it, std's local_addr and libc's getsockname answer it, and getsockname answers a listener's or datagram socket's bound address rather than the literal. Connected and Accepted lose their unread local_port. The guest jobs that play netstack (stream_handles, libc_recv_beside_close) name a TEST-NET-1 local address in their answers. The node's host tests assert the lease's address on connect, accept and export. link_failover opens a stream to the host on each of its two QEMU user networks and asserts each one's local address is its link's lease. The two leases differ, so one is not 10.0.2.15 and the value is netstack's: with std's literal restored the job fails, whichever order the links came up in. issues/getsockname-answers-an-address-nobody-asked-netstack-for.md is closed, and its references go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tstack's too A connected datagram socket answered its bind's address, 0.0.0.0 for a wildcard, where Linux answers the address its connect's route sends from. [udp] already fixes that address at connect (Binding::Connected's local). The shard's udp_peer now gives it as UdpPeer, and netstack's PEER answer (socket::Peer) carries it beside the peer. The SDK's socket::local asks PEER for a datagram socket, so every holder sees the address whichever holder connected; an answer naming no host address is refused. std's UdpSocket::local_addr and libc's getsockname return it. The PEER answer carries it rather than CONNECT's: a socket's connection is every holder's, and only the connecting holder reads a CONNECT answer. - The stream end's check and the datagram one share net::unicast, which says what its doc says through Ipv4Addr's own predicates. - stream::Endpoint names an address and port throughout the stream end. - stream_handles answers a connect as from 192.0.2.1 and asserts std's local_addr reads it, and answers one with the unspecified address and asserts the connect is refused, so the check at open is held. - link_failover also connects a datagram socket over each link and asserts its local address is that link's lease. The stream's port-0 assert is gone: the SDK refuses port 0 before an end exists. - The ramp issue's account of std is in the past tense, and a node test comment that claimed what nothing checks is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… closes The session the issue's exit names ran on 9070b61 (this PR's local address fix, which let osbench_lan measure on the T14's lease): Linux, ToyOS, Linux, all rc 0. judge.py applied the rule set before the run: - p50: ToyOS 114 MB/s, Linux 113 and 113. - Nothing resent: no RTO, recovery or loss probe. - 100 ms: ToyOS's five rounds 6.31-9.93 MB (mean 7.11) against Linux's ten 6.24-6.68 MB; the one-sided rank test that ToyOS ranks low gives p = 0.22. - 300 ms: 29.3-33.2 MB against 28.3-30.0 MB; p = 0.78. - Linux's two runs agree: p = 0.55 and 1.00, so the rounds compare. Verdict: parity. ToyOS's first round read 9.93 MB by 100 ms, far above every other round on either system; its cause is not known. Without it the 100 ms test gives p = 0.053, still a pass, so the verdict does not rest on it. Evidence: https://github.com/ToyOSOrg/evidence/tree/1c9f4c83c362a1b30746f1e096ad412a2d5f25f9/r2/session Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ade it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes
issues/getsockname-answers-an-address-nobody-asked-netstack-for.mdandissues/tcp-opens-a-connections-window-slower-than-linux-in-its-first-300-ms.md.Problem
std's
TcpStream::local_addrand libc'sgetsocknamereturned QEMU's user-network address, 10.0.2.15, with the socket's port, whatever the machine leased. A connected datagram socket returned its bind's 0.0.0.0. Since #1122, osbench names a connection's link by its local address, so on the T14osbench_lanrefused its whole group (no netstack link holds 10.0.2.15; recorded in #1143), and the ramp issue's parity session could not measure.Change
Streams
TcpConnectResponse,TcpAcceptResponseand the streamAdopt.StreamEvent::Connected,Acceptedandexportgive a localEndpoint.net::unicast: not unspecified, multicast or broadcast, byIpv4Addr's own predicates).NetError::Protocol.ADOPTnaming another is treated as malformed.stream::endpointsreturns(peer, local)asstream::Endpoints.ConnectedandAccepteddrop their unreadlocal_port.Connected datagram sockets
udp_peernow returns it in aUdpPeer, and netstack'sPEERanswer (socket::Peer) carries it.socket::localasksPEERfor a datagram socket. So every holder sees the address the route sends from, as on Linux, whichever holder connected. An answer naming no host address is refused.PEERand notCONNECT: a socket's connection is shared by all its holders, but only the connecting holder reads aCONNECTanswer.std and libc
TcpStream::local_addrandUdpSocket::local_addr, and libc'sgetsockname, return these addresses.Elsewhere
sdk/. The copies left are inrust/(upstream tooling) and in tests and log samples that describe QEMU's network.Tests
Node host tests: connect, accept and export assert the lease's address. A connected datagram socket's
udp_peerasserts it too.SDK host tests:
a_streams_own_end_is_a_unicast_address_and_a_port;a_peers_answer_ends_in_the_address_it_is_sent_from;stream_handles(guest, the test plays netstack):local_addr;This covers the check at the call site, not just the predicate.
link_failover(guest, QEMU x86-64, two user networks): a stream and a connected datagram socket to the host on each network each report that link's lease as their local address.traffic_leaves_by_the_second_link_while_the_first_is_down, which already boots two NICs, so it costs no extra boot.Checks
cargo run -- --ci hostat a7c1b31: 109 steps green, exit 0. The first attempt died of a full host disk and was rerun, not judged.stream_holders,traffic_leaves_by_the_second_link_while_the_first_is_down(local addresses 10.0.2.15 and 10.0.3.15 for each kind),netstack_holders_exported_to_children,libc_children. The earlier hang of this combination was the sysroot-lock defect buildlock: one polled wait with a ceiling, and a would-be maker tries #1145 fixed.Round 0 (at 9f4d00502 plus the edits that became 9070b61):
The test rename, the registration comment and the harness's extra accept that followed don't affect them.
Review round 1 (at 7a601f4):
socket::localignoring the connect: the failover test fails ("sends from 0.0.0.0");opendiscarding its local-end check:stream_holdersfails ("connected from 0.0.0.0").The T14 parity session (closes the ramp issue)
judge.pyunder the rule set before the run.Evidence: r1, r2.
Risks
ADDRESS_GONE, netstack runs every link a driver brings: ranked, with failover and return (P5) #1122). A connected datagram socket's address is fixed at its connect, so after a lease change it reports the address it was connected from until it connects again.local_addron a datagram socket is now an IPC round trip to netstack, aspeer_addralready is.🤖 Generated with Claude Code