Changes related to holepunching - #8
Open
cowlicks wants to merge 2 commits into
Open
Conversation
Datagrams could only be sent at the socket's default TTL, so a caller had no way to send one with a deliberately short hop budget. Holepunching needs that. A puncher's first rounds go out at a low TTL (hyperdht uses 5) so they cross the local NAT, creating the mapping that is the whole point of punching, but expire in transit before reaching the peer. Delivering them would be useless (the far NAT has no mapping yet, so they are dropped) and harmful: a burst of unsolicited inbound packets looks like a port scan and gets the source rate-limited or blocked right before the real connection attempt. Transmit gains a `ttl`, emitted as an IP_TTL (v4) or IPV6_HOPLIMIT (v6) control message in prepare_msg, which serves both the sendmmsg and macOS sendmsg paths. CMSG_LEN goes 88 -> 112: 88 was exactly the sum of the three existing control messages with nothing spare, so without the bump the TTL is silently dropped. The init assertion now demands room for two c_int messages instead of one. Stream traffic always uses UDX_DEFAULT_TTL; only the raw datagram path carries a caller value. UdxSocket::send keeps its signature and delegates to the new send_with_ttl, so no downstream source changes are needed (verified: the full hyperswarm suite, JS interop included, passes against this unchanged). The non-unix fallback has no control messages, so it sets TTL as a socket option when the value changes. That path is cfg'd out on unix and is untested.
UdxSocketInner held a concrete udx_udp::UdpSocket built straight from std::net::UdpSocket, so udx could only ever run on a kernel socket. Testing holepunching needs otherwise: a simulated network with configurable NAT behaviour and deterministic delivery. Adds `trait Transport` (poll_send / poll_recv / local_addr / udp_state), stored as `Box<dyn Transport>`. Erasure rather than a type parameter because UdxSocket is already Arc<Mutex<UdxSocketInner>>, i.e. a type-erased handle behind a pointer and a lock, whereas a generic would bubble through nine public types across four crates (UdxSocket -> MessageDataStream -> IoHandler -> Rpc -> Dht -> Server -> Swarm) and break three published crates for a test seam. Dispatch is one virtual call per sendmmsg batch of up to 32 datagrams, behind a mutex and a syscall. `with_transport` takes `impl Transport` and boxes internally, so callers need no turbofish or Box::new, and no existing signature changes. Verified: the whole hyperswarm suite, JS interop included, passes against this with zero source changes downstream. Batching now follows the transport's own UdpState instead of the platform's, via the new UdpState::with_max_gso_segments. Otherwise a non-kernel transport is handed GSO transmits carrying several datagrams under one header, which it would have to take apart again. `mod udp` becomes public so an out-of-tree Transport can name Transmit and RecvMeta through this crate, rather than via a separate udx-udp dependency that could resolve to a second, incompatible copy of those types. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GxZBTwEoK9qRe48qSQ9E5V
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Holepunching requires being able to set the TTL/hoplimit on packets. When holepunching sometimes we want to open a lot ports on a NAT on our own (source) side, but without hitting the NAT on their (destination) side. To do this we send packets with a low TTL so they leave our NAT, creating a opening, then die before reaching the other (destination) NAT. We want to avoid hitting the destination with these packets because it would look like port scanning.
This PR also abstracts the network interface with a trait. We define the
Transporttrait, which by default wraps theUdpSocket. We do this so we use a simulated network for testing and experimenting with holepunching.