diff --git a/quinn-proto/src/connection/mod.rs b/quinn-proto/src/connection/mod.rs index b0e4cabd2..970cce0df 100644 --- a/quinn-proto/src/connection/mod.rs +++ b/quinn-proto/src/connection/mod.rs @@ -365,6 +365,7 @@ where contents: buf, ecn: None, segment_size: None, + src_ip: self.local_ip, }); } } @@ -554,6 +555,7 @@ where None }, segment_size: None, + src_ip: self.local_ip, }) } diff --git a/quinn-proto/src/endpoint.rs b/quinn-proto/src/endpoint.rs index 151724818..72cb25a79 100644 --- a/quinn-proto/src/endpoint.rs +++ b/quinn-proto/src/endpoint.rs @@ -190,6 +190,7 @@ where ecn: None, contents: buf, segment_size: None, + src_ip: local_ip, }); return None; } @@ -253,7 +254,7 @@ where if !self.is_server() { debug!("packet for unrecognized connection {}", dst_cid); - self.stateless_reset(datagram_len, remote, &dst_cid); + self.stateless_reset(datagram_len, remote, local_ip, &dst_cid); return None; } @@ -288,7 +289,7 @@ where // if !dst_cid.is_empty() { - self.stateless_reset(datagram_len, remote, &dst_cid); + self.stateless_reset(datagram_len, remote, local_ip, &dst_cid); } else { trace!("dropping unrecognized short packet without ID"); } @@ -299,6 +300,7 @@ where &mut self, inciting_dgram_len: usize, remote: SocketAddr, + local_ip: Option, dst_cid: &ConnectionId, ) { /// Minimum amount of padding for the stateless reset to look like a short-header packet @@ -336,6 +338,7 @@ where ecn: None, contents: buf, segment_size: None, + src_ip: local_ip, }); } @@ -535,6 +538,7 @@ where debug!("refusing connection"); self.initial_close( remote, + local_ip, crypto, &src_cid, &temp_loc_cid, @@ -553,6 +557,7 @@ where ); self.initial_close( remote, + local_ip, crypto, &src_cid, &temp_loc_cid, @@ -590,6 +595,7 @@ where ecn: None, contents: buf, segment_size: None, + src_ip: local_ip, }); return None; } @@ -608,6 +614,7 @@ where debug!("rejecting invalid stateless retry token"); self.initial_close( remote, + local_ip, crypto, &src_cid, &temp_loc_cid, @@ -645,7 +652,7 @@ where debug!("handshake failed: {}", e); self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained)); if let ConnectionError::TransportError(e) = e { - self.initial_close(remote, crypto, &src_cid, &temp_loc_cid, e); + self.initial_close(remote, local_ip, crypto, &src_cid, &temp_loc_cid, e); } None } @@ -655,6 +662,7 @@ where fn initial_close( &mut self, destination: SocketAddr, + local_ip: Option, crypto: &Keys, remote_id: &ConnectionId, local_id: &ConnectionId, @@ -683,6 +691,7 @@ where ecn: None, contents: buf, segment_size: None, + src_ip: local_ip, }) } diff --git a/quinn-proto/src/lib.rs b/quinn-proto/src/lib.rs index 6703e9441..23d210f72 100644 --- a/quinn-proto/src/lib.rs +++ b/quinn-proto/src/lib.rs @@ -18,7 +18,13 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::too_many_arguments)] -use std::{convert::TryInto, fmt, net::SocketAddr, ops, time::Duration}; +use std::{ + convert::TryInto, + fmt, + net::{IpAddr, SocketAddr}, + ops, + time::Duration, +}; mod cid_queue; #[doc(hidden)] @@ -277,6 +283,8 @@ pub struct Transmit { /// The segment size if this transmission contains multiple datagrams. /// This is `None` if the transmit only contains a single datagram pub segment_size: Option, + /// Optional source IP address for the datagram + pub src_ip: Option, } // diff --git a/quinn/src/platform/unix.rs b/quinn/src/platform/unix.rs index f99ad4100..36b7a8788 100644 --- a/quinn/src/platform/unix.rs +++ b/quinn/src/platform/unix.rs @@ -2,7 +2,7 @@ use std::{ io, io::IoSliceMut, mem::{self, MaybeUninit}, - net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, os::unix::io::AsRawFd, ptr, }; @@ -273,7 +273,7 @@ pub fn caps() -> UdpCapabilities { *CAPABILITIES } -const CMSG_LEN: usize = 64; +const CMSG_LEN: usize = 80; fn prepare_msg( transmit: &Transmit, @@ -312,6 +312,32 @@ fn prepare_msg( gso::set_segment_size(&mut encoder, segment_size as u16); } + if let Some(ip) = &transmit.src_ip { + if cfg!(target_os = "linux") { + match ip { + IpAddr::V4(v4) => { + let pktinfo = libc::in_pktinfo { + ipi_ifindex: 0, + ipi_spec_dst: unsafe { + *(v4 as *const Ipv4Addr as *const () as *const libc::in_addr) + }, + ipi_addr: libc::in_addr { s_addr: 0 }, + }; + encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo); + } + IpAddr::V6(v6) => { + let pktinfo = libc::in6_pktinfo { + ipi6_ifindex: 0, + ipi6_addr: unsafe { + *(v6 as *const Ipv6Addr as *const () as *const libc::in6_addr) + }, + }; + encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo); + } + } + } + } + encoder.finish(); }