Skip to content

Commit 5e997a4

Browse files
authored
fix(netcheck): Do not read from main Conn sockets (#1017)
The get_report function is passed the primary Conn sockets so they can be used to send STUN messages from. However these sockets should not be read from directly, Conn already passes through the STUN payloads. Reading from them directly will lose packets in Conn. When the sockets are not passed in however we should create them ourselves, and read from them. The payloads are now received by the actor in the same way as otherwise, removing a lot of Option<>s.
1 parent 4fc70f5 commit 5e997a4

2 files changed

Lines changed: 105 additions & 116 deletions

File tree

src/hp/netcheck.rs

Lines changed: 101 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use futures::{
1717
};
1818
use rand::seq::IteratorRandom;
1919
use tokio::{
20-
net,
20+
net::UdpSocket,
2121
sync::{self, broadcast, mpsc, RwLock},
2222
task::JoinSet,
2323
time::{self, Duration, Instant},
@@ -203,13 +203,76 @@ impl Client {
203203
pub async fn get_report(
204204
&mut self,
205205
dm: &DerpMap,
206-
stun_conn4: Option<Arc<net::UdpSocket>>,
207-
stun_conn6: Option<Arc<net::UdpSocket>>,
206+
stun_conn4: Option<Arc<UdpSocket>>,
207+
stun_conn6: Option<Arc<UdpSocket>>,
208208
) -> Result<Arc<Report>> {
209-
// TODO: consider if DerpMap should be made to easily clone? It is expensive right
210-
// now.
209+
// If not given UdpSockets to send stun packets, create them.
210+
// TODO: Is failure really fatal?
211+
let stun_conn4 = match stun_conn4 {
212+
Some(stun_conn4) => stun_conn4,
213+
None => {
214+
let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0));
215+
let sock = UdpSocket::bind(addr)
216+
.await
217+
.context("netcheck: failed to bind udp 0.0.0.0:0")?;
218+
let sock = Arc::new(sock);
219+
self.spawn_udp_listener(sock.clone(), self.msg_sender.clone());
220+
sock
221+
}
222+
};
223+
let stun_conn6 = match stun_conn6 {
224+
Some(stun_conn6) => stun_conn6,
225+
None => {
226+
let addr = SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0));
227+
let sock = UdpSocket::bind(addr)
228+
.await
229+
.context("netcheck: failed to bind udp6 [::]:0")?;
230+
let sock = Arc::new(sock);
231+
self.spawn_udp_listener(sock.clone(), self.msg_sender.clone());
232+
sock
233+
}
234+
};
235+
236+
// TODO: consider if DerpMap should be made to easily clone? It seems expensive
237+
// right now.
211238
self.actor.run(dm.clone(), stun_conn4, stun_conn6).await
212239
}
240+
241+
/// Spawns a tokio task reading stun packets from the UDP socket.
242+
fn spawn_udp_listener(&self, sock: Arc<UdpSocket>, sender: mpsc::Sender<ActorMessage>) {
243+
tokio::spawn(async move {
244+
debug!("udp stun socket listener started");
245+
// TODO: Can we do better for buffers here? Probably doesn't matter
246+
// much.
247+
let mut buf = vec![0u8; 64 << 10];
248+
loop {
249+
if let Err(err) = Self::recv_stun_socket(&sock, &mut buf, &sender).await {
250+
// TODO: handle socket closed nicely
251+
warn!(%err, "stun recv failed");
252+
break;
253+
}
254+
}
255+
debug!("udp stun socket listener stopped");
256+
});
257+
}
258+
259+
/// Receive STUN response from a UDP socket, pass it to the actor.
260+
async fn recv_stun_socket(
261+
sock: &UdpSocket,
262+
buf: &mut [u8],
263+
sender: &mpsc::Sender<ActorMessage>,
264+
) -> Result<()> {
265+
let (count, mut from_addr) = sock
266+
.recv_from(buf)
267+
.await
268+
.context("Error reading from stun socket")?;
269+
let payload = &buf[..count];
270+
from_addr.set_ip(to_canonical(from_addr.ip()));
271+
sender
272+
.send(ActorMessage::StunPacket(payload.to_vec(), from_addr))
273+
.await
274+
.context("actor stopped")
275+
}
213276
}
214277

215278
async fn measure_https_latency(_reg: &DerpRegion) -> Result<(Duration, IpAddr)> {
@@ -435,9 +498,9 @@ struct ReportState {
435498
got_hair_stun: broadcast::Receiver<SocketAddr>,
436499
// notified on hair pin timeout
437500
hair_timeout: Arc<sync::Notify>,
438-
pc4: Option<Arc<net::UdpSocket>>,
439-
pc6: Option<Arc<net::UdpSocket>>,
440-
pc4_hair: Arc<net::UdpSocket>,
501+
pc4: Arc<UdpSocket>,
502+
pc6: Arc<UdpSocket>,
503+
pc4_hair: Arc<UdpSocket>,
441504
incremental: bool, // doing a lite, follow-up netcheck
442505
stop_probe: Arc<sync::Notify>,
443506
wait_port_map: wg::AsyncWaitGroup,
@@ -809,8 +872,8 @@ enum ProbeError {
809872
async fn run_probe(
810873
report: Arc<RwLock<Report>>,
811874
resolver: &TokioAsyncResolver,
812-
pc4: Option<Arc<net::UdpSocket>>,
813-
pc6: Option<Arc<net::UdpSocket>>,
875+
pc4: Arc<UdpSocket>,
876+
pc6: Arc<UdpSocket>,
814877
node: DerpNode,
815878
probe: Probe,
816879
in_flight: sync::mpsc::Sender<Inflight>,
@@ -850,33 +913,29 @@ async fn run_probe(
850913
Probe::Ipv4 { .. } => {
851914
// TODO:
852915
// metricSTUNSend4.Add(1)
853-
if let Some(ref pc4) = pc4 {
854-
let n = pc4.send_to(&req, addr).await;
855-
debug!("sending probe IPV4: {:?} to {}", n, addr);
856-
// TODO: || neterror.TreatAsLostUDP(err)
857-
if n.is_ok() && n.unwrap() == req.len() {
858-
result.ipv4_can_send = true;
859-
860-
let (delay, addr) = r.await.map_err(|e| ProbeError::Transient(e.into()))?;
861-
result.delay = Some(delay);
862-
result.addr = Some(addr);
863-
}
916+
let n = pc4.send_to(&req, addr).await;
917+
debug!("sending probe IPV4: {:?} to {}", n, addr);
918+
// TODO: || neterror.TreatAsLostUDP(err)
919+
if n.is_ok() && n.unwrap() == req.len() {
920+
result.ipv4_can_send = true;
921+
922+
let (delay, addr) = r.await.map_err(|e| ProbeError::Transient(e.into()))?;
923+
result.delay = Some(delay);
924+
result.addr = Some(addr);
864925
}
865926
}
866927
Probe::Ipv6 { .. } => {
867-
if let Some(ref pc6) = pc6 {
868-
// TODO:
869-
// metricSTUNSend6.Add(1)
870-
let n = pc6.send_to(&req, addr).await;
871-
debug!("sending probe IPV6: {:?} to {}", n, addr);
872-
// TODO: || neterror.TreatAsLostUDP(err)
873-
if n.is_ok() && n.unwrap() == req.len() {
874-
result.ipv6_can_send = true;
875-
876-
let (delay, addr) = r.await.map_err(|e| ProbeError::Transient(e.into()))?;
877-
result.delay = Some(delay);
878-
result.addr = Some(addr);
879-
}
928+
// TODO:
929+
// metricSTUNSend6.Add(1)
930+
let n = pc6.send_to(&req, addr).await;
931+
debug!("sending probe IPV6: {:?} to {}", n, addr);
932+
// TODO: || neterror.TreatAsLostUDP(err)
933+
if n.is_ok() && n.unwrap() == req.len() {
934+
result.ipv6_can_send = true;
935+
936+
let (delay, addr) = r.await.map_err(|e| ProbeError::Transient(e.into()))?;
937+
result.delay = Some(delay);
938+
result.addr = Some(addr);
880939
}
881940
}
882941
Probe::Https { reg, .. } => {
@@ -1026,8 +1085,8 @@ impl Actor {
10261085
async fn run(
10271086
&mut self,
10281087
dm: DerpMap,
1029-
stun_sock_v4: Option<Arc<net::UdpSocket>>,
1030-
stun_sock_v6: Option<Arc<net::UdpSocket>>,
1088+
stun_sock_v4: Arc<UdpSocket>,
1089+
stun_sock_v6: Arc<UdpSocket>,
10311090
) -> Result<Arc<Report>> {
10321091
let report_state = self
10331092
.create_report_state(&dm, stun_sock_v4.clone(), stun_sock_v6.clone())
@@ -1043,8 +1102,6 @@ impl Actor {
10431102
.await
10441103
}))
10451104
};
1046-
let mut buf4 = vec![0u8; 64 << 10];
1047-
let mut buf6 = vec![0u8; 64 << 10];
10481105
let mut in_flight = HashMap::new();
10491106

10501107
loop {
@@ -1059,20 +1116,6 @@ impl Actor {
10591116
self.receive_stun_packet(&mut in_flight, &pkt, source).await,
10601117
}
10611118
}
1062-
res = maybe_pending(stun_sock_v4.as_ref().map(|c| c.recv_from(&mut buf4))) => {
1063-
match res {
1064-
Err(err) => warn!("failed to read ipv4: {:?}", err),
1065-
Ok((n, addr)) =>
1066-
self.process_packet(&mut in_flight, &buf4[..n], addr).await,
1067-
}
1068-
}
1069-
res = maybe_pending(stun_sock_v6.as_ref().map(|c| c.recv_from(&mut buf6))) => {
1070-
match res {
1071-
Err(err) => warn!("failed to read ipv6: {:?}", err),
1072-
Ok((n, addr)) =>
1073-
self.process_packet(&mut in_flight, &buf6[..n], addr).await,
1074-
}
1075-
}
10761119
res = &mut running => {
10771120
match res {
10781121
Ok(Ok((report, dm))) => {
@@ -1096,13 +1139,13 @@ impl Actor {
10961139
async fn create_report_state(
10971140
&mut self,
10981141
dm: &DerpMap,
1099-
pc4: Option<Arc<net::UdpSocket>>,
1100-
pc6: Option<Arc<net::UdpSocket>>,
1142+
pc4: Arc<UdpSocket>,
1143+
pc6: Arc<UdpSocket>,
11011144
) -> Result<ReportState> {
11021145
let now = Instant::now();
11031146

11041147
// Create a UDP4 socket used for sending to our discovered IPv4 address.
1105-
let pc4_hair = net::UdpSocket::bind("0.0.0.0:0")
1148+
let pc4_hair = UdpSocket::bind("0.0.0.0:0")
11061149
.await
11071150
.context("udp4: failed to bind")?;
11081151

@@ -1113,12 +1156,6 @@ impl Actor {
11131156

11141157
let got_hair_stun_r = self.got_hair_stun.subscribe();
11151158
let if_state = interfaces::State::new().await;
1116-
let pc4 = Some(self.init_stun_conn4(pc4).await?);
1117-
let pc6 = if if_state.have_v6 {
1118-
Some(self.init_stun_conn6(pc6).await?)
1119-
} else {
1120-
None
1121-
};
11221159
let mut do_full = self.reports.next_full
11231160
|| now.duration_since(self.reports.last_full) > FULL_REPORT_INTERVAL;
11241161

@@ -1161,34 +1198,6 @@ impl Actor {
11611198
})
11621199
}
11631200

1164-
async fn init_stun_conn4(
1165-
&self,
1166-
pc4: Option<Arc<net::UdpSocket>>,
1167-
) -> Result<Arc<net::UdpSocket>> {
1168-
if let Some(pc4) = pc4 {
1169-
return Ok(pc4);
1170-
}
1171-
let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0));
1172-
let u4 = net::UdpSocket::bind(addr)
1173-
.await
1174-
.with_context(|| format!("udp4: failed to bind to: {}", addr))?;
1175-
Ok(Arc::new(u4))
1176-
}
1177-
1178-
async fn init_stun_conn6(
1179-
&self,
1180-
pc6: Option<Arc<net::UdpSocket>>,
1181-
) -> Result<Arc<net::UdpSocket>> {
1182-
if let Some(pc6) = pc6 {
1183-
return Ok(pc6);
1184-
}
1185-
let addr = SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0));
1186-
let u6 = net::UdpSocket::bind(addr)
1187-
.await
1188-
.with_context(|| format!("udp6: failed to bind to: {}", addr))?;
1189-
Ok(Arc::new(u6))
1190-
}
1191-
11921201
async fn receive_stun_packet(
11931202
&self,
11941203
in_flight: &mut HashMap<stun::TransactionId, Inflight>,
@@ -1230,21 +1239,6 @@ impl Actor {
12301239
}
12311240
}
12321241

1233-
/// Reads STUN packets from pc until there's an error. In either case, it closes `pc`.
1234-
async fn process_packet(
1235-
&self,
1236-
in_flight: &mut HashMap<stun::TransactionId, Inflight>,
1237-
pkt: &[u8],
1238-
mut addr: SocketAddr,
1239-
) {
1240-
if !stun::is(pkt) {
1241-
// ignore non stun packets
1242-
return;
1243-
}
1244-
addr.set_ip(to_canonical(addr.ip()));
1245-
self.receive_stun_packet(in_flight, pkt, addr).await;
1246-
}
1247-
12481242
async fn finish_and_store_report(&mut self, report: Report, dm: &DerpMap) -> Arc<Report> {
12491243
let report = self.add_report_history_and_set_preferred_derp(report).await;
12501244
self.log_concise_report(&report, dm).await;
@@ -1401,7 +1395,7 @@ impl Actor {
14011395
/// Test if IPv6 works at all, or if it's been hard disabled at the OS level.
14021396
async fn os_has_ipv6() -> bool {
14031397
// TODO: use socket2 to specify binding to ipv6
1404-
let udp = net::UdpSocket::bind("[::1]:0").await;
1398+
let udp = UdpSocket::bind("[::1]:0").await;
14051399
udp.is_ok()
14061400
}
14071401

@@ -1418,14 +1412,6 @@ async fn os_has_ipv6() -> bool {
14181412
// metricHTTPSend = clientmetric.NewCounter("netcheck_https_measure")
14191413
// )
14201414

1421-
/// Resolves to pending if the future is `None`.
1422-
async fn maybe_pending<T>(maybe_fut: Option<impl Future<Output = T>>) -> T {
1423-
match maybe_fut {
1424-
Some(t) => t.await,
1425-
None => futures::future::pending().await,
1426-
}
1427-
}
1428-
14291415
/// Resolves to pending if the inner is `None`.
14301416
#[derive(Debug)]
14311417
struct MaybeFuture<T> {
@@ -1554,7 +1540,7 @@ mod tests {
15541540
let local_addr = "127.0.0.1";
15551541
let bind_addr = "0.0.0.0";
15561542

1557-
let server = net::UdpSocket::bind(format!("{bind_addr}:0")).await?;
1543+
let server = UdpSocket::bind(format!("{bind_addr}:0")).await?;
15581544
let addr = server.local_addr()?;
15591545

15601546
let server_task = tokio::task::spawn(async move {
@@ -1565,7 +1551,7 @@ mod tests {
15651551
server.send_to(&buf[..n], addr).await.unwrap();
15661552
});
15671553

1568-
let client = net::UdpSocket::bind(format!("{bind_addr}:0")).await?;
1554+
let client = UdpSocket::bind(format!("{bind_addr}:0")).await?;
15691555
let data = b"foobar";
15701556
println!("client: send");
15711557
let server_addr = format!("{local_addr}:{}", addr.port());

src/net/ip.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ fn is_link_local(ip: IpAddr) -> bool {
131131
}
132132
}
133133

134-
/// Converts this address to an IpAddr::V4 if it is an IPv4-mapped IPv6 addresses, otherwise it return self as-is.
134+
/// Converts IPv4-mappend IPv6 addresses to IPv4.
135+
///
136+
/// Converts this address to an [`IpAddr::V4`] if it is an IPv4-mapped IPv6 addresses,
137+
/// otherwise it return self as-is.
135138
// TODO: replace with IpAddr::to_canoncial once stabilized.
136139
pub fn to_canonical(ip: IpAddr) -> IpAddr {
137140
match ip {

0 commit comments

Comments
 (0)