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

A 17 octet connection ID #1088

Closed
wants to merge 3 commits into
base: invariants
from

Conversation

Projects
None yet
9 participants
@martinthomson
Member

martinthomson commented Jan 31, 2018

This comes out of the discussion about what it takes to construct a sensible
connection ID for NEW_CONNECTION_ID. Trying to cram sufficient routing state
into a 64-bit identifier is tricky, especially since that information needs to
be encrypted and probably also authenticated (at least a little). Using a
block cipher would be ideal, but all the 64-bit ciphers are pretty bad, whereas
all the 128-bit ones are both faster and more available.

The extra 8 bits is for accounting. If you want to manage key rotation, a key
identifier is helpful. Having extra space allows for that to be added without
forcing the block cipher to protect less than 128 octets, which can be hard.

The way that packet overheads are managed are different for long and short
headers. In long headers, the overheads are much higher, but we don't send
many of those, so it's not a big deal. For short headers, each endpoint sends
a truncate_connection_id transport parameter which sets the length of the
connection ID that will be used in short headers. Each endpoint sets their own
limit and their peer has to follow this limit. Anywhere from 0 (no connection
ID) to 17 (all of the connection ID) can be set.

This also closes the discussion about whether connection ID is a number or not.
With truncation, treating it as a number no longer makes sense.

Note that this change shows the corresponding changes to the invariants draft
on the assumption that it will need to be changed at the same time. We can take
this before adoption, but it will need to be rebased if we do.

Closes #1052, #821, #834, #833, #881.

Make connection ID 136 bits long
This comes out of the discussion about what it takes to construct a sensible
connection ID for NEW_CONNECTION_ID.  Trying to cram sufficient routing state
into a 64-bit identifier is tricky, especially since that information needs to
be encrypted and probably also authenticated (at least a little).  Using a
block cipher would be ideal, but all the 64-bit ciphers are pretty bad, whereas
all the 128-bit ones are both faster and more available.

The extra 8 bits is for accounting.  If you want to manage key rotation, a key
identifier is helpful.  Having extra space allows for that to be added without
forcing the block cipher to protect less than 128 octets, which can be hard.

The way that packet overheads are managed are different for long and short
headers.  In long headers, the overheads are much higher, but we don't send
many of those, so it's not a big deal.  For short headers, each endpoint sends
a truncate_connection_id transport parameter which sets the length of the
connection ID that will be used in short headers.  Each endpoint sets their own
limit and their peer has to follow this limit.  Anywhere from 0 (no connection
ID) to 17 (all of the connection ID) can be set.

This also closes the discussion about whether connection ID is a number or not.
With truncation, treating it as a number no longer makes sense.

Note that this change shows the corresponding changes to the invariants draft
on the assumption that it will need to be changed at the same time.

Closes #1052, #821, #834, #833, #881.
@mcmanus

This comment has been minimized.

Contributor

mcmanus commented Jan 31, 2018

thanks. I think this accurately reflects which way the wind was blowing in Melbourne. The most important property to me, which this reflects, is that the connID is actually a fixed size which might be encoded in shorthand in headers but is still clearly 17 bytes in other contexts.

A couple thoughts: It would be helpful to be explicit about which of the 17 are omitted when being truncated (i.e. the last or low bytes). truncate is probably technically sufficient but explicit is good.

In previous drafts the default was don't omit cid, and now it is do omit cid (i.e. truncate @0).. given that not all environments are robust without some cid, I think our default should be something other than omit completely.

@mikkelfj

This comment has been minimized.

Contributor

mikkelfj commented Jan 31, 2018

It would be helpful to be explicit about which of the 17 are omitted

Yes I think so.

I was thinking perhaps it would be best to have the CID length exposed. Consider a HA-Proxy that does stateful routing. In a future version where it understood QUIC it could be configured to expect a certain CID length so it is not necessary to have it public. But it does complicate configuration and deployment.

Another concern is that a 17 byte CID needs to be hashed if used for state lookup at the client. If there were som assurances of collision resistance on the early bytes it would be simpler to manage.

@MikeBishop

I like the property that the "real" CID is fixed-size and it gets shortened in certain circumstances. However, I'm concerned that having no indication of CID presence or length in the short header limits the usefulness of the CID for routing for any network appliance not in explicit cooperation with the server. That seems like it has the potential to bite us.

recipient of a packet to identify the packet protection keys that are used to
protect the packet. See {{QUIC-TLS}} for details.
Short Packet Type:
: The remaining 5 bits of octet 0 include one of 32 packet types.
: The remaining 6 bits of octet 0 include one of 32 packet types.

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

If you have one more bit, you now have 64 packet types.

selected QUIC version. See {{version-specific}} for details on how packets
from different versions of QUIC are interpreted.
The header form and connection ID of a short header packet are
version-independent. The remaining fields are specific to the selected QUIC

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

This makes the Connection ID in the short header a difficult invariant. There might be one, but there's no way to identify how long it is. Middleboxes that want to use the CID for routing, unless they're in active collaboration with the server, are kind of stuck. Which might be intentional, but it seems awkward.

This comment has been minimized.

@martinthomson

martinthomson Jan 31, 2018

Member

I think that the purpose of the connection ID is for cooperating intermediaries, not random middleboxen. I'm sure that others will disagree, as we're seeing with the proposal to encrypt packet numbers.

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

I think there's some concern that the connection ID ought to be self-describing. One concern, that I'm slightly sympathetic to, is that you'd still have to configure a co-operating middlebox to align with server policy, which may not be easy to do in all server deployments. I'd recommend leaving this particular question -- of removing the C bit -- out and discuss on an issue instead. The change to 17 byte CIDs and truncation seem quite independent of this question.

This comment has been minimized.

@martinthomson

martinthomson Feb 1, 2018

Member

That change is integral to the PR. The bit only makes sense if you intend to send the connection ID on some packets but not others and if you think that an intermediary that is ignorant of the actual length being able to do something meaningful with both sets of packets.

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

That's not the scenario I outlined in my comment. You run a server farm and purchase a middlebox from a vendor. If you have a connection ID bit, you don't have to configure the middlebox to align with server policy about connection ID, because packets are self-describing.

The bit makes sense only if the size is known to both the server and the middlebox however, so you have a good point. Both entities have to agree on a way to determine the size of the connection ID, and as we've discussed earlier, that can be achieved by making the connection ID a varlen. The spec doesn't have to make it a varlen. A middlebox can simply require that connection IDs are vended with a particular encoding by servers, and that's adequate.

I don't want to design this here; I'm noting that self-describing packets have value. It's a separate issue and this should be discussed separately from this PR.

That said, ekr is right that we had agreed on a design team for this point, so that may be the place to discuss this as well.

using the Omit Connection ID flag. When present, the Connection ID is in the
same location in all packet headers, making it straightforward for middleboxes,
such as load balancers, to locate and use it.
QUIC connections are identified by their 136-bit Connection ID. All long

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

You lower-cased all occurrences of "Connection ID" in this paragraph except this one.

same location in all packet headers, making it straightforward for middleboxes,
such as load balancers, to locate and use it.
QUIC connections are identified by their 136-bit Connection ID. All long
headers contain a connection ID. The connection ID is truncated in the short

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

Since 17 bytes isn't truncated, perhaps "can be" truncated?

headers contain a connection ID. The connection ID is truncated in the short
header, using between 0 and 17 octets. When present, the connection ID is in
the same location in all packet headers, making it straightforward for
middleboxes, such as load balancers, to locate and use it.

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

Except it's not straightforward; see above.

This comment has been minimized.

@martinthomson

martinthomson Jan 31, 2018

Member

Removed that bit. (I don't like making value judgments in specifications anyway.)

* the location and size of the Connection ID field in both header forms,
* the location of the Connection ID field in short headers,

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

Perhaps worth re-emphasizing that the size is not invariant?

connection when they move networks. This includes state that can be hard to
recover such as outstanding requests, which might otherwise be lost with no easy
way to retry them.
QUIC connections are identified by their 136-bit Connection ID or whatever part

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

Along the lines of being explicit about which side we're truncating, maybe call this a "prefix" rather than a "part"?

If the server uses a longer connection ID than the client, there is the
potential for the additional octets to be used to reveal that the stateless
reset is not a regular packet. This is not a serious issue, as it is already
possible to use other fields to distinguish them from other packet.

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

Really? I thought we'd been able to stick to that goal so far.

This comment has been minimized.

@martinthomson

martinthomson Jan 31, 2018

Member

I guess we have. Assuming that we do packet number encryption. Right now the packet number leaks quite a lot of information.

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

How does packet number leak information when there are packet number gaps? Sure, it needs to be fixed so the gaps are asymmetric, but that's basically fixable.

This comment has been minimized.

@martinthomson

martinthomson Feb 1, 2018

Member

The construction of a stateless reset requires that the server provide a packet number. It is probably using a connection ID that it used in the past, so packets can be correlated with previous packets. The difference in packet numbers is observable. A server has to pick a random packet number, which likely means a large gap. That might be used to distinguish a stateless reset.

Packet number encryption fixes that problem. I think that this will change if we decide to do that.

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

Fair enough. I don't grok your point about using a connection ID from the past.

This is another way that a stateless reset might be detectable: while sending a stateless reset the server does not know what the client had negotiated for connection ID truncation in the server -> client direction. If the client required the server to send a connection ID, the stateless reset would likely not carry one.

This comment has been minimized.

@martinthomson

martinthomson Feb 6, 2018

Member

My point is that if there were packets sent on this connection in the past, then the packet numbers there and in the stateless reset might reveal something.

The point about truncation is addressed in this PR (3 paragraphs down). It's ugly though.

is equal to or longer than the connection ID requested by the client. In
choosing a connection ID, the server already knows the client's preferred
transaction ID length, and so can request that the client provide a similarly
long connection ID.

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

This is probably the right choice, but again departs from our current semantics that servers don't have to look at the client's transport params before generating their own.

This comment has been minimized.

@martinthomson

martinthomson Jan 31, 2018

Member

Yeah, but this is still a degree shy of making it a negotiation.

transaction ID length, and so can request that the client provide a similarly
long connection ID.
A server can construct the connection ID such that it includes a record the

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

"a record of", or "an indicator of"

@martinthomson

This comment has been minimized.

Member

martinthomson commented Jan 31, 2018

In previous drafts the default was don't omit cid, and now it is do omit cid (i.e. truncate @0).. given that not all environments are robust without some cid, I think our default should be something other than omit completely.

There are really only two values you can default to without making some sort of judgment about what sort of environment gets preferential treatment: 0 or 17. I don't think that 17 is a great choice. I would rather force the inclusion of the transport parameter in the common case than decide that 8 is the best number.

QUIC connections are identified by their 136-bit Connection ID. All long
headers contain a connection ID. The connection ID is truncated in the short
QUIC connections are identified by their 136-bit connection ID. All long
headers contain a connection ID. The connection ID can truncated in the short

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

can => can be

set of cryptographic handshake messages from either peer. These packets are
expected to be correlated with a connection using the tuple of IP addresses and
ports. Packets that might be reordered in this fashion SHOULD be buffered in
For connections with incomplete cryptographic handshakes packets with the long

This comment has been minimized.

@MikeBishop

MikeBishop Jan 31, 2018

Contributor

Probably want a comma after "handshakes"

@mcmanus

This comment has been minimized.

Contributor

mcmanus commented Feb 1, 2018

I would rather force the inclusion of the transport parameter in the common case

seems reasonable

selected QUIC version. See {{version-specific}} for details on how packets
from different versions of QUIC are interpreted.
The header form and connection ID of a short header packet are
version-independent. The remaining fields are specific to the selected QUIC

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

I think there's some concern that the connection ID ought to be self-describing. One concern, that I'm slightly sympathetic to, is that you'd still have to configure a co-operating middlebox to align with server policy, which may not be easy to do in all server deployments. I'd recommend leaving this particular question -- of removing the C bit -- out and discuss on an issue instead. The change to 17 byte CIDs and truncation seem quite independent of this question.

sent to this endpoint. Values between 0 and 17 inclusive are valid; larger
values MUST be treated as a connection error of type
TRANSPORT_PARAMETER_ERROR. This parameter is set to 0 if the transport
parameter is omitted, meaning that packets with short headers that are sent to

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

I think you meant to say Connection ID here: "This parameter is set to 0 if the transport parameter is omitted".

I'm not sure I like the rest of the phrasing, so how about "This parameter is set to 0 if the connection ID may be omitted in short header packets sent to this endpoint."

This comment has been minimized.

@martinthomson

martinthomson Feb 6, 2018

Member

I just wanted to say that it defaults to 0. I'll revise.

If the server uses a longer connection ID than the client, there is the
potential for the additional octets to be used to reveal that the stateless
reset is not a regular packet. This is not a serious issue, as it is already
possible to use other fields to distinguish them from other packet.

This comment has been minimized.

@janaiyengar

janaiyengar Feb 1, 2018

Contributor

How does packet number leak information when there are packet number gaps? Sure, it needs to be fixed so the gaps are asymmetric, but that's basically fixable.

@ekr

This comment has been minimized.

Collaborator

ekr commented Feb 1, 2018

I don't think this is the right answer. As I mentioned in Melbourne, I believe a better approach is to have the server provide a complete connection ID of variable length and then each side provides exactly that connection ID (or in the case of the server, potentially none at all). Aside from aesthetic considerations, one quite silly side effect of this design is that the client tells the server how to truncate the connection ID without knowing what the server's truncation is, so it's not at all impossible for the server to send you a connection ID where only 8 bytes are meaningful but the client doesn't know that and asks for 12 bytes.

In terms of process, I don't think that this has consensus and I read the minutes as pretty clearly saying that we were going to form a design team to come up with some proposals, and then the WG was going to decide, so this PR seems premature.

@britram

This comment has been minimized.

Contributor

britram commented Feb 1, 2018

I share both of @ekr's concerns here.

@mikkelfj

This comment has been minimized.

Contributor

mikkelfj commented Feb 1, 2018

I share both of @ekr's concerns here.

Also note my proposal for asymmetric CID which would resolve some of those concerns. I think it would require a separate initial packet type when responding to a stateless retry, but then CID and CID length in each direction can be placed in transport params.
#1089

@huitema

Well, OK, that PR matches pretty much what we discussed. I don't know how the truncation will work in practice -- I suspect that the choice will end up being either 0 or 17. And I would really like to consider a symmetric solution, in which packets sent to the client bear a connection ID chosen by the client. But of course, that doesn't play well with Stateless Reset.

1 through 8 of the packet. See {{connection-id}} for more details.
: A connection ID occupies between 0 and 17 octets inclusive, starting from
octet 1. This value can be truncated from the full 17 octet connection ID.
See {{connection-id}} for more details.

This comment has been minimized.

@huitema

huitema Feb 6, 2018

Contributor

So, if I read correctly, there is no indication in the short header of the presence and size of the connection ID field. In practice, this forces clients and servers to pick one same size for all connections. Yes, in theory it can be negotiated on a per connection basis, but you need to parse the CID to retrieve the connection context and find the result of that negotiation. Better not try to change the encoded length over the lifetime of the server...

This comment has been minimized.

@martinthomson

martinthomson Feb 6, 2018

Member

A server has to pick a consistent minimum. It could occasionally choose longer, but the load balancer would just ignore those extra bytes.

: The odd length of the connection ID is chosen to enable efficient construction
of the value using 128-bit ciphers with an additional octet for overheads such
as key identifiers.

This comment has been minimized.

@huitema

huitema Feb 6, 2018

Contributor

OK. Now, in the direction from server to client, are we sending the server chosen CID, or the client chosen one? Because, the client has no way to know how many bytes of the server connection ID it might need before seeing the CID. Yet the client's transport parameters are sent in the client initial packet, before seeing the value chosen by the server. So the clients only practical choices are 0 and 17...

This comment has been minimized.

@martinthomson

martinthomson Feb 6, 2018

Member

Yes, there is general recognition that a client can't use stateless routing. For a client, you are right. This is why I would prefer some symmetric in connection ID selection, but that means making a hard call with respect to the stateless reset.

This comment has been minimized.

@ianswett

ianswett Feb 6, 2018

Contributor

In regards to the stateless reset issue for asymmetric connection IDs, one use case is picking a connection ID so the packet is received by the optimal receive queue and doesn't have to hop threads/CPUs. For use cases like this, it's an optimization, but the machine should be able to route the packet correctly if it chooses to.

Also, I'll note that right now, the client doesn't get to pick any portion of the connection ID, so allowing the client to pick knowing that stateless reset might not route optimally seems acceptable, since the client can always pick the same value as the server, like today.

@@ -552,7 +552,7 @@ older than 1.3 is negotiated.
QUIC requires that the initial handshake packet from a client fit within the
payload of a single packet. The size limits on QUIC packets mean that a record
containing a ClientHello needs to fit within 1171 octets.
containing a ClientHello needs to fit within 1162 octets.

This comment has been minimized.

@huitema

huitema Feb 6, 2018

Contributor

That's not the only change required to the TLS document. The connection ID is also used as in the creation of Handshake Secrets. Pointing out the change in length may not be strictly necessary, but it will be helpful for implementers.

@martinthomson

This comment has been minimized.

Member

martinthomson commented Feb 28, 2018

Parking this. We will need something to capture the design team conclusions, which included two connection IDs.

@martinthomson martinthomson deleted the connid17 branch May 29, 2018

@rmarx rmarx referenced this pull request Nov 21, 2018

Open

Main text #1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment