Skip to content

Commit a5d1853

Browse files
authored
fix: correctly unmap IPv4-mapped local addresses (#2454)
1 parent 30df183 commit a5d1853

File tree

11 files changed

+292
-50
lines changed

11 files changed

+292
-50
lines changed

quic/s2n-quic-core/src/inet/ip.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ impl IpAddress {
3535
}
3636
}
3737

38+
/// Returns `true` if the two addresses are equal from a network perspective.
39+
///
40+
/// This will unmap IPv4-mapped addresses to IpV4 tagged enum values
41+
#[inline]
42+
pub fn unmapped_eq(&self, other: &Self) -> bool {
43+
self.unmap() == other.unmap()
44+
}
45+
3846
/// Converts the IP address into IPv6 if it is IPv4, otherwise the address is unchanged
3947
#[inline]
4048
#[must_use]
@@ -167,6 +175,14 @@ impl SocketAddress {
167175
Self::IpV6(addr) => addr.unmap(),
168176
}
169177
}
178+
179+
/// Returns `true` if the two addresses are equal from a network perspective.
180+
///
181+
/// This will unmap IPv4-mapped addresses to IpV4 tagged enum values
182+
#[inline]
183+
pub fn unmapped_eq(&self, other: &Self) -> bool {
184+
self.unmap() == other.unmap()
185+
}
170186
}
171187

172188
impl Default for SocketAddress {

quic/s2n-quic-core/src/packet/interceptor.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::{
55
event::api::{SocketAddress, Subject},
66
havoc,
77
packet::number::{PacketNumber, PacketNumberSpace},
8+
path::{LocalAddress, RemoteAddress},
89
time::Timestamp,
910
varint::VarInt,
1011
};
@@ -45,9 +46,15 @@ pub trait Interceptor: 'static + Send {
4546
}
4647

4748
#[inline(always)]
48-
fn intercept_rx_remote_port(&mut self, subject: &Subject, port: &mut u16) {
49+
fn intercept_rx_local_address(&mut self, subject: &Subject, addr: &mut LocalAddress) {
4950
let _ = subject;
50-
let _ = port;
51+
let _ = addr;
52+
}
53+
54+
#[inline(always)]
55+
fn intercept_rx_remote_address(&mut self, subject: &Subject, addr: &mut RemoteAddress) {
56+
let _ = subject;
57+
let _ = addr;
5158
}
5259

5360
#[inline(always)]
@@ -116,9 +123,15 @@ where
116123
}
117124

118125
#[inline(always)]
119-
fn intercept_rx_remote_port(&mut self, subject: &Subject, port: &mut u16) {
120-
self.0.intercept_rx_remote_port(subject, port);
121-
self.1.intercept_rx_remote_port(subject, port);
126+
fn intercept_rx_local_address(&mut self, subject: &Subject, addr: &mut LocalAddress) {
127+
self.0.intercept_rx_local_address(subject, addr);
128+
self.1.intercept_rx_local_address(subject, addr);
129+
}
130+
131+
#[inline(always)]
132+
fn intercept_rx_remote_address(&mut self, subject: &Subject, addr: &mut RemoteAddress) {
133+
self.0.intercept_rx_remote_address(subject, addr);
134+
self.1.intercept_rx_remote_address(subject, addr);
122135
}
123136

124137
#[inline(always)]
@@ -188,8 +201,10 @@ where
188201
R: 'static + Send + havoc::Random,
189202
{
190203
#[inline]
191-
fn intercept_rx_remote_port(&mut self, _subject: &Subject, port: &mut u16) {
192-
*port = self.port.havoc_u16(&mut self.random, *port);
204+
fn intercept_rx_remote_address(&mut self, _subject: &Subject, addr: &mut RemoteAddress) {
205+
let port = addr.port();
206+
let port = self.port.havoc_u16(&mut self.random, port);
207+
addr.set_port(port);
193208
}
194209

195210
#[inline]
@@ -229,9 +244,16 @@ where
229244

230245
impl<T: Interceptor> Interceptor for Option<T> {
231246
#[inline]
232-
fn intercept_rx_remote_port(&mut self, subject: &Subject, port: &mut u16) {
247+
fn intercept_rx_local_address(&mut self, subject: &Subject, addr: &mut LocalAddress) {
248+
if let Some(inner) = self.as_mut() {
249+
inner.intercept_rx_local_address(subject, addr)
250+
}
251+
}
252+
253+
#[inline]
254+
fn intercept_rx_remote_address(&mut self, subject: &Subject, addr: &mut RemoteAddress) {
233255
if let Some(inner) = self.as_mut() {
234-
inner.intercept_rx_remote_port(subject, port)
256+
inner.intercept_rx_remote_address(subject, addr)
235257
}
236258
}
237259

quic/s2n-quic-core/src/path/mod.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,20 @@ pub trait Handle: 'static + Copy + Send + fmt::Debug {
5959
/// Returns the remote address for the given handle
6060
fn remote_address(&self) -> RemoteAddress;
6161

62-
/// Updates the remote port to the given value
63-
fn set_remote_port(&mut self, port: u16);
62+
/// Updates the remote address to the given value
63+
fn set_remote_address(&mut self, addr: RemoteAddress);
6464

6565
/// Returns the local address for the given handle
6666
fn local_address(&self) -> LocalAddress;
6767

68+
/// Updates the local address to the given value
69+
fn set_local_address(&mut self, addr: LocalAddress);
70+
6871
/// Returns `true` if the two handles are equal from a network perspective
6972
///
7073
/// This function is used to determine if a connection has migrated to another
7174
/// path.
72-
fn eq(&self, other: &Self) -> bool;
75+
fn unmapped_eq(&self, other: &Self) -> bool;
7376

7477
/// Returns `true` if the two handles are strictly equal to each other, i.e.
7578
/// byte-for-byte.
@@ -95,6 +98,16 @@ macro_rules! impl_addr {
9598
#[cfg_attr(kani, derive(kani::Arbitrary))]
9699
pub struct $name(pub SocketAddress);
97100

101+
impl $name {
102+
/// Returns `true` if the two addresses are equal from a network perspective.
103+
///
104+
/// This will unmap IPv4-mapped addresses to IpV4 tagged enum values
105+
#[inline]
106+
pub fn unmapped_eq(&self, other: &Self) -> bool {
107+
self.0.unmapped_eq(&other.0)
108+
}
109+
}
110+
98111
impl From<event::api::SocketAddress<'_>> for $name {
99112
#[inline]
100113
fn from(value: event::api::SocketAddress<'_>) -> Self {
@@ -162,8 +175,8 @@ impl Handle for RemoteAddress {
162175
}
163176

164177
#[inline]
165-
fn set_remote_port(&mut self, port: u16) {
166-
self.0.set_port(port)
178+
fn set_remote_address(&mut self, addr: RemoteAddress) {
179+
*self = addr;
167180
}
168181

169182
#[inline]
@@ -172,8 +185,13 @@ impl Handle for RemoteAddress {
172185
}
173186

174187
#[inline]
175-
fn eq(&self, other: &Self) -> bool {
176-
PartialEq::eq(&self.unmap(), &other.unmap())
188+
fn set_local_address(&mut self, _addr: LocalAddress) {
189+
// nothing to update
190+
}
191+
192+
#[inline]
193+
fn unmapped_eq(&self, other: &Self) -> bool {
194+
Self::unmapped_eq(self, other)
177195
}
178196

179197
#[inline]
@@ -218,8 +236,8 @@ impl Handle for Tuple {
218236
}
219237

220238
#[inline]
221-
fn set_remote_port(&mut self, port: u16) {
222-
self.remote_address.set_port(port)
239+
fn set_remote_address(&mut self, addr: RemoteAddress) {
240+
self.remote_address = addr;
223241
}
224242

225243
#[inline]
@@ -228,9 +246,14 @@ impl Handle for Tuple {
228246
}
229247

230248
#[inline]
231-
fn eq(&self, other: &Self) -> bool {
232-
PartialEq::eq(&self.local_address.unmap(), &other.local_address.unmap())
233-
&& Handle::eq(&self.remote_address, &other.remote_address)
249+
fn set_local_address(&mut self, addr: LocalAddress) {
250+
self.local_address = addr;
251+
}
252+
253+
#[inline]
254+
fn unmapped_eq(&self, other: &Self) -> bool {
255+
self.local_address.unmapped_eq(&other.local_address)
256+
&& self.remote_address.unmapped_eq(&other.remote_address)
234257
}
235258

236259
#[inline]

quic/s2n-quic-core/src/xdp/encoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ mod tests {
409409

410410
header.path.swap();
411411

412-
assert!(Handle::eq(&header.path, &message.path));
412+
assert!(header.path.unmapped_eq(&message.path));
413413
assert_eq!(header.ecn, message.ecn);
414414
assert_eq!(payload.into_less_safe_slice(), &message.payload);
415415
});

quic/s2n-quic-core/src/xdp/path.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ impl Handle for Tuple {
9797
}
9898

9999
#[inline]
100-
fn set_remote_port(&mut self, port: u16) {
101-
self.remote_address.port = port;
100+
fn set_remote_address(&mut self, addr: path::RemoteAddress) {
101+
self.remote_address.ip = addr.ip();
102+
self.remote_address.port = addr.port();
102103
}
103104

104105
#[inline]
@@ -107,7 +108,13 @@ impl Handle for Tuple {
107108
}
108109

109110
#[inline]
110-
fn eq(&self, other: &Self) -> bool {
111+
fn set_local_address(&mut self, addr: path::LocalAddress) {
112+
self.local_address.ip = addr.ip();
113+
self.local_address.port = addr.port();
114+
}
115+
116+
#[inline]
117+
fn unmapped_eq(&self, other: &Self) -> bool {
111118
// TODO only compare everything if the other is all filled out
112119
PartialEq::eq(&self.local_address.unmap(), &other.local_address.unmap())
113120
&& PartialEq::eq(&self.remote_address.unmap(), &other.remote_address.unmap())

quic/s2n-quic-platform/src/message/msg/handle.rs

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use super::ext::Ext as _;
55
use crate::{features, message::cmsg::Encoder};
66
use libc::msghdr;
77
use s2n_quic_core::{
8-
inet::{AncillaryData, SocketAddressV4},
8+
ensure,
9+
inet::{AncillaryData, SocketAddressV4, Unspecified},
910
path::{self, LocalAddress, RemoteAddress},
1011
};
1112

@@ -54,8 +55,8 @@ impl path::Handle for Handle {
5455
}
5556

5657
#[inline]
57-
fn set_remote_port(&mut self, port: u16) {
58-
self.remote_address.0.set_port(port);
58+
fn set_remote_address(&mut self, addr: RemoteAddress) {
59+
self.remote_address = addr;
5960
}
6061

6162
#[inline]
@@ -64,15 +65,41 @@ impl path::Handle for Handle {
6465
}
6566

6667
#[inline]
67-
fn eq(&self, other: &Self) -> bool {
68-
let mut eq = true;
68+
fn set_local_address(&mut self, addr: LocalAddress) {
69+
self.local_address = addr;
70+
}
71+
72+
#[inline]
73+
fn unmapped_eq(&self, other: &Self) -> bool {
74+
ensure!(
75+
self.remote_address.unmapped_eq(&other.remote_address),
76+
false
77+
);
6978

7079
// only compare local addresses if the OS returns them
71-
if features::pktinfo::IS_SUPPORTED {
72-
eq &= self.local_address.eq(&other.local_address);
80+
ensure!(features::pktinfo::IS_SUPPORTED, true);
81+
82+
// Make sure to only compare the fields if they're both set
83+
//
84+
// This avoids cases where we don't have the full context for the local address and find it
85+
// out with a later packet.
86+
if !self.local_address.ip().is_unspecified() && !other.local_address.ip().is_unspecified() {
87+
ensure!(
88+
self.local_address
89+
.ip()
90+
.unmapped_eq(&other.local_address.ip()),
91+
false
92+
);
7393
}
7494

75-
eq && path::Handle::eq(&self.remote_address, &other.remote_address)
95+
if self.local_address.port() > 0 && other.local_address.port() > 0 {
96+
ensure!(
97+
self.local_address.port() == other.local_address.port(),
98+
false
99+
);
100+
}
101+
102+
true
76103
}
77104

78105
#[inline]
@@ -92,3 +119,50 @@ impl path::Handle for Handle {
92119
}
93120
}
94121
}
122+
123+
#[cfg(test)]
124+
mod tests {
125+
use crate::message::msg::Handle;
126+
use s2n_quic_core::{
127+
inet::{IpAddress, IpV4Address},
128+
path::{Handle as _, LocalAddress},
129+
};
130+
131+
/// Checks that unmapped_eq is correct independent of argument ordering
132+
fn reflexive_check(a: Handle, b: Handle) {
133+
assert!(a.unmapped_eq(&b));
134+
assert!(b.unmapped_eq(&a));
135+
}
136+
137+
#[test]
138+
fn unmapped_eq_test() {
139+
// All of these values should be considered equivalent for local addresses
140+
let ips: &[IpAddress] = &[
141+
// if we have an unspecified IP address then don't consider it for equality
142+
IpV4Address::new([0, 0, 0, 0]).into(),
143+
// a regular IPv4 IP should match the IPv4-mapped into IPv6
144+
IpV4Address::new([1, 1, 1, 1]).into(),
145+
IpV4Address::new([1, 1, 1, 1]).to_ipv6_mapped().into(),
146+
];
147+
let ports = [0u16, 4440];
148+
149+
for ip_a in ips {
150+
for ip_b in ips {
151+
for port_a in ports {
152+
for port_b in ports {
153+
reflexive_check(
154+
Handle {
155+
remote_address: Default::default(),
156+
local_address: LocalAddress::from(ip_a.with_port(port_a)),
157+
},
158+
Handle {
159+
remote_address: Default::default(),
160+
local_address: LocalAddress::from(ip_b.with_port(port_b)),
161+
},
162+
);
163+
}
164+
}
165+
}
166+
}
167+
}
168+
}

quic/s2n-quic-qns/src/intercept.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ use s2n_codec::encoder::scatter;
77
use s2n_quic_core::{
88
event::api::Subject,
99
havoc::{self, Strategy as _, *},
10-
packet,
11-
packet::interceptor::{DecoderBufferMut, Havoc},
10+
packet::{
11+
self,
12+
interceptor::{DecoderBufferMut, Havoc},
13+
},
14+
path::RemoteAddress,
1215
};
1316
use structopt::StructOpt;
1417

@@ -113,10 +116,10 @@ impl Interceptor {
113116

114117
impl packet::interceptor::Interceptor for Interceptor {
115118
#[inline]
116-
fn intercept_rx_remote_port(&mut self, subject: &Subject, port: &mut u16) {
119+
fn intercept_rx_remote_address(&mut self, subject: &Subject, addr: &mut RemoteAddress) {
117120
if self.port {
118121
self.strategy_for(subject)
119-
.intercept_rx_remote_port(subject, port)
122+
.intercept_rx_remote_address(subject, addr)
120123
}
121124
}
122125

0 commit comments

Comments
 (0)