Skip to content

Commit 1ca7149

Browse files
Matthias Einwagdjc
Matthias Einwag
authored andcommitted
Allow to use the incoming IP address for sending outgoing packets
This is a follow-up for #943. When a socket is bound to a wildcard IP address, sending the outgoing IP might use a different source IP address than the one the packet was received on, since the OS might not be able to identify the necessary route. This would lead packets not allowing to reach the client. This change adds a setting which will set an explicit source address in all outgoing packets. The source address which will be used is the local IP address which was used to receive the initial incoming packet.
1 parent 42fd00c commit 1ca7149

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

quinn-proto/src/connection/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ where
365365
contents: buf,
366366
ecn: None,
367367
segment_size: None,
368+
src_ip: self.local_ip,
368369
});
369370
}
370371
}
@@ -554,6 +555,7 @@ where
554555
None
555556
},
556557
segment_size: None,
558+
src_ip: self.local_ip,
557559
})
558560
}
559561

quinn-proto/src/endpoint.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ where
190190
ecn: None,
191191
contents: buf,
192192
segment_size: None,
193+
src_ip: local_ip,
193194
});
194195
return None;
195196
}
@@ -253,7 +254,7 @@ where
253254

254255
if !self.is_server() {
255256
debug!("packet for unrecognized connection {}", dst_cid);
256-
self.stateless_reset(datagram_len, remote, &dst_cid);
257+
self.stateless_reset(datagram_len, remote, local_ip, &dst_cid);
257258
return None;
258259
}
259260

@@ -288,7 +289,7 @@ where
288289
//
289290

290291
if !dst_cid.is_empty() {
291-
self.stateless_reset(datagram_len, remote, &dst_cid);
292+
self.stateless_reset(datagram_len, remote, local_ip, &dst_cid);
292293
} else {
293294
trace!("dropping unrecognized short packet without ID");
294295
}
@@ -299,6 +300,7 @@ where
299300
&mut self,
300301
inciting_dgram_len: usize,
301302
remote: SocketAddr,
303+
local_ip: Option<IpAddr>,
302304
dst_cid: &ConnectionId,
303305
) {
304306
/// Minimum amount of padding for the stateless reset to look like a short-header packet
@@ -336,6 +338,7 @@ where
336338
ecn: None,
337339
contents: buf,
338340
segment_size: None,
341+
src_ip: local_ip,
339342
});
340343
}
341344

@@ -535,6 +538,7 @@ where
535538
debug!("refusing connection");
536539
self.initial_close(
537540
remote,
541+
local_ip,
538542
crypto,
539543
&src_cid,
540544
&temp_loc_cid,
@@ -553,6 +557,7 @@ where
553557
);
554558
self.initial_close(
555559
remote,
560+
local_ip,
556561
crypto,
557562
&src_cid,
558563
&temp_loc_cid,
@@ -590,6 +595,7 @@ where
590595
ecn: None,
591596
contents: buf,
592597
segment_size: None,
598+
src_ip: local_ip,
593599
});
594600
return None;
595601
}
@@ -608,6 +614,7 @@ where
608614
debug!("rejecting invalid stateless retry token");
609615
self.initial_close(
610616
remote,
617+
local_ip,
611618
crypto,
612619
&src_cid,
613620
&temp_loc_cid,
@@ -645,7 +652,7 @@ where
645652
debug!("handshake failed: {}", e);
646653
self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
647654
if let ConnectionError::TransportError(e) = e {
648-
self.initial_close(remote, crypto, &src_cid, &temp_loc_cid, e);
655+
self.initial_close(remote, local_ip, crypto, &src_cid, &temp_loc_cid, e);
649656
}
650657
None
651658
}
@@ -655,6 +662,7 @@ where
655662
fn initial_close(
656663
&mut self,
657664
destination: SocketAddr,
665+
local_ip: Option<IpAddr>,
658666
crypto: &Keys<S>,
659667
remote_id: &ConnectionId,
660668
local_id: &ConnectionId,
@@ -683,6 +691,7 @@ where
683691
ecn: None,
684692
contents: buf,
685693
segment_size: None,
694+
src_ip: local_ip,
686695
})
687696
}
688697

quinn-proto/src/lib.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
#![allow(clippy::cognitive_complexity)]
1919
#![allow(clippy::too_many_arguments)]
2020

21-
use std::{convert::TryInto, fmt, net::SocketAddr, ops, time::Duration};
21+
use std::{
22+
convert::TryInto,
23+
fmt,
24+
net::{IpAddr, SocketAddr},
25+
ops,
26+
time::Duration,
27+
};
2228

2329
mod cid_queue;
2430
#[doc(hidden)]
@@ -277,6 +283,8 @@ pub struct Transmit {
277283
/// The segment size if this transmission contains multiple datagrams.
278284
/// This is `None` if the transmit only contains a single datagram
279285
pub segment_size: Option<usize>,
286+
/// Optional source IP address for the datagram
287+
pub src_ip: Option<IpAddr>,
280288
}
281289

282290
//

quinn/src/platform/unix.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{
22
io,
33
io::IoSliceMut,
44
mem::{self, MaybeUninit},
5-
net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6},
5+
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
66
os::unix::io::AsRawFd,
77
ptr,
88
};
@@ -273,7 +273,7 @@ pub fn caps() -> UdpCapabilities {
273273
*CAPABILITIES
274274
}
275275

276-
const CMSG_LEN: usize = 64;
276+
const CMSG_LEN: usize = 80;
277277

278278
fn prepare_msg(
279279
transmit: &Transmit,
@@ -312,6 +312,32 @@ fn prepare_msg(
312312
gso::set_segment_size(&mut encoder, segment_size as u16);
313313
}
314314

315+
if let Some(ip) = &transmit.src_ip {
316+
if cfg!(target_os = "linux") {
317+
match ip {
318+
IpAddr::V4(v4) => {
319+
let pktinfo = libc::in_pktinfo {
320+
ipi_ifindex: 0,
321+
ipi_spec_dst: unsafe {
322+
*(v4 as *const Ipv4Addr as *const () as *const libc::in_addr)
323+
},
324+
ipi_addr: libc::in_addr { s_addr: 0 },
325+
};
326+
encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
327+
}
328+
IpAddr::V6(v6) => {
329+
let pktinfo = libc::in6_pktinfo {
330+
ipi6_ifindex: 0,
331+
ipi6_addr: unsafe {
332+
*(v6 as *const Ipv6Addr as *const () as *const libc::in6_addr)
333+
},
334+
};
335+
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
336+
}
337+
}
338+
}
339+
}
340+
315341
encoder.finish();
316342
}
317343

0 commit comments

Comments
 (0)