-
-
Notifications
You must be signed in to change notification settings - Fork 402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: QUIC Address discovery extension #2043
base: main
Are you sure you want to change the base?
feat: QUIC Address discovery extension #2043
Conversation
276c164
to
b0f0a30
Compare
eaa83bc
to
5c0e633
Compare
5c0e633
to
60b2e57
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this!
@@ -974,6 +981,8 @@ pub(crate) struct State { | |||
send_buffer: Vec<u8>, | |||
/// We buffer a transmit when the underlying I/O would block | |||
buffered_transmit: Option<proto::Transmit>, | |||
/// Our last external address reported by the peer. | |||
pub(crate) observed_external_addr: watch::Sender<Option<SocketAddr>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is pub(crate)
necessary here?
@@ -636,6 +636,12 @@ impl Connection { | |||
// May need to send MAX_STREAMS to make progress | |||
conn.wake(); | |||
} | |||
|
|||
/// Track changed on our external address as reported by the peer. | |||
pub fn observed_external_addr(&self) -> watch::Receiver<Option<SocketAddr>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly document when this is None
, whether it might contain spurious updates, and the influence of configuration and peer support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about exposing the watch::Receiver
as part of the public API, that seems like it's leaking a bunch of implementation details?
@@ -68,6 +68,7 @@ tokio = { workspace = true, features = ["rt", "rt-multi-thread", "time", "macros | |||
tracing-subscriber = { workspace = true } | |||
tracing-futures = { workspace = true } | |||
url = { workspace = true } | |||
tokio-stream = "0.1.15" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this used?
assert_matches!(conn.poll(), Some(Event::HandshakeDataReady)); | ||
assert_matches!( | ||
conn.poll(), | ||
Some(Event::ConnectionLost { reason }) if matches!(reason, ConnectionError::TransportError(_) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit: Why not Some(Event::ConnectionLost { reason: ConnectionError::TransportError(_) })
?
/// NOTE: this test is the same as zero_rtt_happypath, changing client transport parameters on | ||
/// resumption. | ||
#[test] | ||
fn address_discovery_zero_rtt_accepted() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What additional coverage does this case supply? I don't think changing client transport parameters are ever a concern for 0-RTT.
|
||
/// Conjuction of the information contained in the address discovery frames | ||
/// ([`FrameType::OBSERVED_IPV4_ADDR`], [`FrameType::OBSERVED_IPV6_ADDR`]). | ||
#[derive(Debug, PartialEq, Eq, Clone)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[derive(Debug, PartialEq, Eq, Clone)] | |
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
@@ -3760,6 +3874,8 @@ pub enum Event { | |||
DatagramReceived, | |||
/// One or more application datagrams have been sent after blocking | |||
DatagramsUnblocked, | |||
/// Received an observation of our external address from the peer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly document whether this is necessarily different from the previous one, and the required configuration and peer support.
// include in migration | ||
migration_observed_addr = Some(observed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a special case?
} | ||
|
||
impl ObservedAddr { | ||
pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever want to pass something that isn't already a VarInt
in here? If not, skip the generic for simplicity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, import SocketAddr
instead of qualifying it.
/// | ||
/// When enabled, this is reported as a transport parameter. | ||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] | ||
pub(crate) enum Role { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we go to a lot of trouble to convert this to and from a pair of bool
s. Would it it be simpler to represent it that way in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't love the Role
name for something arguably pretty niche, should we make the type name AddressDiscoveryRole
instead? Or maybe with the bool
fields it's no longer really a Role
, either?
|
||
use crate::VarInt; | ||
|
||
pub(crate) const TRANSPORT_PARAMETER_CODE: u64 = 0x9f81a176; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: constants last in the file, please.
/// | ||
/// When enabled, this is reported as a transport parameter. | ||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] | ||
pub(crate) enum Role { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't love the Role
name for something arguably pretty niche, should we make the type name AddressDiscoveryRole
instead? Or maybe with the bool
fields it's no longer really a Role
, either?
{ | ||
return Err(TransportError::PROTOCOL_VIOLATION( | ||
"received OBSERVED_ADDRESS frame when not negotiated", | ||
)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we yield this error from within should_report()
instead? (Maybe also pass in packet.header.space()
?)
new_path.last_observed_addr_report = self.path.last_observed_addr_report.clone(); | ||
if let Some(report) = observed_addr { | ||
if let Some(updated) = new_path.update_observed_addr_report(report) { | ||
self.events.push_back(Event::ObservedAddr(updated)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we push all/most of this logic into a method on PathData
?
} | ||
|
||
impl ObservedAddr { | ||
pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, import SocketAddr
instead of qualifying it.
let seq_no = bytes.get()?; | ||
let ip = if is_ipv6 { | ||
IpAddr::V6(bytes.get()?) | ||
} else { | ||
IpAddr::V4(bytes.get()?) | ||
}; | ||
let port = bytes.get()?; | ||
Ok(Self { seq_no, ip, port }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok(Self {
seq_no: bytes.get()?,
ip: match is_ipv6 {
true => IpAddr::V6(bytes.get()?),
false => IpAddr::V4(bytes.get()?),
},
port: bytes.get()?,
})
if !params.address_discovery_role.is_disabled() { | ||
// duplicate parameter | ||
return Err(Error::Malformed); | ||
} | ||
let value: VarInt = r.get()?; | ||
if len != value.size() { | ||
return Err(Error::Malformed); | ||
} | ||
params.address_discovery_role = value.try_into()?; | ||
tracing::debug!( | ||
role = ?params.address_discovery_role, | ||
"address discovery enabled for peer" | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest rolling this up into a Role::from_parameter()
(name TBD) constructor.
let mut external_addresses = conn.observed_external_addr(); | ||
tokio::spawn(async move { | ||
loop { | ||
if let Some(new_addr) = *external_addresses.borrow_and_update() { | ||
info!(%new_addr, "new external address report"); | ||
} | ||
if external_addresses.changed().await.is_err() { | ||
break; | ||
} | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is widely used enough that it merits real estate in these examples.
@@ -636,6 +636,12 @@ impl Connection { | |||
// May need to send MAX_STREAMS to make progress | |||
conn.wake(); | |||
} | |||
|
|||
/// Track changed on our external address as reported by the peer. | |||
pub fn observed_external_addr(&self) -> watch::Receiver<Option<SocketAddr>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about exposing the watch::Receiver
as part of the public API, that seems like it's leaking a bunch of implementation details?
Implements the Quic Address discovery extension IETF draft