-
Notifications
You must be signed in to change notification settings - Fork 902
Allow to specify dcid when creating a client side connection #2234
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
base: master
Are you sure you want to change the base?
Conversation
|
I really only care about be able to do this using the c headers but never less thought it might also be useful to be able to do this via the rust api so I added two new functions there as well. |
Motivation: Sometimes the user want to have control over generating the dcid when creating a new client side connection. This was not possible before. Modification: - Only use dcid for local transport params when the connection is for server side - Use provided odcid as dcid on the client side when given and if not just randomly generate one Result: More flexible usage possible
|
The RFC places requirements for unpredictability and length on the client DCID field; see https://datatracker.ietf.org/doc/html/rfc9000#section-7.2-3 I'm concerned this change can make QUIC connections vulnerable, in ways described in the RFC security considerations, or in ways that are yet to be determined. That said, there could be use cases where a deterministic CID (yet unpredictable to some actors) is useful. Providing safety rails for the majority of default users is important. Perhaps this could be placed behind a very explicit opt-in feature flag with some clear warning are references to the relevant RFC text |
Fair enough...
Yeah that's why I need it :)
That would be fine with me ... That said I think we could also add some validations for the length at least (must be at least 8 bytes). Let me do this in any case. |
|
@LPardue added the length check and also added some more docs. That said I would also be ok to just remove the two new rust functions completely as long as I can do it via FFI / c :) |
| // The Minimum length is 8. | ||
| // See https://datatracker.ietf.org/doc/html/rfc9000#section-7.2-3 | ||
| if dcid.to_vec().len() < 8 { | ||
| return Err(Error::InvalidTransportParam); |
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 this is the best Error value to use...
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 think it would be better to put this validation in the new connect_with_dcid* functions since that is where the new logic is being added. It will also make this common function less busy.
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.
@toidiu I did put it here to have the same safe-guards when using quiche via the C api (I use quiche via the C API) as this function is directly used from there. Otherwise I would have had to duplicate this which I think is more error-prone. WDYT ?
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.
regarding error value, I would define a new one like InvalidDcidInitialization. This would be especially useful for implementing the suggestion to add a new client_dcid: Option<&ConnectionId>, param to the function. We can check that only one of odcid or client_dcd are Some and return that error if some expectation failed.
I'd prefer that level of robustness vs. just relying on debug_assert
|
Just to make it clear.... I basically (as a library user) would like to be able to control how this random value is generated for the DCID. |
|
ping again... |
|
As noted in #2234 (comment), I think this need to be put behind a feature flag. I'm not willing to make this the default behaviour and risk clients accidentally using the API incorrectly |
|
@LPardue that's fair enough let me make the change later today |
|
@LPardue I added the feature flag as requested... PTAL |
| // The Minimum length is 8. | ||
| // See https://datatracker.ietf.org/doc/html/rfc9000#section-7.2-3 | ||
| if dcid.to_vec().len() < 8 { | ||
| return Err(Error::InvalidTransportParam); |
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 think it would be better to put this validation in the new connect_with_dcid* functions since that is where the new logic is being added. It will also make this common function less busy.
| fn with_tls( | ||
| scid: &ConnectionId, odcid: Option<&ConnectionId>, local: SocketAddr, | ||
| scid: &ConnectionId, dcid: Option<&ConnectionId>, local: SocketAddr, | ||
| peer: SocketAddr, config: &Config, tls: tls::Handshake, is_server: bool, | ||
| ) -> Result<Connection<F>> { |
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.
The Option odcid is currently being used to detect if the connection is a Server or Client. i.e. if odcid is Some then implement some server specific logic.
I dont love that we are using Option for this and now that the client can also provide a dcid, we now need a bunch of if server logic, which makes things brittle and harder to maintain.
Given that this is an internal function, I think to make this less error prone we should introduce a new parameter, client_dcid, here instead of reusing odcid.
fn with_tls(
scid: &ConnectionId,
odcid: Option<&ConnectionId>,
client_dcid: Option<&ConnectionId>,
local: SocketAddr,
peer: SocketAddr,
config: &Config,
tls: tls::Handshake,
is_server: bool,
) -> Result<Connection<F>>
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.
@toidiu I am fine with this but then I will also need to add another FFI / c api to use it. I did aim to keep the changes minimal and that's why I did it this way.
Basically I only found out that I need changes when I tried to pass in the dcid via the C api and it just didn't work. So I added the changes here to make it work without any changes in the FFI / c layer.
Just let me know what you guys think should be done and I am happy to do so
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.
Ahh that makes sense. Guessing that would be this function. I am less familiar with where ffi is used and if a breaking change is acceptable so I'll let @LPardue speak more on that.
From the Rust and lib.rs logic maintenance perspective, I would love to not mix dcid and odcid.
Another option could be to add a new function to the ffi layer.. something like quiche_conn_new_with_client_dcid.
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'm a bit confused, are you not using quiche_connect()?
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.
@LPardue no I don't do... I use quiche_conn_new_with_tls as I need to use my own existing SSL pointer:
https://github.com/cloudflare/quiche/blob/master/quiche/src/ffi.rs#L608C19-L608C43
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.
But yeah I think I agree with @toidu that we can probably tweak the internal with_tls and then wrap it by add something in the FFI layer like below where returning null means something failed
pub extern "C" fn quiche_conn_new_with_tls_and_client_dcid(
scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
config: &Config, ssl: *mut c_void,
) -> *mut Connection {
#[cfg(not(feature = "connect-with-dcid"))]
{
return ptr::null_mut();
}
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
let scid = ConnectionId::from_ref(scid);
let dcid = if !dcid.is_null() && dcid_len >= 8 {
Some(ConnectionId::from_ref(unsafe {
slice::from_raw_parts(dcid, dcid_len)
}))
} else {
return ptr::null_mut();
};
let local = std_addr_from_c(local, local_len);
let peer = std_addr_from_c(peer, peer_len);
let tls = unsafe { tls::Handshake::from_ptr(ssl) };
match Connection::with_tls(
&scid,
None, // odcid
dcid.as_ref(),
local,
peer,
config,
tls,
false, // only ever used for client connections
) {
Ok(c) => Box::into_raw(Box::new(c)),
Err(_) => ptr::null_mut(),
}
}
| # Allow to provide generated dcid and connect with it. | ||
| connect-with-dcid = [] |
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: what do you think about
| # Allow to provide generated dcid and connect with it. | |
| connect-with-dcid = [] | |
| # Allow client connections to provide a custom dcid. | |
| custom-client-dcid = [] |
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 kinda prefer this name
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.
@LPardue you prefer the suggested one correct ? I also like it more... Just wanted to check before starting to write the code
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.
Yes. lets go with this suggested one. Also while I'm here nitpicking, any comment that refers to DCID should be capitalized (there's a few of those through the PR. The feautre/functions themself obviously needs to be lowercase
| /// certificate. | ||
| #[cfg(feature = "connect-with-dcid")] | ||
| #[cfg_attr(docsrs, doc(cfg(feature = "connect-with-dcid")))] | ||
| pub fn connect_with_dcid( |
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 would be nice to also include a test for this. Although not sure if we currently have a great way of enable features in tests.
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 didn't see how :( I did validate it via our integration in netty tho.
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 think this is possible. There's a few examples of tests only being run behind certain features in quiche/src/tests.rs, e.g., https://github.com/cloudflare/quiche/blob/master/quiche/src/tests.rs#L7545
Then, we can add another CI test step. For example something like
add a new env in https://github.com/cloudflare/quiche/blob/master/.github/workflows/stable.yml#L9
env:
DEFAULT_OPTIONS: "--features=async,ffi,qlog --workspace"
NO_BORING_OPTIONS: "--features=ffi,qlog --workspace --exclude h3i --exclude tokio-quiche"
CONNECT_WITH_DCID_OPTIONS: "--features=ffi,connect-with-dcid --workspace --exclude h3i --exclude tokio-quiche"
then add a new step around https://github.com/cloudflare/quiche/blob/master/.github/workflows/stable.yml#L85
- name: Run cargo test for connect-with-dcd
if: ${{ matrix.tls-feature == 'boringssl-boring-crate' }}
run: cargo test --verbose --all-targets --features=${{ matrix.tls-feature }} ${{ env.CONNECT_WITH_DCID_OPTIONS }}
Motivation:
Sometimes the user want to have control over generating the dcid when creating a new client side connection. This was not possible before.
Modification:
Result:
More flexible usage possible