Skip to content

Commit

Permalink
Allow to use the incoming IP address for sending outgoing packets
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Matthias Einwag authored and djc committed Jan 6, 2021
1 parent 42fd00c commit 1ca7149
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 6 deletions.
2 changes: 2 additions & 0 deletions quinn-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ where
contents: buf,
ecn: None,
segment_size: None,
src_ip: self.local_ip,
});
}
}
Expand Down Expand Up @@ -554,6 +555,7 @@ where
None
},
segment_size: None,
src_ip: self.local_ip,
})
}

Expand Down
15 changes: 12 additions & 3 deletions quinn-proto/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ where
ecn: None,
contents: buf,
segment_size: None,
src_ip: local_ip,
});
return None;
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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");
}
Expand All @@ -299,6 +300,7 @@ where
&mut self,
inciting_dgram_len: usize,
remote: SocketAddr,
local_ip: Option<IpAddr>,
dst_cid: &ConnectionId,
) {
/// Minimum amount of padding for the stateless reset to look like a short-header packet
Expand Down Expand Up @@ -336,6 +338,7 @@ where
ecn: None,
contents: buf,
segment_size: None,
src_ip: local_ip,
});
}

Expand Down Expand Up @@ -535,6 +538,7 @@ where
debug!("refusing connection");
self.initial_close(
remote,
local_ip,
crypto,
&src_cid,
&temp_loc_cid,
Expand All @@ -553,6 +557,7 @@ where
);
self.initial_close(
remote,
local_ip,
crypto,
&src_cid,
&temp_loc_cid,
Expand Down Expand Up @@ -590,6 +595,7 @@ where
ecn: None,
contents: buf,
segment_size: None,
src_ip: local_ip,
});
return None;
}
Expand All @@ -608,6 +614,7 @@ where
debug!("rejecting invalid stateless retry token");
self.initial_close(
remote,
local_ip,
crypto,
&src_cid,
&temp_loc_cid,
Expand Down Expand Up @@ -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
}
Expand All @@ -655,6 +662,7 @@ where
fn initial_close(
&mut self,
destination: SocketAddr,
local_ip: Option<IpAddr>,
crypto: &Keys<S>,
remote_id: &ConnectionId,
local_id: &ConnectionId,
Expand Down Expand Up @@ -683,6 +691,7 @@ where
ecn: None,
contents: buf,
segment_size: None,
src_ip: local_ip,
})
}

Expand Down
10 changes: 9 additions & 1 deletion quinn-proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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<usize>,
/// Optional source IP address for the datagram
pub src_ip: Option<IpAddr>,
}

//
Expand Down
30 changes: 28 additions & 2 deletions quinn/src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -273,7 +273,7 @@ pub fn caps() -> UdpCapabilities {
*CAPABILITIES
}

const CMSG_LEN: usize = 64;
const CMSG_LEN: usize = 80;

fn prepare_msg(
transmit: &Transmit,
Expand Down Expand Up @@ -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();
}

Expand Down

0 comments on commit 1ca7149

Please sign in to comment.