use std::time::Duration;
use n0_error::{Result, StackResultExt};
use n0_tracing_test::traced_test;
use patchbay::{Nat, NatConfig, NatFiltering, NatMapping};
use testdir::testdir;
use tracing::info;
use super::util::{Pair, PathWatcherExt, lab_with_relay};
use crate::util::{ping_accept, ping_open};
enum NatKind {
None,
Easiest,
Easy,
Hard,
}
impl From<NatKind> for Nat {
fn from(value: NatKind) -> Self {
let (mapping, filtering) = match value {
NatKind::None => return Nat::None,
NatKind::Easiest => (
NatMapping::EndpointIndependent,
NatFiltering::EndpointIndependent,
),
NatKind::Easy => (
NatMapping::EndpointIndependent,
NatFiltering::AddressAndPortDependent,
),
NatKind::Hard => (
NatMapping::EndpointDependent,
NatFiltering::AddressAndPortDependent,
),
};
Nat::Custom(NatConfig {
mapping,
filtering,
timeouts: Default::default(),
hairpin: false,
})
}
}
async fn run_nat_holepunch(nat_server: NatKind, nat_client: NatKind) -> Result {
let (lab, relay_map, _relay_guard, guard) = lab_with_relay(testdir!()).await?;
let router_server = lab
.add_router("nat_server")
.nat(nat_server.into())
.build()
.await?;
let router_client = lab
.add_router("nat_client")
.nat(nat_client.into())
.build()
.await?;
let server = lab
.add_device("server")
.uplink(router_server.id())
.build()
.await?;
let client = lab
.add_device("client")
.uplink(router_client.id())
.build()
.await?;
let timeout = Duration::from_secs(15);
Pair::new(relay_map)
.server(server, async move |_dev, _ep, conn| {
let mut paths = conn.paths();
assert!(paths.selected().is_relay(), "connection started relayed");
paths
.wait_ip(timeout)
.await
.context("holepunch to direct")?;
info!("connection became direct");
ping_accept(&conn, timeout).await?;
conn.closed().await;
Ok(())
})
.client(client, async move |_dev, _ep, conn| {
let mut paths = conn.paths();
assert!(paths.selected().is_relay(), "connection started relayed");
paths
.wait_ip(timeout)
.await
.context("holepunch to direct")?;
info!("connection became direct");
ping_open(&conn, timeout).await?;
conn.close(0u32.into(), b"bye");
Ok(())
})
.run()
.await?;
guard.ok();
Ok(())
}
#[tokio::test]
#[traced_test]
async fn nat_none_x_none() -> Result {
run_nat_holepunch(NatKind::None, NatKind::None).await
}
#[tokio::test]
#[traced_test]
async fn nat_none_x_easiest() -> Result {
run_nat_holepunch(NatKind::None, NatKind::Easiest).await
}
#[tokio::test]
#[traced_test]
async fn nat_none_x_easy() -> Result {
run_nat_holepunch(NatKind::None, NatKind::Easy).await
}
#[tokio::test]
#[traced_test]
async fn nat_none_x_hard() -> Result {
run_nat_holepunch(NatKind::None, NatKind::Hard).await
}
#[tokio::test]
#[traced_test]
async fn nat_easiest_x_none() -> Result {
run_nat_holepunch(NatKind::Easiest, NatKind::None).await
}
#[tokio::test]
#[traced_test]
async fn nat_easiest_x_easiest() -> Result {
run_nat_holepunch(NatKind::Easiest, NatKind::Easiest).await
}
#[tokio::test]
#[traced_test]
async fn nat_easiest_x_easy() -> Result {
run_nat_holepunch(NatKind::Easiest, NatKind::Easy).await
}
#[tokio::test]
#[traced_test]
async fn nat_easiest_x_hard() -> Result {
run_nat_holepunch(NatKind::Easiest, NatKind::Hard).await
}
#[tokio::test]
#[traced_test]
async fn nat_easy_x_none() -> Result {
run_nat_holepunch(NatKind::Easy, NatKind::None).await
}
#[tokio::test]
#[traced_test]
async fn nat_easy_x_easiest() -> Result {
run_nat_holepunch(NatKind::Easy, NatKind::Easiest).await
}
#[tokio::test]
#[traced_test]
async fn nat_easy_x_easy() -> Result {
run_nat_holepunch(NatKind::Easy, NatKind::Easy).await
}
#[tokio::test]
#[traced_test]
#[ignore = "not yet passing (and likely can't without port guessing)"]
async fn nat_easy_x_hard() -> Result {
run_nat_holepunch(NatKind::Easy, NatKind::Hard).await
}
#[tokio::test]
#[traced_test]
#[ignore = "not yet passing (but did in iroh 0.35)"]
async fn nat_hard_x_none() -> Result {
run_nat_holepunch(NatKind::Hard, NatKind::None).await
}
#[tokio::test]
#[traced_test]
#[ignore = "not yet passing (but did in iroh 0.35)"]
async fn nat_hard_x_easiest() -> Result {
run_nat_holepunch(NatKind::Hard, NatKind::Easiest).await
}
#[tokio::test]
#[traced_test]
#[ignore = "not yet passing (and likely can't without port guessing)"]
async fn nat_hard_x_easy() -> Result {
run_nat_holepunch(NatKind::Hard, NatKind::Easy).await
}
#[tokio::test]
#[traced_test]
#[ignore = "not yet passing (and likely can't without port guessing)"]
async fn nat_hard_x_hard() -> Result {
run_nat_holepunch(NatKind::Hard, NatKind::Hard).await
}