Skip to content

Commit 1c22b91

Browse files
committed
feat(iroh): Make poll_writable precise by using NodeMap::addr_for_send
1 parent 8a24a95 commit 1c22b91

File tree

6 files changed

+87
-24
lines changed

6 files changed

+87
-24
lines changed

Cargo.lock

Lines changed: 3 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,8 @@ unexpected_cfgs = { level = "warn", check-cfg = ["cfg(iroh_docsrs)", "cfg(iroh_l
4040

4141
[workspace.lints.clippy]
4242
unused-async = "warn"
43+
44+
[patch.crates-io]
45+
iroh-quinn = { git = "https://github.com/n0-computer/quinn.git", branch = "matheus23/poll_writable_remote" }
46+
iroh-quinn-proto = { git = "https://github.com/n0-computer/quinn.git", branch = "matheus23/poll_writable_remote" }
47+
iroh-quinn-udp = { git = "https://github.com/n0-computer/quinn.git", branch = "matheus23/poll_writable_remote" }

iroh/src/magicsock.rs

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ pub(crate) struct MagicSock {
222222
/// Nearest relay node ID; 0 means none/unknown.
223223
my_relay: Watchable<Option<RelayUrl>>,
224224
/// Tracks the networkmap node entity for each node discovery key.
225-
node_map: NodeMap,
225+
node_map: Arc<NodeMap>,
226226
/// Tracks the mapped IP addresses
227227
ip_mapped_addrs: IpMappedAddresses,
228228
/// NetReport client
@@ -1737,7 +1737,7 @@ impl Handle {
17371737
my_relay: Default::default(),
17381738
net_reporter: net_reporter.addr(),
17391739
disco_secrets: DiscoSecrets::default(),
1740-
node_map,
1740+
node_map: Arc::new(node_map),
17411741
ip_mapped_addrs,
17421742
udp_disco_sender,
17431743
discovery,
@@ -2129,7 +2129,7 @@ impl RelayDatagramRecvQueue {
21292129
}
21302130

21312131
impl AsyncUdpSocket for MagicSock {
2132-
fn create_io_poller(self: Arc<Self>) -> Pin<Box<dyn quinn::UdpPoller>> {
2132+
fn create_io_poller(self: Arc<Self>, remote: SocketAddr) -> Pin<Box<dyn quinn::UdpPoller>> {
21332133
// To do this properly the MagicSock would need a registry of pollers. For each
21342134
// node we would look up the poller or create one. Then on each try_send we can
21352135
// look up the correct poller and configure it to poll the paths it needs.
@@ -2150,6 +2150,10 @@ impl AsyncUdpSocket for MagicSock {
21502150
let ipv6_poller = self.sockets.v6.as_ref().map(|sock| sock.create_io_poller());
21512151
let relay_sender = self.relay_datagram_send_channel.clone();
21522152
Box::pin(IoPoller {
2153+
mapped_addr: MappedAddr::from(remote),
2154+
node_map: self.node_map.clone(),
2155+
ip_mapped_addrs: self.ip_mapped_addrs.clone(),
2156+
ipv6_reported: self.ipv6_reported.clone(),
21532157
#[cfg(not(wasm_browser))]
21542158
ipv4_poller,
21552159
#[cfg(not(wasm_browser))]
@@ -2248,6 +2252,10 @@ impl AsyncUdpSocket for MagicSock {
22482252

22492253
#[derive(Debug)]
22502254
struct IoPoller {
2255+
mapped_addr: MappedAddr,
2256+
node_map: Arc<NodeMap>,
2257+
ip_mapped_addrs: IpMappedAddresses,
2258+
ipv6_reported: Arc<AtomicBool>,
22512259
#[cfg(not(wasm_browser))]
22522260
ipv4_poller: Pin<Box<dyn quinn::UdpPoller>>,
22532261
#[cfg(not(wasm_browser))]
@@ -2257,21 +2265,60 @@ struct IoPoller {
22572265

22582266
impl quinn::UdpPoller for IoPoller {
22592267
fn poll_writable(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
2260-
// This version returns Ready as soon as any of them are ready.
22612268
let this = &mut *self;
2262-
#[cfg(not(wasm_browser))]
2263-
match this.ipv4_poller.as_mut().poll_writable(cx) {
2264-
Poll::Ready(_) => return Poll::Ready(Ok(())),
2265-
Poll::Pending => (),
2266-
}
2267-
#[cfg(not(wasm_browser))]
2268-
if let Some(ref mut ipv6_poller) = this.ipv6_poller {
2269-
match ipv6_poller.as_mut().poll_writable(cx) {
2270-
Poll::Ready(_) => return Poll::Ready(Ok(())),
2271-
Poll::Pending => (),
2269+
let udp_addr = match &this.mapped_addr {
2270+
MappedAddr::None(dest) => {
2271+
error!(%dest, "Cannot convert to a mapped address, voiding transmit.");
2272+
// Because we can't send these, we just stall whatever endpoint driver got into this state
2273+
// TODO: Maybe we shoulde error out instead? Since there's no way this recovers, right?
2274+
return Poll::Pending;
22722275
}
2273-
}
2274-
this.relay_sender.poll_writable(cx)
2276+
MappedAddr::NodeId(dest) => {
2277+
trace!(%dest, "polling writable");
2278+
2279+
// Get the node's relay address and best direct address, as well
2280+
// as any pings that need to be sent for hole-punching purposes.
2281+
match this
2282+
.node_map
2283+
.addr_for_send(*dest, this.ipv6_reported.load(Ordering::Relaxed))
2284+
{
2285+
Some((_, None, Some(_relay_url))) => {
2286+
return this.relay_sender.poll_writable(cx)
2287+
}
2288+
Some((_, Some(udp_addr), None)) => udp_addr,
2289+
Some((_, Some(udp_addr), Some(_relay_url))) => {
2290+
// If we're in mixed connection mode, then wait for anything to be ready.
2291+
if let Poll::Ready(r) = this.relay_sender.poll_writable(cx) {
2292+
return Poll::Ready(r);
2293+
}
2294+
udp_addr
2295+
}
2296+
_ => {
2297+
// TODO ensure this is correct
2298+
return Poll::Pending;
2299+
}
2300+
}
2301+
}
2302+
MappedAddr::Ip(mapped_addr) => {
2303+
let Some(udp_addr) = this.ip_mapped_addrs.get_ip_addr(mapped_addr) else {
2304+
// TODO idk
2305+
return Poll::Pending;
2306+
};
2307+
udp_addr
2308+
}
2309+
};
2310+
2311+
let poller = match udp_addr {
2312+
SocketAddr::V4(_) => this.ipv4_poller.as_mut(),
2313+
SocketAddr::V6(_) => {
2314+
let Some(poller) = this.ipv6_poller.as_mut() else {
2315+
// TODO error? Trace?
2316+
return Poll::Pending;
2317+
};
2318+
poller.as_mut()
2319+
}
2320+
};
2321+
poller.poll_writable(cx)
22752322
}
22762323
}
22772324

iroh/src/magicsock/node_map.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,20 @@ impl NodeMap {
246246
.handle_call_me_maybe(sender, cm)
247247
}
248248

249+
pub(super) fn addr_for_send(
250+
&self,
251+
addr: NodeIdMappedAddr,
252+
have_ipv6: bool,
253+
) -> Option<(PublicKey, Option<SocketAddr>, Option<RelayUrl>)> {
254+
let mut inner = self.inner.lock().expect("poisoned");
255+
let ep = inner.get_mut(NodeStateKey::NodeIdMappedAddr(addr))?;
256+
let public_key = *ep.public_key();
257+
trace!(dest = %addr, node_id = %public_key.fmt_short(), "dst mapped to NodeId");
258+
let now = Instant::now();
259+
let (udp_addr, relay_url) = ep.addr_for_send(&now, have_ipv6);
260+
Some((public_key, udp_addr, relay_url))
261+
}
262+
249263
#[allow(clippy::type_complexity)]
250264
pub(super) fn get_send_addrs(
251265
&self,

iroh/src/magicsock/node_map/node_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl NodeState {
276276
/// Returns the address(es) that should be used for sending the next packet.
277277
///
278278
/// This may return to send on one, both or no paths.
279-
fn addr_for_send(
279+
pub(super) fn addr_for_send(
280280
&mut self,
281281
now: &Instant,
282282
have_ipv6: bool,

iroh/src/magicsock/udp_conn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl UdpConn {
3434
}
3535

3636
impl AsyncUdpSocket for UdpConn {
37-
fn create_io_poller(self: Arc<Self>) -> Pin<Box<dyn quinn::UdpPoller>> {
37+
fn create_io_poller(self: Arc<Self>, _remote: SocketAddr) -> Pin<Box<dyn quinn::UdpPoller>> {
3838
(*self).create_io_poller()
3939
}
4040

0 commit comments

Comments
 (0)