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.
This commit is contained in:
Matthias Einwag
2021-01-04 18:21:03 +00:00
committed by Dirkjan Ochtman
parent 42fd00c20d
commit 1ca7149de1
4 changed files with 51 additions and 6 deletions
+2
View File
@@ -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,
})
}
+12 -3
View File
@@ -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<IpAddr>,
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<IpAddr>,
crypto: &Keys<S>,
remote_id: &ConnectionId,
local_id: &ConnectionId,
@@ -683,6 +691,7 @@ where
ecn: None,
contents: buf,
segment_size: None,
src_ip: local_ip,
})
}
+9 -1
View File
@@ -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<usize>,
/// Optional source IP address for the datagram
pub src_ip: Option<IpAddr>,
}
//
+28 -2
View File
@@ -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();
}