Skip to content
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

Renumber packet types and invert Connection ID Flag #956

Merged
merged 1 commit into from
Nov 29, 2017

Conversation

csperkins
Copy link
Contributor

This commit makes the following changes:

  • Renumber long header packet types to count from 0x7f downwards, rather than from 0x01 upwards
  • Renumber short header packet types to count from 0x1f downward, rather than from 0x01 upward
  • Invert the sense of the Connection ID Flag in the short header packets, to become an Omit Connection ID Flag

This is a purely syntactic change, with no semantic impact on QUIC.

The purpose of the changes is to make it possible to easily demultiplex QUIC and STUN packets running on a single UDP port, to enable peer to peer use of QUIC. It also enables demultiplexing of WebRTC packets and QUIC packets on a single UDP port.

Discussed in QUIC and AVTCORE working groups at IETF 100 (draft-aboba-avtcore-quic-multiplexing-01) and on QUIC mailing list.

Addresses issue #426

This commit makes the following changes:
- Renumber long header packet types to count from 0x7f downwards,
  rather than from 0x01 upwards
- Renumber short header packet types to count from 0x1f downward,
  rather than from 0x01 upward
- Invert the sense of the Connection ID Flag in the short header
  packets, to become an Omit COnnection ID Flag

The purpose of the changes is to make it possible to easily demultiplex
QUIC and STUN packets running on a single UDP port, to enable peer to
peer use of QUIC. They also enable demultiplexing of WebRTC packets and
QUIC packets on a single UDP port.

Discussed in QUIC and AVTCORE working groups at IETF 100.
Copy link
Member

@martinthomson martinthomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Colin. As you say, this is a fairly modest change, but I'd like to double check that this doesn't cause issues for people who are already deploying versions of QUIC that might be hard to run concurrent with these new codepoints.

That includes @janaiyengar and @dtikhonov at a minimum.

@marten-seemann
Copy link
Contributor

I recently implemented the header parsing logic for quic-go, such that it can handle both gQUIC and IETF QUIC. You might find my summary of the different formats for the type byte useful: https://docs.google.com/document/d/162BtfR84c6xTvAG6AaPK0dXSQXKahMrtJOXxNnpcwDw/edit?usp=sharing.

The current packet header format has the nice property that it is possible to decide if the packet is a gQUIC or an IETF QUIC packet by just looking at the type byte. This is possible due to the following properties:

for packets sent by the server

For packets sent by the server the client already knows the QUIC version needed for parsing the packet, as soon as version negotiation is completed. Therefore, the only requirement here is that the type byte used for the Version Negotiation Packet in IETF QUIC is not used in gQUIC, and the type byte used for the gQUIC VNP is not used in IETF QUIC.
This property is guaranteed by the first bit of the type byte alone already (0x80 is unset in gQUIC, and set for an IETF QUIC VNP).

for packets sent by the client

All gQUIC packets have bits 0x80 and 0x40 unset, and currently IETF QUIC packets either have 0x80 set (for long headers) or 0x40 set (for short headers, if connection ID omission is not allowed, which I assume is what server implementations will do).
This property would be changed by this PR (because the connection ID bit is inverted), so a server wouldn't be able to distinguish between gQUIC and short header packets.

@csperkins
Copy link
Contributor Author

I haven't looked at gQUIC, but does it matter that the "server wouldn't be able to distinguish between gQUIC and short header packets"? Can it tell from the initial long header packets if the flow is gQUIC or IETF QUIC?

@marten-seemann
Copy link
Contributor

marten-seemann commented Nov 22, 2017

In order to do that, you would have to associate the packet with a flow, which would mean that the server would have to extract the connection ID from the header first. This might or might not be possible, depending on how we shift around the location of the connection ID.
Furthermore, this would have the not so nice property that packet handling would not be truly stateless anymore (since you'd have to do lookups in the connection map).

@martinthomson
Copy link
Member

First, assume that you require a connection ID at the server. The general case for packets sent by clients in gQUIC is 0b00pp1000. The change to short headers in this PR makes the typical IETF QUIC type byte 0b00k111pp, though obviously we don't make any promises about those 1 octets (and we will probably want to grease them).

One possible strategy we could pursue here is to disable greasing until we wean people off gQUIC. Or - perhaps better - negotiate greasing. Actually, I like that second idea, because it suits Colin's use case very nicely.

@marten-seemann
Copy link
Contributor

@martinthomson: Right, I missed that part. Relying on bit 0x4 might be the solution here.
It doesn't change your argument, but clients in gQUIC can set the Version Flag, so the general case will be 0b00pp100v.

In general, I don't think greasing should be negotiated (we should always grease as much as possible), but if that's the only way to make a transition possible, this wouldn't be a bad option.

@martinthomson
Copy link
Member

I wouldn't have suggested negotiation if not for this realtime case. There, they probably want us to avoid using certain values (blame RFC 7983). Negotiation would be a solution to that problem, and as long as the overwhelming majority of QUIC traffic greased, then it would all work out. Of course, they negotiate sessions and could limit greasing to a narrower range of values.

@csperkins
Copy link
Contributor Author

Right – we likely want to grease by default, but have the ability to disable/limit greasing for certain flows where we know the greasing will conflict with other uses of the port.

@dtikhonov
Copy link
Member

I did a write-up about this some time ago. I think this change will make it easier to differentiate between gQUIC and QUIC. Types 0x1F, 0x1E, and 0x1D would give us the following gQUIC interpretations, in order:

  1. 2-byte packet number; connection ID, nonce, public reset, version bits set.
  2. 2-byte packet number; connection ID, nonce, public reset bits set.
  3. 2-byte packet number; connection ID, nonce, version bits set.

gQUIC public resets do not have two-byte packet numbers. Likewise, gQUIC version negotiation packets are unlikely to have two-byte packet numbers.

@ianswett
Copy link
Contributor

I think this change doesn't change how difficult it is to support GQUIC and IETF QUIC at once.

The fact we're not moving the connection ID really helps, since we can always lookup by connection ID first, and then if nothing is found, try to parse it as a handshake packet that creates a new connection.

On the client side, the main issue would be version negotiation, since for every other packet, the client knows the version in use. This VN codepoint is not used in GQUIC, so we should be all set.

But to pop the stack, I'm trying to walk through why the heuristics approach outlined in the draft isn't sufficient in practice. The negatives like not being able to run DTLS and QUIC at the same time on the same 5-tuple seem like non-issues. Can someone outline what the issues are that motivate this change?
https://tools.ietf.org/html/draft-aboba-avtcore-quic-multiplexing-01#section-2.3

The issue of greasing is real. Unfortunately, I think we may need a way to communicate codepoints not to grease for WebRTC, GQUIC, and other future use cases.

If a server is running GQUIC and IETF QUIC at once, then it is actually using all the codepoints, just for two different protocols, so the space is effectively being greased.

@martinthomson
Copy link
Member

It's possible that we can still grease even for cases where multiplexing with RTP is desired, we just might multiplex less. Would it help if I wrote up what I am considering specifying for greasing?

@martinthomson martinthomson merged commit 1344895 into quicwg:master Nov 29, 2017
@dtikhonov
Copy link
Member

@ianswett writes:

I think this change doesn't change how difficult it is to support GQUIC and IETF QUIC at once.

Could you please elaborate why you think that?

And (emphasis added):

The fact we're not moving the connection ID really helps, since we can always lookup by connection ID first, and then if nothing is found, try to parse it as a handshake packet that creates a new connection.

This is quite expensive compared to simply examining the packet header! One can also imagine a situation where a demultiplexer forwards packets to two separate (processes or machines) QUIC implementations: gQUIC and IETF QUIC. The demux would not have the associated state.

The fallback to looking up a connection for each packet to determine which QUIC version it is should be used as last resort. I hope we can avoid it.

@marten-seemann
Copy link
Contributor

I'm surprised this was merged already. The problem of stateless packet handling wasn't solved, since relying on bit 0x4 will not work as soon as we start greasing (or change anything else for the short headers).

@ianswett
Copy link
Contributor

Agreed on this merging sooner than I expected. I realize we want to nail down the type byte, but I'm still not sure why we're doing this?

@dtikhonov You seemed to think it made supporting both at once easier than before? Is that because all the new code points are either undefined or in practice unused in GQUIC?

@martinthomson Yes, I think it would help to have a specific greasing proposal written up that deals with both coexistence of GQUIC and IETF QUIC, as well as IETF QUIC and WebRTC.

@csperkins
Copy link
Contributor Author

It's possible that we can still grease even for cases where multiplexing with RTP is desired, we just might multiplex less.

When multiplexing, don’t grease the ranges being used by the protocols that you’re multiplexing with. You’re already exercising the ability to send packets using those bits.

Would it help if I wrote up what I am considering specifying for greasing?

Likely, yes

@dtikhonov
Copy link
Member

@ianswett: The reason I think this change makes it easier is that some of the potential ambiguities no longer exist. The heuristics to tell one version from another using the new packet type numbering scheme based are described in my original post above. You state that you do not think this change make it easier to differentiate between packet types; I'd like to know why.

@ianswett
Copy link
Contributor

Our server always requires connection id to be present on packets it receives, so the ambiguity you mention in your original post is not a problem.

With the previous numbering scheme, on the server side, we can just look at the first two bits. If either is 1, then it's IETF QUIC, and if both are 0, it's GQUIC.

In the new scheme, I can still check the first bit for the long header, and then I can check the nonce bit, and if it's that's 1, it must be IETF QUIC because the client never sends a diversification nonce.

But I believe the nonce bit is the bit that would end up being the spin bit, which could start making things pretty tricky. It's probably worth thinking through what happens if two bits are reserved for measurement.

@dtikhonov
Copy link
Member

With the previous numbering scheme, on the server side, we can just look at the first two bits. If either is 1, then it's IETF QUIC, and if both are 0, it's GQUIC.

I see. If one can assume that 0x40 (multipath) bit is never set in GQUIC, then this is indeed very easy. I did not make this assumption in my analyses to err on the conservative side.

But I believe the nonce bit is the bit that would end up being the spin bit, which could start making things pretty tricky. It's probably worth thinking through what happens if two bits are reserved for measurement.

Are you saying that the packet type in the short header would be narrower and occupy fewer than the currently-allocated low five bits? (I apologize for not being on top of this development.)

@marten-seemann
Copy link
Contributor

But I believe the nonce bit is the bit that would end up being the spin bit, which could start making things pretty tricky.

That would be really unpleasant. What is the reason for inverting the Connection ID flag?
We had a type byte format that worked perfectly in all corner cases, and by merging this PR, we just destroyed this feature and create a big problem for all people who have gQUIC deployed.

@ianswett
Copy link
Contributor

Yes, the 0x40 bit is always 0, because we never deployed multipath.

There's been quite a bit of discussion about the spin bit, including a new draft sent out today:
https://datatracker.ietf.org/doc/html/draft-trammell-quic-spin-00

The current proposal makes the type shorter, but the nonce bit would still always be 1 in the above proposal, so as long as no other bits are used for anything, we should still be able to use the plan of looking at the first bit or the nonce bit to detect IETF QUIC?

But as Marten says, this PR definitely makes things more complex than I originally thought, particularly in light of other potential header changes.

@janaiyengar
Copy link
Contributor

I'm surprised to see this PR merged -- I don't think we had agreed on STUN types being necessary to multiplex with. In addition to concerns about GQUIC vs QUIC, there remains the high-level question of what do we need to multiplex QUIC with on the same port. This was a conversation that made good progress in Singapore, and I was expecting to continue it and finalize on list.

@janaiyengar
Copy link
Contributor

Let's have discussions on issues and not PRs. Let's please continue this on #426.

@ronieven
Copy link

ronieven commented Dec 6, 2017

My understanding from the meeting was that there is no need to multiplex short header with stun so the change of the type values for the short header is a surprise and I did not see any discussion on the QUIC mailing list about this change. It is not mentioned also in the Singapore meeting notes. The change that was agreed as far as I remember was to flip the value of the connection ID flag since it will be on for the peer to peer case.

@csperkins
Copy link
Contributor Author

We certainly need to demux QUIC short header packets with STUN, to maintain bindings.

Changing the packet type values doesn't prevent the spin bit, although it might change which bit is used as the spin bit.

@britram
Copy link
Contributor

britram commented Dec 6, 2017

Note also that #989 would give us more degrees of freedom in the short header first octet.

ianswett added a commit that referenced this pull request Dec 6, 2017
Un-does most of the changes in PR #956, to give time for rough consensus to be reached before changing the type byte.
@ekr
Copy link
Collaborator

ekr commented Dec 6, 2017

Colin is right here. You have to multiplex short headers with STUN b/c of consent checks, as well as candidates you learn about late

@janaiyengar
Copy link
Contributor

janaiyengar commented Dec 6, 2017

Can we please continue this discussion on #426? This is a merged PR, and as such, effectively dead space.

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

Successfully merging this pull request may close these issues.

10 participants