Git Product home page Git Product logo

Comments (9)

huitema avatar huitema commented on July 23, 2024

Defining connection states is hard, but here is what I would suggest:

  1. Client side, up to the "ready" state:
  • attempted (Initial sent, no handshake keys received yet)
  • handshake (handshake keys received)
  • almost ready (1RTT keys received, but handshake done not received yet)
  • ready (or active) (handshake done received from server, or equivalent)
  1. Server side:
  • received (initial received)
  • validated (client address has been verified)
  • handshake (handshake packets received from client, handshake in progress)
  • false start (1RTT keys write received, but handshake not complete yet)
  • ready (handshake complete, handshake done sent)
  1. Both sides:
  • draining (close connection packet sent or received, waiting for some time)
  • disconnected (done with this connection)

from qlog.

marten-seemann avatar marten-seemann commented on July 23, 2024

I like the addition of validated. That can be really useful when debugging the early handshake stages.

  • almost ready (1RTT keys received, but handshake done not received yet)
  • ready (or active) (handshake done received from server, or equivalent)

This could be handshake_completed and handshake_confimred. Both of them would work for client as well as the server.

false start (1RTT keys write received, but handshake not complete yet)

Not sure if we need this. We already have a key_updated event.

from qlog.

marten-seemann avatar marten-seemann commented on July 23, 2024

Here's my proposal:

enum ConnectionState {
    validated, // only for the server, when the client's IP has been validated
    handshake_completed, // TLS handshake successful
    handshake_confirmed, // handshake confirmed, see sec. 4.1.2 of the QUIC-TLS draft
    draining, // CONNECTION_CLOSE sent
    closed // connection actually fully closed, memory freed
}

@rmarx, @huitema What do you think?

from qlog.

huitema avatar huitema commented on July 23, 2024

@marten-seemann I think that's too coarse. You have to consider scenarios in which Initial packets are exchanged for some time before handshake keys are available, e.g., for post quantum, and scenarios in which handshake packets are exchanged for some time before 1RTT keys are available, e.g., client auth. Also, per 4.1.2, handshake confirmed on the server happens at exactly the same time as handshake confirmed. That's why I use a "false start" state for the server.

We are discussing logging options here. Having detailed logging options does not hurt, there is no point in being too parsimonious. If you only want to log a subset of them, that's a fine implementation choice, but that should not prevent precise logging for those who want it.

from qlog.

marten-seemann avatar marten-seemann commented on July 23, 2024

Also, per 4.1.2, handshake confirmed on the server happens at exactly the same time as handshake confirmed.

True. This event would be kind of redundant for the server. It's also kind of redundant redundant for the client, since this is the time when the client drops Handshake keys.

@marten-seemann I think that's too coarse. You have to consider scenarios in which Initial packets are exchanged for some time before handshake keys are available, e.g., for post quantum, and scenarios in which handshake packets are exchanged for some time before 1RTT keys are available, e.g., client auth. [...] That's why I use a "false start" state for the server.

First of all, I don't understand the name "false start". My point in #49 (comment) was that we already have an event for this: it's the key updated event.

Now it seems like both "false start", handshake_comfirmed and handshake_completed are all redundant, since they all accompanied by key generation / key discarding events. Not sure what to make of that...

from qlog.

rmarx avatar rmarx commented on July 23, 2024

So, I've been trying to build a QUIC handshake/connection state machine to see which states we actually need. This initial design is garnered towards expressing the -entire- state machine with all state transitions. While I agree with @marten-seemann above that some of these can be deduced from e.g., key_update events, for now I'd like to keep this event as complete as possible, so it can be used without those other events as well.

I believe that we need 8 states in total (mainly to allow for some phases to last longer than 1 RTT/1 flight):

  1. attempted (initial sent/received)
  2. peer_validated (peer address validated by: client sent Handshake packet OR client used CONNID chosen by the server)
  3. handshake_started
  4. false_start (1 RTT can be sent, but handshake isn't done yet)
  5. handshake_complete (TLS handshake complete: Finished received and sent)
  6. handshake_confirmed (HANDSHAKE_DONE sent/received) (connection is now "active", 1RTT can be sent)
  7. draining (connection_close sent/received)
  8. closed (draining period done)

Following the handshake examples in the transport draft, this would lead to the following events and transitions:

Client:

  • send initial
    • state = attempted
  • get initial
    • state = validated (not really "needed" at the client, but somewhat useful to indicate progress nonetheless)
  • get first Handshake packet
    • state = handshake_started
  • get Handshake packet containing ServerFinished
    • state = handshake_complete
  • send ClientFinished
    • state = false_start
      (1RTT can now be sent)
  • get HANDSHAKE_DONE
    • state = handshake_confirmed

Server:

  • get initial
    • state = attempted
  • send initial (don't think this needs a separate state, since some handshake will always be sent in the same flight as this?)
  • send handshake EE, CERT, CV, ...
    • state = handshake_started
  • send ServerFinished
    • state = false_start
      (1RTT can now be sent)
  • get first handshake packet / something using a server-issued CID of min length
    • state = validated
  • get handshake packet containing ClientFinished
    • state = handshake_complete
  • send HANDSHAKE_DONE
    • state = handshake_confirmed

This has a few ugly things, of course:

  • The server validated and false-start events won't always be in the same order, depending on how long the handshake takes (e.g., with a large CERT, handshake might need several flights to complete, causing validated to come before false-start)
  • At the server side, handshake_complete and handshake_confirmed will always fire directly after one another (this is also true in the TLS draft's definitions however)
  • The false_start and validated aren't really needed at the client side (but do provide a nice parallel in logic to the server's progression imo)

Finally, for users not wishing to log all states this fine-grainedly, this condenses nicely to:

  1. attempted
  2. handshake_started
  3. handshake_confirmed
  4. closed

What do people think? Did I miss something?

from qlog.

huitema avatar huitema commented on July 23, 2024

From an implementation point of view, you probably want to separate "false_start" (on the server side) and "almost_ready" on the client side. The server side is more constrained, because it cannot receive 1-RTT packets. Also, when the client enters the "almost_ready" start, the TLS stack on the client has seen the server certificate and validated the "server finished" message. In contrast, the server will only know that there is no MITM or 0-RTT replay attack when its TLS stack validates and receives the "client finished" message, so in theory the server has to apply some caution before sending data.

from qlog.

martinthomson avatar martinthomson commented on July 23, 2024

Our implementation has "waiting for initial", which roughly corresponds to your "attempted", but also encapsulates the server side prior to receiving the initial. We don't keep that state very long server side; your description implies that it persists, but I don't think that is helpful.

Then we have "handshaking", which we keep orthogonal to the states where various keys are available. Keeping key availability separate is better than trying to enumerate the various availability options. false_start is a problem, I think in that it implies something else; an "early write" state is fine. In this state you can sometimes send, based on what keys are available, but you can never receive. Also, trying to conflate address validation state with handshake state will likely complicate things more than necessary.

Then we have "complete" where TLS is done. And "confirmed" where you have thrown out handshaking state.

We use the states from the spec for closing, which is not a single state, but three.

So aside from your closed state, the summary form is probably best. Logging both address validation and key availability independently would be good.

from qlog.

rmarx avatar rmarx commented on July 23, 2024

Thank you both @huitema and @martinthomson for your input!

Changes I've made for -02:

  1. renamed "false_start" to "early_write". I appreciate that this is potentially not entirely the correct term, but I was thinking mainly of non-protocol experts trying to following along and "early write" seemed easiest to grok. Open to other suggestions for -03.
  2. changed to having 3 closing states: closing, draining, closed
  3. added explicit guidance to support the condensed/simpler set

@martinthomson: we keep support for logging the key updates separately as well. WRT address validation, this will be re-evaluated once I add proper support for connection migration/path management. For now, I'm keeping it in there because @huitema suggested it above and it's the most logical place to put it at this point.

@martinthomson: I do not have anything before "attempted" because this event is connection scoped and IIUC servers don't have a connection concept before the first initial from the client arrives (similar for the client). In qlog, this is more generally reflected by other events like server_listening atm.

from qlog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.