Skip to content

Commit 88dff7b

Browse files
authored
add loopback support (#603)
1 parent 1b3e9c4 commit 88dff7b

File tree

8 files changed

+101
-14
lines changed

8 files changed

+101
-14
lines changed

ice/src/agent/agent_config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ pub struct AgentConfig {
152152
/// Controls if self-signed certificates are accepted when connecting to TURN servers via TLS or
153153
/// DTLS.
154154
pub insecure_skip_verify: bool,
155+
156+
/// Include loopback addresses in the candidate list.
157+
pub include_loopback: bool,
155158
}
156159

157160
impl AgentConfig {

ice/src/agent/agent_gather.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub(crate) struct GatherCandidatesInternalParams {
3434
pub(crate) agent_internal: Arc<AgentInternal>,
3535
pub(crate) gathering_state: Arc<AtomicU8>,
3636
pub(crate) chan_candidate_tx: ChanCandidateTx,
37+
pub(crate) include_loopback: bool,
3738
}
3839

3940
struct GatherCandidatesLocalParams {
@@ -46,6 +47,7 @@ struct GatherCandidatesLocalParams {
4647
ext_ip_mapper: Arc<Option<ExternalIpMapper>>,
4748
net: Arc<Net>,
4849
agent_internal: Arc<AgentInternal>,
50+
include_loopback: bool,
4951
}
5052

5153
struct GatherCandidatesLocalUDPMuxParams {
@@ -56,6 +58,7 @@ struct GatherCandidatesLocalUDPMuxParams {
5658
net: Arc<Net>,
5759
agent_internal: Arc<AgentInternal>,
5860
udp_mux: Arc<dyn UDPMux + Send + Sync>,
61+
include_loopback: bool,
5962
}
6063

6164
struct GatherCandidatesSrflxMappedParasm {
@@ -100,6 +103,7 @@ impl Agent {
100103
ext_ip_mapper: Arc::clone(&params.ext_ip_mapper),
101104
net: Arc::clone(&params.net),
102105
agent_internal: Arc::clone(&params.agent_internal),
106+
include_loopback: params.include_loopback,
103107
};
104108

105109
let w = wg.worker();
@@ -203,6 +207,7 @@ impl Agent {
203207
ext_ip_mapper,
204208
net,
205209
agent_internal,
210+
include_loopback,
206211
} = params;
207212

208213
// If we wanna use UDP mux, do so
@@ -216,6 +221,7 @@ impl Agent {
216221
net,
217222
agent_internal,
218223
udp_mux,
224+
include_loopback,
219225
})
220226
.await;
221227

@@ -226,7 +232,14 @@ impl Agent {
226232
return;
227233
}
228234

229-
let ips = local_interfaces(&net, &interface_filter, &ip_filter, &network_types).await;
235+
let ips = local_interfaces(
236+
&net,
237+
&interface_filter,
238+
&ip_filter,
239+
&network_types,
240+
include_loopback,
241+
)
242+
.await;
230243
for ip in ips {
231244
let mut mapped_ip = ip;
232245

@@ -380,6 +393,7 @@ impl Agent {
380393
net,
381394
agent_internal,
382395
udp_mux,
396+
include_loopback,
383397
} = params;
384398

385399
// Filter out non UDP network types
@@ -388,8 +402,14 @@ impl Agent {
388402

389403
let udp_mux = Arc::clone(&udp_mux);
390404

391-
let local_ips =
392-
local_interfaces(&net, &interface_filter, &ip_filter, &relevant_network_types).await;
405+
let local_ips = local_interfaces(
406+
&net,
407+
&interface_filter,
408+
&ip_filter,
409+
&relevant_network_types,
410+
include_loopback,
411+
)
412+
.await;
393413

394414
let candidate_ips: Vec<std::net::IpAddr> = ext_ip_mapper
395415
.as_ref() // Arc

ice/src/agent/agent_gather_test.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ async fn test_vnet_gather_no_local_ip_address() -> Result<()> {
2424
&a.interface_filter,
2525
&a.ip_filter,
2626
&[NetworkType::Udp4],
27+
false,
2728
)
2829
.await;
2930
assert!(local_ips.is_empty(), "should return no local IP");
@@ -51,8 +52,14 @@ async fn test_vnet_gather_dynamic_ip_address() -> Result<()> {
5152
})
5253
.await?;
5354

54-
let local_ips =
55-
local_interfaces(&nw, &a.interface_filter, &a.ip_filter, &[NetworkType::Udp4]).await;
55+
let local_ips = local_interfaces(
56+
&nw,
57+
&a.interface_filter,
58+
&a.ip_filter,
59+
&[NetworkType::Udp4],
60+
false,
61+
)
62+
.await;
5663
assert!(!local_ips.is_empty(), "should have one local IP");
5764

5865
for ip in &local_ips {
@@ -85,8 +92,14 @@ async fn test_vnet_gather_listen_udp() -> Result<()> {
8592
})
8693
.await?;
8794

88-
let local_ips =
89-
local_interfaces(&nw, &a.interface_filter, &a.ip_filter, &[NetworkType::Udp4]).await;
95+
let local_ips = local_interfaces(
96+
&nw,
97+
&a.interface_filter,
98+
&a.ip_filter,
99+
&[NetworkType::Udp4],
100+
false,
101+
)
102+
.await;
90103
assert!(!local_ips.is_empty(), "should have one local IP");
91104

92105
for ip in local_ips {
@@ -340,8 +353,14 @@ async fn test_vnet_gather_with_interface_filter() -> Result<()> {
340353
})
341354
.await?;
342355

343-
let local_ips =
344-
local_interfaces(&nw, &a.interface_filter, &a.ip_filter, &[NetworkType::Udp4]).await;
356+
let local_ips = local_interfaces(
357+
&nw,
358+
&a.interface_filter,
359+
&a.ip_filter,
360+
&[NetworkType::Udp4],
361+
false,
362+
)
363+
.await;
345364
assert!(
346365
local_ips.is_empty(),
347366
"InterfaceFilter should have excluded everything"
@@ -361,8 +380,14 @@ async fn test_vnet_gather_with_interface_filter() -> Result<()> {
361380
})
362381
.await?;
363382

364-
let local_ips =
365-
local_interfaces(&nw, &a.interface_filter, &a.ip_filter, &[NetworkType::Udp4]).await;
383+
let local_ips = local_interfaces(
384+
&nw,
385+
&a.interface_filter,
386+
&a.ip_filter,
387+
&[NetworkType::Udp4],
388+
false,
389+
)
390+
.await;
366391
assert_eq!(
367392
local_ips.len(),
368393
1,

ice/src/agent/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ pub struct Agent {
104104

105105
pub(crate) udp_network: UDPNetwork,
106106
pub(crate) interface_filter: Arc<Option<InterfaceFilterFn>>,
107+
pub(crate) include_loopback: bool,
107108
pub(crate) ip_filter: Arc<Option<IpFilterFn>>,
108109
pub(crate) mdns_mode: MulticastDnsMode,
109110
pub(crate) mdns_name: String,
@@ -200,6 +201,7 @@ impl Agent {
200201
udp_network: config.udp_network,
201202
internal: Arc::new(ai),
202203
interface_filter: Arc::clone(&config.interface_filter),
204+
include_loopback: config.include_loopback,
203205
ip_filter: Arc::clone(&config.ip_filter),
204206
mdns_mode,
205207
mdns_name,
@@ -465,6 +467,7 @@ impl Agent {
465467
agent_internal: Arc::clone(&self.internal),
466468
gathering_state: Arc::clone(&self.gathering_state),
467469
chan_candidate_tx: Arc::clone(&self.internal.chan_candidate_tx),
470+
include_loopback: self.include_loopback,
468471
};
469472
tokio::spawn(async move {
470473
Self::gather_candidates_internal(params).await;

ice/src/util/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ pub async fn local_interfaces(
9999
interface_filter: &Option<InterfaceFilterFn>,
100100
ip_filter: &Option<IpFilterFn>,
101101
network_types: &[NetworkType],
102+
include_loopback: bool,
102103
) -> HashSet<IpAddr> {
103104
let mut ips = HashSet::new();
104105
let interfaces = vnet.get_interfaces().await;
@@ -123,7 +124,7 @@ pub async fn local_interfaces(
123124
for ipnet in iface.addrs() {
124125
let ipaddr = ipnet.addr();
125126

126-
if !ipaddr.is_loopback()
127+
if (!ipaddr.is_loopback() || include_loopback)
127128
&& ((ipv4requested && ipaddr.is_ipv4()) || (ipv6requested && ipaddr.is_ipv6()))
128129
&& ip_filter
129130
.as_ref()

ice/src/util/util_test.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,31 @@ use super::*;
44
async fn test_local_interfaces() -> Result<()> {
55
let vnet = Arc::new(Net::new(None));
66
let interfaces = vnet.get_interfaces().await;
7-
let ips = local_interfaces(&vnet, &None, &None, &[NetworkType::Udp4, NetworkType::Udp6]).await;
8-
log::info!("interfaces: {:?}, ips: {:?}", interfaces, ips);
7+
let ips = local_interfaces(
8+
&vnet,
9+
&None,
10+
&None,
11+
&[NetworkType::Udp4, NetworkType::Udp6],
12+
false,
13+
)
14+
.await;
15+
16+
let ips_with_loopback = local_interfaces(
17+
&vnet,
18+
&None,
19+
&None,
20+
&[NetworkType::Udp4, NetworkType::Udp6],
21+
true,
22+
)
23+
.await;
24+
assert!(ips_with_loopback.is_superset(&ips));
25+
assert!(!ips.iter().any(|ip| ip.is_loopback()));
26+
assert!(ips_with_loopback.iter().any(|ip| ip.is_loopback()));
27+
log::info!(
28+
"interfaces: {:?}, ips: {:?}, ips_with_loopback: {:?}",
29+
interfaces,
30+
ips,
31+
ips_with_loopback
32+
);
933
Ok(())
1034
}

webrtc/src/api/setting_engine/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub struct Candidates {
4444
pub multicast_dns_host_name: String,
4545
pub username_fragment: String,
4646
pub password: String,
47+
pub include_loopback_candidate: bool,
4748
}
4849

4950
#[derive(Default, Clone)]
@@ -279,6 +280,15 @@ impl SettingEngine {
279280
self.disable_srtcp_replay_protection = is_disabled;
280281
}
281282

283+
/// set_include_loopback_candidate enables webrtc-rs to gather loopback candidates, it is
284+
/// useful for, e.g., some VMs that have public IP mapped to loopback interface.
285+
/// Note that allowing loopback candidates to be gathered is technically inconsistent with the
286+
/// webRTC spec (see https://www.rfc-editor.org/rfc/rfc8445#section-5.1.1.1). This option is
287+
/// therefore disabled by default, and should be used with caution.
288+
pub fn set_include_loopback_candidate(&mut self, allow_loopback: bool) {
289+
self.candidates.include_loopback_candidate = allow_loopback;
290+
}
291+
282292
/// set_sdp_media_level_fingerprints configures the logic for dtls_transport Fingerprint insertion
283293
/// If true, fingerprints will be inserted in the sdp at the fingerprint
284294
/// level, instead of the session level. This helps with compatibility with

webrtc/src/ice_transport/ice_gatherer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ impl RTCIceGatherer {
122122
ip_filter: self.setting_engine.candidates.ip_filter.clone(),
123123
nat_1to1_ips: self.setting_engine.candidates.nat_1to1_ips.clone(),
124124
nat_1to1_ip_candidate_type: nat_1to1_cand_type,
125+
include_loopback: self.setting_engine.candidates.include_loopback_candidate,
125126
net: self.setting_engine.vnet.clone(),
126127
multicast_dns_mode: mdns_mode,
127128
multicast_dns_host_name: self

0 commit comments

Comments
 (0)