Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ jobs:
save-if: false

- name: Run all tests
run: cargo test --package "$CRATE" --all-features
run: |
if [ "$CRATE" = "libp2p-upnp" ]; then
# Run integration tests serially to avoid multiple IGD mock servers running in parallel.
cargo test --package "$CRATE" --all-features -- --test-threads 1
else
cargo test --package "$CRATE" --all-features
fi

- name: Check if we compile without any features activated
run: cargo build --package "$CRATE" --no-default-features
Expand Down
69 changes: 37 additions & 32 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ libp2p-swarm-test = { version = "0.6.0", path = "swarm-test" }
libp2p-tcp = { version = "0.44.1", path = "transports/tcp" }
libp2p-tls = { version = "0.6.2", path = "transports/tls" }
libp2p-uds = { version = "0.43.1", path = "transports/uds" }
libp2p-upnp = { version = "0.6.0", path = "protocols/upnp" }
libp2p-upnp = { version = "0.6.1", path = "protocols/upnp" }
libp2p-webrtc = { version = "0.9.0-alpha.2", path = "transports/webrtc" }
libp2p-webrtc-utils = { version = "0.4.0", path = "misc/webrtc-utils" }
libp2p-webrtc-websys = { version = "0.4.0", path = "transports/webrtc-websys" }
Expand Down
6 changes: 5 additions & 1 deletion protocols/upnp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.1

- Add integration tests. See [PR 6264](https://github.com/libp2p/rust-libp2p/pull/6264)

## 0.6.0

- Change `Event::NewExternalAddr` and `Event::ExpiredExternalAddr` from tuple variants to struct variants
Expand Down Expand Up @@ -31,7 +35,7 @@
## 0.4.0

- update igd-next to 0.15.1.
See [PR XXXX](https://github.com/libp2p/rust-libp2p/pull/XXXX).
See [PR 5625](https://github.com/libp2p/rust-libp2p/pull/5625).

<!-- Update to libp2p-core v0.43.0 -->

Expand Down
7 changes: 6 additions & 1 deletion protocols/upnp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "libp2p-upnp"
edition.workspace = true
rust-version.workspace = true
description = "UPnP support for libp2p transports"
version = "0.6.0"
version = "0.6.1"
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
keywords = ["peer-to-peer", "libp2p", "networking"]
Expand All @@ -19,6 +19,11 @@ libp2p-swarm = { workspace = true }
tokio = { workspace = true, default-features = false, features = ["rt"], optional = true }
tracing = { workspace = true }

[dev-dependencies]
libp2p-swarm-test = { path = "../../swarm-test" }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
mock-igd = "0.1"

[features]
tokio = ["igd-next/aio_tokio", "dep:tokio"]

Expand Down
59 changes: 42 additions & 17 deletions protocols/upnp/src/behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,32 @@ pub struct Behaviour {

/// Pending behaviour events to be emitted.
pending_events: VecDeque<Event>,

/// Relaxes certain checks to enable testing in environments without real UPnP infrastructure.
test_mode: bool,
}

impl Default for Behaviour {
fn default() -> Self {
Self {
state: GatewayState::Searching(crate::tokio::search_gateway()),
state: GatewayState::Searching(crate::tokio::search_gateway(None)),
mappings: Default::default(),
pending_events: VecDeque::new(),
test_mode: false,
}
}
}

impl Behaviour {
/// Creates a new `Behaviour` with `test_mode` enabled.
pub fn new_with_test_mode() -> Self {
Self {
test_mode: true,
// Shorten the timeout to speed up `GatewayNotFound` tests.
state: GatewayState::Searching(crate::tokio::search_gateway(Some(
Duration::from_secs(1),
))),
..Default::default()
}
}
}
Expand Down Expand Up @@ -317,7 +335,8 @@ impl NetworkBehaviour for Behaviour {
listener_id,
addr: multiaddr,
}) => {
let Ok((addr, protocol)) = multiaddr_to_socketaddr_protocol(multiaddr.clone())
let Ok((addr, protocol)) =
multiaddr_to_socketaddr_protocol(multiaddr.clone(), self.test_mode)
else {
tracing::debug!("multiaddress not supported for UPnP {multiaddr}");
return;
Expand Down Expand Up @@ -440,7 +459,7 @@ impl NetworkBehaviour for Behaviour {
GatewayState::Searching(ref mut fut) => match Pin::new(fut).poll(cx) {
Poll::Ready(Ok(result)) => match result {
Ok(gateway) => {
if !is_addr_global(gateway.external_addr) {
if !self.test_mode && !is_addr_global(gateway.external_addr) {
self.state =
GatewayState::NonRoutableGateway(gateway.external_addr);
tracing::debug!(
Expand Down Expand Up @@ -614,27 +633,33 @@ impl NetworkBehaviour for Behaviour {
///
/// Fails if the given [`Multiaddr`] does not begin with an IP
/// protocol encapsulating a TCP or UDP port.
///
/// When `test_mode` is true, the private IP check is skipped to allow testing with
/// non-private addresses like 127.0.0.1.
fn multiaddr_to_socketaddr_protocol(
addr: Multiaddr,
test_mode: bool,
) -> Result<(SocketAddr, PortMappingProtocol), ()> {
let mut iter = addr.into_iter();
match iter.next() {
// Idg only supports Ipv4.
Some(multiaddr::Protocol::Ip4(ipv4)) if ipv4.is_private() => match iter.next() {
Some(multiaddr::Protocol::Tcp(port)) => {
return Ok((
SocketAddr::V4(SocketAddrV4::new(ipv4, port)),
PortMappingProtocol::TCP,
));
}
Some(multiaddr::Protocol::Udp(port)) => {
return Ok((
SocketAddr::V4(SocketAddrV4::new(ipv4, port)),
PortMappingProtocol::UDP,
));
Some(multiaddr::Protocol::Ip4(ipv4)) if test_mode || ipv4.is_private() => {
match iter.next() {
Some(multiaddr::Protocol::Tcp(port)) => {
return Ok((
SocketAddr::V4(SocketAddrV4::new(ipv4, port)),
PortMappingProtocol::TCP,
));
}
Some(multiaddr::Protocol::Udp(port)) => {
return Ok((
SocketAddr::V4(SocketAddrV4::new(ipv4, port)),
PortMappingProtocol::UDP,
));
}
_ => {}
}
_ => {}
},
}
_ => {}
}
Err(())
Expand Down
Loading
Loading