private-octopus / picoquic

Minimal implementation of the QUIC protocol
MIT License
523 stars 153 forks source link

Confusion around picoquic_connection_id_t #1671

Open notBroman opened 2 months ago

notBroman commented 2 months ago

I am currently a bit confused by the picoquic_connection_id_t struct. I see that it is an array plus it's length. From some of the tests that have already been written I could deduce that the it is used to define the connection (latency, connection speeds, jitter, loss_mask), but the documentation never (in picoquic.h) does not really say how it is supposed to be used or what it is supposed to store.

My goal is to understand how the simulation suite works better and write some documentation on how to use it, if that is desired.

huitema commented 2 months ago

The use of connection ID is documented in Section 5 of RFC 9000. There are further specification in progress with the QUIC Multipath Draft. Connection IDs are defined as byte strings of length 0 to 255 in the QUIC Invariants per RFC 8999, but different versions of QUIC may restrict the length -- for version 1 as defined in RFC 9000, the length is at most 20 bytes.

The main purpose of connection ID is to identify the connection to which an incoming packet belongs. This leads to a distinction between "local" connection IDs, created by the local endpoint and communicated to the peer during the connection setup or after that through NEW_CONNECTION_ID frames, and the "remote" connection IDs, provided by the peer and used when sending packets to that peer.

In picoquic, a particular UDP port, or UDP socket, is tied to a "QUIC context" that may contain multiple connection. The QUIC context contains a hash table, keying by the Connection ID and pointing to the connection context. There may be multiple connection IDs pointing to the same connection context.

In server farm configurations, the connection IDs may be used by the load balancers to direct an incoming packet to the server that manage the specific connection, as specified for example in the QUIC-LB draft. By default, picoquic creates connection IDs as strings of random bytes. There is an API to override that default and use an application specified generation method, for example to match the encoding conventions of a specific load balancer.

The simulation infrastructure used in picoquic tests does not route packets based on connection IDs -- it merely simulate routing by IP address and UDP port. We could use a simulated load balancer to the simulation infrastructure, but that would only be useful to test server farm configurations. I have not heard anybody asking for that yet.

hfstco commented 2 months ago

Hi Roman,

I think you talk about the initialisation of the initial connection id in some of the test cases, like for example here.

In some test cases, an initial cid is only defined to identify the test case. (in logs, qlog files, etc.) I haven't found a test case where we get information out of the connection id. We just set them in the beginning.

In several tests the first two - sometimes even more - bytes represent the test and the following bytes represent the options used. But I think there is no rule which byte of the array is supposed to store specific information. For example, the fourth byte stores the number of link states, in another test it is used to store a test id, but it is also used to store the upload mbps of a link.

In my opinion, you can simply choose your own pattern for your tests to find your test results easily afterward. Try not to overlap with other test cases maybe.

Best regards, Matthias

huitema commented 2 months ago

@hfstco Yes, in the test suite I often encode the identifier of the test in the "initial connection ID". Don't do that in production, the Initial connection ID is supposed to be random! The tests execution never parses that value, but picoquic constructs the file name of the log files using the initial connection ID. So that's a way to associate logs and test cases.

hfstco commented 2 months ago

@huitema You have already explained the use in production environment well. Of course, my post only refers to the test environment! I forgot to make that clear.

notBroman commented 2 months ago

Thank you both for clarifying, on a Sunday no less. I will read the highlighted draft/RFC sections to make sure I understand correctly.

notBroman commented 2 months ago

A follow up question around the simulation suite. What is the difference in use of tls_api_connection_loop and tls_api_data_sending_loop_ex (called by tls_api_data_sending_loop). To me they seem to do similar things. Although tls_api_connection_loop does not take into account the link state, both end up calling tls_api_one_sim_round. Meaning they both perform a simulation?

Could you explain what separates their functionality wise?

hfstco commented 2 months ago

tls_api_connection_loop establishes the connection between server and client of the picoquic_test_tls_api_ctx_t.

tls_api_data_sending_loop sends/receives the data provided with stream0_target or/and a test_api_stream_desc_t scenario.

tls_api_data_sending_loop_ex is just the extended version of tls_api_data_sending_loop. Most of the time the base version only calls the extended version.

tls_api_one_sim_round performs a simulation round. All functions above use it, because they need to exchange packets between server and client. All of them consist of several tls_api_one_sim_rounds.

afaik

tls_api_connection_loop: On this line we run simulation rounds until the connection is established.

tls_api_data_sending_loop_ex: And here line we are looping until the tests are finished.

notBroman commented 2 months ago

Thank you. Do I understand this correctly when I say that tls_api_connection_loop achieves the same as the 1-RTT/0-RTT handshake of a normal QUIC connection would?

While tls_api_data_sending_loop_ex is responsible for every packet after that including closing the connection?

hfstco commented 2 months ago

Thank you. Do I understand this correctly when I say that tls_api_connection_loop achieves the same as the 1-RTT/0-RTT handshake of a normal QUIC connection would?

Yes, the simulation framework (tls_api_) simulates a "normal" QUIC connection - including 1-RTT/0-RTT handshakes.

While tls_api_data_sending_loop_ex is responsible for every packet after that including closing the connection?

I think tls_api_attempt_to_close - which ends in tls_api_close_with_losses - is responsible for closing the connection. It is called when you verify the scenario (tls_api_one_scenario_body_verify).

Here is a quick overview of a connection simulated with tls_api_ (sending 5KB of data from client -> server, stream0_target, boundaries partially overlap!):

tls_api_functions

Maybe take a look at the tls_api_one_ functions too. They also based on the functions we are discussing here.

huitema commented 2 months ago

Two points:

1) The xxxx_ex() APIs are generally extensions of the corresponding xxxx() APIs. That's a way to not have to modify old code when extending the API to support a new functionality.

2) The "sending loop" function will take the connection in its current state, and try to run the simulation until all the data is sent. The diagram above is generally correct, but there are probably examples of calling "sending loop" without calling "connection loop" first.