private-octopus / picoquic

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

Make picoquic_sample work properly under long delay senarios #1594

Closed KathyNJU closed 3 months ago

KathyNJU commented 7 months ago

The purpose of my experiment is to achieve normal file transfer function under long delay senarios (for example, RTT of 2min). As the default handshake_completion_timer was set to 30s and the default maximum_idle_timer was set to 120s, the first trial did not work. According to your guidance, I have made the following modifications. Modification 1: Set the idle timeout parameter for the context (both on server side and client side)

...
// simply call the API (Modification 1)
// set_rtt is the delay introduced by netem, in our experiment is 120s
picoquic_set_default_idle_timeout(quic, (3*set_rtt));

picoquic_set_default_congestion_algorithm(quic, picoquic_bbr_algorithm);
...

Modification 2: set PICOQUIC_MICROSEC_HANDSHAKE_MAX (defined in picoquic_internal.h) to 360000000 (namely 3*set_rtt)

So far, the modifications to maximum_idle_timer and handshake_completion_timer should be completed. However, it still did not work. I also noticed the remarks in Sample: Server or client close the connection if it remains inactive for more than 10 seconds. So I guess it was because the maximum time remained inactive was too short that the handshake failed. As a result, I have made the following modification: Modification 3: set PICOQUIC_MICROSEC_STATELESS_RESET_INTERVAL_DEFAULT (defined in picoquic_internal.h) to 3600000ull.

After making the above modifications, the handshake was not implemented successfully. I was a little lost in the face of the problem. Last time, you mentioned some parameters to overcome the flow control limits. Are these parameters relevant to the handshake?

huitema commented 7 months ago

@KathyNJU There are several tests of long delay behavior in the test suite. You may want to look at the tests in .\picoquictest\delay_tolerant.c, with tests for connections with long delays, e.g., 2 minutes or 20 minutes.

You do not need to recompile the code and change the value of PICOQUIC_MICROSEC_HANDSHAKE_MAX. This parameter is only use to set a default value to the "idle timeout", before it is negotiated during the handshake. Instead, you need to set the value of the idle timeout explicitly, using the API:

void picoquic_set_default_idle_timeout(picoquic_quic_t* quic, uint64_t idle_timeout);

You need to do that in both the client and the server, because the handshake will negotiate the smaller value of the client and the server proposal.

For the client, in picoquic_sample_client(), I would insert a line near the call to picoquic_set_default_congestion_algorithm. For the server, do the same in picoquic_sample_server().

KathyNJU commented 6 months ago

According to your suggestion, I referred to delay_tolerant.c. I set up a basic topology through mininet and ran server-side and client-side commands on two nodes. Therefore, I haven't found a good way to directly call the test in delay_tolerant.c. I mainly set some parameters according to delay-tolerant.c.

Based on dtn_basic_test(), I changed the parameters as follows:

picoquic_set_default_idle_timeout(quic, (5*2*1000*60));
picoquic_set_enable_time_stamp(quic, 3);
uint64_t initial_flow_control_credit = 20000000;
 // update the flow control limits
 picoquic_set_initial_max_data(quic, initial_flow_control_credit);
 picoquic_set_initial_max_stream_data_bidi_local(quic, initial_flow_control_credit);
 picoquic_set_initial_max_stream_data_bidi_remote(quic, initial_flow_control_credit);
 picoquic_set_initial_max_stream_data_uni(quic, initial_flow_control_credit);

I repeated the experiment, and things got better. Initial retransmit timer are doubling as expected, and Qlogs are being generated on the server side. However, the experiment was not a complete success. According to qvis, I found that the server could not successfully send ACK and handshake packets after receiving the initial package from the client. And there is an Info on the server side: "Session ticket could not be decrypted".

I think it could be for the following reasons:

  1. When the server receives the Initial packet, the corresponding connection has been closed (because it is mentioned in the remarks that if the client or server is inactive for 10s, the connection will be closed).
  2. The key used in the connection has been updated and the server does not have the correct key to decrypt (inferred from Info)
  3. There were other parameters that I didn't notice that led to the failure of the experiment.

I'd like to know your opinion on this issue.

huitema commented 6 months ago

@KathyNJU can you post the qlog that you collected on the server side? Also, did you apply the changes above to both the client and the server side?

KathyNJU commented 6 months ago

I apply the changes to both the client and the server side, and it did't work. The Qlogs are as follows: Qlog.zip

huitema commented 6 months ago

The message "Session ticket could not be decrypted" happens because the client already had received a Session Resume Ticket from this server, probably during a previous attempt using a small RTT. So the client is trying to use 0RTT. The server does not have the decryption key for that ticket, so protests. In theory, the only effect would be to refuse the incoming 0-RTT packets. The handshake would continue, as verified in the unit test zero_rtt_spurious.

The qlog shows that no packet from the server reaches the client. This cannot be an issue with the QUIC code, since the logs also show that the server is attempting to send several packets to the client, and none arrives... The packets seem correct, the DCID match what the client expects. I think that these packets are dropped by an intermediate system. Maybe there is something wrong in the addressing and routing. Or maybe there is an issue in the link between the application and the simulator.

KathyNJU commented 6 months ago

I tried to change the RTT to 2 seconds just now, and the file transfer was successful. Therefore, I think it was some parameters that were not set properly, which led to the failure of the experiment. Later I will try more RTT values to see if the experiment can be successful.

I checked the latest picoquic-test-idle-timeout version. All the modifications in my test:

  1. both in client and server side:
    
    ...
    picoquic_set_default_idle_timeout(quic, (5*2*60000));
    picoquic_set_default_handshake_timeout(quic, (5*2*60000000));

picoquic_set_cookie_mode(quic, 2); picoquic_set_default_congestion_algorithm(quic, picoquic_bbr_algorithm); ...

2. in quicctx.c, I changed the default value:
```c
void picoquic_init_transport_parameters(picoquic_tp_t* tp, int client_mode)
{
    memset(tp, 0, sizeof(picoquic_tp_t));
    // tp->initial_max_stream_data_bidi_local = 0x200000;
    // tp->initial_max_stream_data_bidi_remote = 65635;
    // tp->initial_max_stream_data_uni = 65535;
    // tp->initial_max_data = 0x100000;
    tp->initial_max_stream_data_bidi_local = 20000000;
    tp->initial_max_stream_data_bidi_remote = 20000000;
    tp->initial_max_stream_data_uni = 20000000;
    tp->initial_max_data = 20000000;
    // below are unchanged
    tp->initial_max_stream_id_bidir = 512;
    tp->initial_max_stream_id_unidir = 512;
    tp->max_idle_timeout = PICOQUIC_MICROSEC_HANDSHAKE_MAX/1000;
    tp->max_packet_size = PICOQUIC_PRACTICAL_MAX_MTU;
    tp->max_datagram_frame_size = 0;
    tp->ack_delay_exponent = 3;
    tp->active_connection_id_limit = PICOQUIC_NB_PATH_TARGET;
    tp->max_ack_delay = PICOQUIC_ACK_DELAY_MAX;
    tp->enable_loss_bit = 2;
    tp->min_ack_delay = PICOQUIC_ACK_DELAY_MIN;
    tp->enable_time_stamp = 0;
    tp->enable_bdp_frame = 0;
}

I reviewed the experiment. When RTT is 2 minutes, the server cannot generate Qlog during the first few runs. (The generated Qlog is as follws) : client_qlog.zip

At first I just thought there was some other error, so I shut down the server and tried a few more times immediatelly. That could be the reason why "Session tickets could not be decrypted"? If so, what should be done?

huitema commented 6 months ago

The error "Session tickets could not be decrypted" happens because the client remembers tickets from a previous server run. These tickets are encrypted with a key. By default, the key is picked at random each time the server runs. To eliminate the error, you should either:

huitema commented 6 months ago

The new client qlog confirms that the client is not receiving any packet sent by the server. This was already visible in the previous logs. At this point we now that:

Since the packets are sent by the server but not received by the client, something between the server and the client is dropping them. I suspect this is some kind of firewalls. Many UDP firewalls open a "pin hole" when the client sends a packet, and leave the pinhole open for some time to receive the response. After a delay, they close it. If the RTT is long enough, packets arrive after that delay.

To verify that, you could record a PCAP of what the client sends and receive. You will probably see server packets arrive to the client's device, but you will not see the same packets in the qlog. You can test were the limit is by trying delays of 5, 10, 30, 60 seconds, etc.

If this really is a firewall issue, you should be able to work around it by explicitly specifying the UDP port for the client, and changing the call to picoquic_packet_loop from:

    /* Wait for packets */
    ret = picoquic_packet_loop(quic, 0, server_address.ss_family, 0, 0, 0, sample_client_loop_cb, &client_ctx);

to:

    /* Wait for packets */
    ret = picoquic_packet_loop(quic, client_port, server_address.ss_family, 0, 0, 0, sample_client_loop_cb, &client_ctx);

I had not heard of this problem before. You have just found something "interesting", and worth publishing in, for example, a short blog, or a communication to the QUIC or "deepspace" mailing lists. Or just ask me to do that, but I want to make sure you get credits for finding it.

KathyNJU commented 6 months ago

I changed the call to picoquic_packet_loop, but it seems that there still exists something wrong. I plan to further find the problem by printing some status information (such as UDP port) when processing the Initial packet. If I want to add this print code, where should I add it to?

I plan to try different RTTs as you said, and analyze the results in combination with Qlog and Wireshark. I have conducted some experiments and may have found some problems. When all is done, I will share the results with you and have a further discussion!

huitema commented 6 months ago

For the print code, you could print the "addr_from" and "addr_to" just before the call to "picoquic_incoming_packet_ex" in "sockloop.c", and the "peer_addr" and "local_addr" just after the call to "picoquic_prepare_next_packet_ex".

KathyNJU commented 6 months ago

OK, I'll add print code for debugging later.

Recently, I conducted simulations with RTTs of 5s, 10s, 15s, 20s, and 30s respectively. The specific results are presented in the PDF file in zip. Relevant images, QLOG, and Wireshark packet capture data are all saved in their respective folders. Simulation.zip

If you have the time, could you take a look? I'd like to verify if some of my interpretations might be incorrect.

Thanks very much for your help!

huitema commented 6 months ago

I am looking at your report. First, a quick note on the use of CID during handshake, as specified in RFC 9000:

huitema commented 6 months ago

On Initial packets marked "lost": for security reason, the client ignores any further initial packet from the server as soon as it has received the first "initial" packet containing the "server hello". After that, the client only processes handshake packets, and later 1RTT packets.

Because of the long delay, the server does not know that the client has received the message, so it repeats them a couple time. This will stop when the server receives an ACK from the client, and a Handshake message. After receiving the handshake message the server deletes the Initial key, consider all Initial packets as acknowledged, and clears the Initial repeat queue.

huitema commented 6 months ago

The PING packet that you noticed is for detecting the PATH MTU. RTT is computed from the delays between packets and the ACK that acknowledges them, plus timestamps.

huitema commented 6 months ago

The error in the initial UDP port is bizarre. I do not see that anywhere else. I suspect it is some kind of configuration issue. How did you specify the client and server ports? Can something there be crosswired?

huitema commented 6 months ago

For the provision of CID, see RFC 9000 section 5. See section 7.1 for examples of handshake flows.

huitema commented 6 months ago

For the ACK frequency, the spec is in the ACK Frequency draft

huitema commented 6 months ago

I suspect that there is some system dropping packets in path from server to client. You see somewhat random results based on the value of the delay and time out. I suppose this is because these change the timing of packets. You may want to plot the interval between the last packet sent by the client and the arrival time (or planned arrival time) of the server packet. I suspect that packets are accepted when this delay is short, rejected if the delay exceeds a theshold.

You probably have a firewall or equivalent somewhere in the setup. You want to disable it, or at a minimum program it to let packets through for the IP addresses and ports of client and server.

KathyNJU commented 6 months ago

In the experiment of 2s scenario, I did not specifically set the client port, and the server port was directly set through the call, as follows:

// server side
./picoquic_sample server 4433 ./ca-cert.pem ./server-key.pem ./sample/server_files

// client side
./picoquic_sample client fc01::1 4433 /tmp smallfile.txt

I just repeated the experiment, and maybe that strange phenomenon was a fluke. I am very sorry that the test was not repeated in time for occasional circumstances.

huitema commented 6 months ago

Many operating systems treat "server ports" and "client ports" differently. Server ports are generally open, because the servers can receive packets are any time. CLient ports are often protected, with the firewall open by an outgoing packet and then close some time after that packet is sent.

That's why I asked you to set an explicit port for the client, maybe 4434 or some such, just by hard coding it in your prototype. The firewalls should treat that as a server port, and let the traffic go through.

KathyNJU commented 6 months ago

OK, I see what you mean. I will later set an explicit port for the client and repeat the experiment to see if there is any difference.

KathyNJU commented 6 months ago

Today I discusse the simulation results with my tutor. For cases where the Qlog displays packet generation on the client side but Wireshark fails to capture these packets on the client port, there's a suspicion that there might be an issue with the UDP socket calls at the UDP layer. If I want to print whether picoquic's packets were sent through the UDP socket, and whether the operation of the UDP socket was executed properly, how should I add the print code?

In your blog, you mentioned implementing long-delay transmission. I'm curious about your network topology. Did you use physical servers or virtual machines for the server and client? If virtual, could you provide details about the VM configurations? We suspect environmental setup issues and would like various configuration information. We're planning to directly connect two virtual machines instead of using Mininet to set up the topology for file transfer.

huitema commented 6 months ago

The UDP sending code is in sockloop.c, lines 349-427. The return code of the UDP sendmsg call is "sock_ret", line 376.

You can also enable debug logging by calling: debug_printf_push_stream(stderr); in your main program (client and/or server).

huitema commented 6 months ago

The tests use the test simulator built into picoquic -- see "delay_tolerant_test.c". The simulator abstracts away the socket calls and passes them to simulated links. The simulator works in virtual time, which means for example that a connection with a 20 minutes RTT can complete in a a few milliseconds (for a connection tests) or a few seconds (for a data transmission test). This is used widely through the test suite.

The downside is that the actual socket calls are not tested in the automated tests. They are tested manually a lot, because the same "socket loop" is used in applications like "picoquicdemo".

There is a known issue with the mininet drivers. They are somewhat non standard, because they do not support UDP GSO. However, there is a work around in the code. These drivers are used for example in the interop tests, which execute correctly.

KathyNJU commented 6 months ago

Can I call delay_tolerant_test.c directly from picoquic_sample? Does that mean I probably don't need mininet?

I want to achieve successful transmission under long delay senarios at first.

huitema commented 6 months ago

You are blocked by an interface issue in your workstation. You have to find out what it is. I don't know what system you use. Windows? Mac? Linux? Linux systems come with IPTable. Do you know what configuration you are using?

huitema commented 6 months ago

Yes, you would not need Mininet if you just extended the code in the dtn tests. There are four tests: "dtn_basic" simulates establishment of a connection with 2 minutes seconds RTT; "dtn_data" simulates transfer of 100MB with 2 minutes RTT; "dtn_twenty" does the same with 20 minutes RTT; "dtn silence" simulates doing three transactions with a 2 minutes RTT.

There are two parameters for each test: the "test spec" that sets latency, data rate, etc.; and the 'stream description" which specifies how many streams to send, which comes after which, and how much data in each direction.

huitema commented 6 months ago

Also, no, you cannot call the test code from picoquic sample. I mean, you could, but it would not use any of the code in the sample. The code simulates client and server, and is meant to be call from a test program. You could write your own test program and call the test code. You could probably write something interactive, letting the user program latency, bandwidth, etc.

KathyNJU commented 6 months ago

I use Linux. I use Vmware to create an ubuntu virtual machine, ubuntu version 18.04, Linux version 5.4.0.

If I want to call delay_tolerant_test.c directly to test, should I use picoquicdemo? Since the scenario we are currently applying does not consider HTTP and may only use picoquic for file transfer, I initially chose picoquic_sample instead of picoquicdemo.

At present, the solution I am still trying is to create two virtual machines and connect them directly through vmware for communication. The delay and bandwidth characteristics will be set at the network port of the virtual machine, so mininet is not needed. Do you think this method is feasible?

huitema commented 6 months ago

The tests run their own very simple application protocol. They are not meant to be used in any application, beside just tests. The simplest way to call the test is to run: picoquic_ct <name-of-test>.

I am not familar with vmware. It seems that your two VM setup could work.

KathyNJU commented 6 months ago

Yesterday, I attempted to install two new virtual machines on VMware. Their IPv6 addresses were within the same subnet, enabling normal communication. I set delays on the interfaces of both virtual machines to avoid using Mininet. Currently, with a round-trip delay of 1 minute, file transmission has been achieved at the very least! The client successfully received the requested file! I couldn't wait to share this good news with you.

It seems that indeed part of the issue might have stemmed from the previous use of Mininet. However, upon examining the packet captures from Qlog and Wireshark, I noticed some peculiar occurrences persisting. Firstly, the initial few Initial packets from the client still show transmission failures, and Wireshark didn't capture these packets. Secondly, the client's request for connection closure still fails to transmit. (as indicated in Qlog). 1min_Qlog.zip

I will continue investigating to pinpoint where the issue lies.

huitema commented 6 months ago

Congratulations! I will study the qlog file tomorrow.

huitema commented 6 months ago

You have an interesting configuration...

First, I can see that the first packet that arrives at the server is indeed:

[0, "transport", "datagram_received", { "byte_length": 1232, "addr_from" : { "ip_v6": "fd15:4ba5:5a2b:1008:d573:a7dc:e9c5:9014", "port_v6" :4434}, "addr_to" : { "ip_v6": "fd15:4ba5:5a2b:1008:8a80:dccf:8350:bcc3", "port_v6" :20753}}],
[0, "transport", "packet_received", { "packet_type": "initial", "header": { "packet_size": 1232, "packet_number": 99361, "version": "00000001", "payload_length": 1186, "scid": "285128707d81e89e", "dcid": "c19d96de9d706a41" }, "frames": [{ 
    "frame_type": "ping"}, { 
    "frame_type": "crypto", "offset": 0, "length": 287}, { 
    "frame_type": "padding"}]}],

This is not normal. If you look at the client's log, you will see that there is no difference between packet #99361 and the preceding packets. All these packets leave the client with a header set to:

[2287, "transport", "datagram_sent", { "byte_length": 1232, "addr_to" : { "ip_v6": "fd15:4ba5:5a2b:1008:8a80:dccf:8350:bcc3", "port_v6" :4433}, "addr_from" : { "ip_v6": "0:0:0:0:0:0:0:0", "port_v6" :0}}]

The source IP address is unspecified, which means that it should set by the system during the system call -- which is done for packet #99361, which the server sees coming from address ending in 9014, port 20753. I wonder what is happening there. Could those previous packets have been sent from a different address or port? Do you see them in the PCAP?

Second, the last packet received by the server from the client is:

[91035274, "transport", "datagram_received", { "byte_length": 55}],
[91035274, "transport", "packet_dropped", {
    "packet_type" : "handshake",
    "packet_size" : 55,
    "trigger": "key_unavailable",
    "raw": "680bee4176a12a48a60203014534000102000000000000000000000000000000"}],

This is the handshake packet sent by the client at:

[155302706, "transport", "packet_sent", { "packet_type": "handshake", "header": { "packet_size": 39, "packet_number": 2, "payload_length": 10, "scid": "285128707d81e89e", "dcid": "0bee4176a12a48a6" }, "frames": [{ 
    "frame_type": "ack", "ack_delay": 0, "acked_ranges": [[0, 3]], "ect0": 4, "ect1": 0, "ce": 0}, { 
    "frame_type": "padding"}]}],

The drop is expected, because the server has sent "handshake done" and is now receiving 1RTT packets.

The next packets from the client are not delivered to the server.

Picoquic seems to behave correctly, but there is still something weird happening at the socket layer.

KathyNJU commented 6 months ago

The first phenomenon you mentioned is indeed peculiar. I repeated the experiment, and the same situation occurred. Moreover, Wireshark packet captures indicate that these packets were not captured on the client-side port, meaning these data packets simply weren't successfully transmitted. I believe your hypothesis is accurate. I will attempt to investigate the interaction between Picoquic and the socket layer to identify any potential issues.

KathyNJU commented 6 months ago

I've been busy with exams and final projects recently, so I haven't shared much progress. I apologize that I haven't found a perfect solution to the problem I mentioned earlier, but I will continue to make some attempts. Additionally, I've tried simulations of reconnecting after a long period of silence. In one scenario, the keys from the previous connection were saved, theoretically enabling 0RTT transmission; in another scenario, the keys were not saved, which should theoretically result in 1RTT transmission. However, from observations using Qlog and Wireshark, although the first scenario successfully transmitted the file, it seemed to achieve 1RTT transmission instead of 0RTT. The following diagram shows a transmission that, if correct, would successfully implement 0RTT transmission. But in reality, these packets were not successfully transmitted, and even Wireshark did not capture them. This seems to be the same issue as before. If the transmission shown in the diagram is successful, it means 0RTT transmission is achieved, right? 0RTT失败 0RTT_1RTT.zip

I also have a few questions I'd like to discuss with you:

  1. Has the current picoquic_sample successfully implemented multi-stream multiplexing for multiple file transfers?
  2. Does the current picoquic_sample support transmission during address migration?
  3. If I want to build upon the existing picoquic_sample to implement the above features and fully demonstrate the characteristics of the QUIC protocol in satellite scenarios, do I need to consider other features of QUIC? And will this task be quite labor-intensive? (The timeline for this task might be a bit tight).
huitema commented 6 months ago

The sample was designed to be as simple as possible, and thus has limited features, but the scenarios that you describe ought to be supported. The sample client is programmed to open a picoquic stream for each of the files requested in the command line. These streams are opened when the program starts, and then served as soon as transmission capacity permits.

The sample uses the default stream scheduling programmed in picoquic, which is first-in, first-out. By default, the only amount of parallelism is for loss recovery. If packets are lost when sending a stream, the next stream may well begin immediately, while recovery of the packet losses happens. The scheduling of streams could be changed by changing stream priorities, as explained in lines 1123-1175 of picoquic.h.

The sample code does not currently support explicit address migration. It will perform NAT rebinding if the client address changes. This will take 1 or 2 RTT, during which packets sent by the server may very well be lost.

There are many features of QUIC that could be used, such as address migration or maybe multipath, but I would not try that if you are short on time.

If the socket API is fixed, you will need to experiment with parameters such as the idle timeout, the maximum data allowed on the streams, and the maximum data allowed by default on bidirectional streams. These last 2 parameters affect flow control. If not set to a sufficient value, the transmission will be slowed.

After flow control, yes, you should also experiment with 0RTT. As far as I can tell, the 0RTT qlog that you sent shows a successful 0RTT connection. The first data packet carried both the Initial request and a 0RTT packet. Per QUIC specification, they are "coalesced" into a single network-level packet. The server accepted the 0RTT message from the client, and started sending "stream 0" immediately. The file was short, so the transmission completed quickly.

KathyNJU commented 6 months ago

For the 0RTT experiment, it seems that the client did initially implement the 0RTT feature and the server tried to make the correct response. However, for some reasons, the actual transmission achieved in the Qlog was still 1RTT, right? I noticed in the compressed file that the actual responses made by the server for 1RTT and 0RTT are quite similar. Does this mean my actual experiment failed to achieve 0RTT?

A file is transmitted through one stream, and different files can be transmitted in parallel through multiple streams, as there are no dependencies between multiple files. In picoquic_sample, up to eight streams can be parallelized, meaning up to eight files can be transmitted simultaneously. Based on what you said, if a packet loss occurs in one stream, the next stream will be immediately initiated. Does this mean that it's not possible to transmit eight files in parallel in case of packet loss?

Regarding the address migration issue, if I want to test performance in an actual satellite scenario, my current idea is to use the actual satellite as the server and the ground user as the client, to achieve file transmission of satellite data. But in this scenario, it would be the server's address that changes. Does this also involve NAT rebinding? If the satellite is used as the server side, and the satellite's address is in a state of change, can a connection still be established and file transfer be achieved in this case?Are there any potential issues with this experimental assumption?

huitema commented 6 months ago

Oh, I see. Yes, the TLS ticket was accepted by the QUIC layer, but the 0-RTT key was not installed by the TLS component. This is weird. It means some check inside picotls failed. The client then notices that TLS did not negotiate 0RTT, and considers that all 0RTT packets pending acknowledgement have been lost. This would happen if the ticket lifetime was expired.

The ticket lifetime is hard coded in picoquic to 100,000 seconds, a bit more than a day. Did more than 100,000 seconds pass between the beginning of the first connection and the resumption attempt?

The constant is set in the function picoquic_master_tlscontext in tls_api.c.

ctx->ticket_lifetime = 100000; /* 100,000 seconds, a bit more than one day */

You could probably try changing that to 1 million seconds -- but not much more, as picotls uses a 32 bit integer to store the value in milliseconds. By the way, this kind of hidded constants are very much what the long transmission delay tests are set to discover :-).

It is possible to transmit all streams in parallel in a "round robin" manner. You need to set the stream priority to an even value, such as 8, instead of the default odd value, 9. Streams at the same priority level are sent FIFO if the priority level is odd, round-robin if the priority is even. See lines 1123-1175 of picoquic.h.

QUIC does not support changes in server address, sorry. Of course, this could be changed with an extension, but that will take time. In the short term, you can either change your setup to have the satellite be the QUIC client, or ask the network layer to establish some kind of tunnel to ensure that the satellite can be reached at a fixed address.

KathyNJU commented 6 months ago

For the 0RTT experiment, the interval for re-establishing the connection did not exceed 100,000s. I suspect the same issue occurred as before where the initial packets were not truly sent out. I will address this in subsequent attempts.

Due to the requirements of the task, I am currently adjusting parameters for the Earth-Moon scenario. I have modified the PICOQUIC_INITIAL_RTT and PICOQUIC_INITIAL_RETRANSMIT_TIMER values, both set to 4s, in hopes of optimizing for the specific Earth-Moon context. I have also set idle_timeout and handshake_timeout using the API. Currently, I am somewhat confused about the parameters for flow control.

As we know, in actual satellite links, there are uplink and downlink channels, which are typically asymmetric. We denote the bandwidth of the uplink as client_flow_control_credit and the bandwidth of the downlink as server_flow_control_credit. Regarding flow control, I have made the following settings:

`// sample_client: set flow control credit picoquic_set_initial_max_data(quic, server_flow_control_credit); picoquic_set_initial_max_stream_data_bidi_local(quic, client_flow_control_credit); picoquic_set_initial_max_stream_data_bidi_remote(quic, server_flow_control_credit); picoquic_set_initial_max_stream_data_uni(quic, client_flow_control_credit);

// sample_server: set flow control credit picoquic_set_initial_max_data(quic, client_flow_control_credit); picoquic_set_initial_max_stream_data_bidi_local(quic, server_flow_control_credit); picoquic_set_initial_max_stream_data_bidi_remote(quic, client_flow_control_credit); picoquic_set_initial_max_stream_data_uni(quic, server_flow_control_credit); `

Due to the asymmetric bandwidth of the uplink and downlink, I thought of setting the parameters for these four flow controls separately. Do you think my settings are reasonable?

huitema commented 6 months ago

The QUIC names for the variables may be a bit confusing. Here is what it means:

Note that the local flow control parameters do not limit the amount of data sent by the local host to the peer -- this will be limited by the parameters received from the peer. That means your setup should really be:

`// sample_client: set flow control credit
picoquic_set_initial_max_data(quic, server_flow_control_credit);
picoquic_set_initial_max_stream_data_bidi_local(quic, server_flow_control_credit);
picoquic_set_initial_max_stream_data_bidi_remote(quic, server_flow_control_credit);
picoquic_set_initial_max_stream_data_uni(quic, server_flow_control_credit);

// sample_server: set flow control credit
picoquic_set_initial_max_data(quic, client_flow_control_credit);
picoquic_set_initial_max_stream_data_bidi_local(quic, client_flow_control_credit);
picoquic_set_initial_max_stream_data_bidi_remote(quic, client_flow_control_credit);
picoquic_set_initial_max_stream_data_uni(quic, client_flow_control_credit);
KathyNJU commented 6 months ago

Alright, I understand, thank you very much!

Regarding the issue of the initial packet not being sent out, I have a thought. Since I've set the delay to 30 seconds, it needs to negotiate the MAC address before it can send out packets. I found that IPv6 uses NDP, and there seems to be an issue with the probe timer in this process. Because the delay exceeds the timer's threshold, it leads to the assumption that the neighbor is unreachable. Therefore, initially, NDP is in a failed state, and since the link layer isn't established, the packet isn't sent and isn't captured by Wireshark either. It's not until the Picoquic client starts requesting that it further probes for reachability, and upon confirming communication is possible, it successfully sends the packet. But this idea is currently just a speculation, and I am conducting more experiments to verify it. I would like to hear your thoughts on this.

huitema commented 6 months ago

NDP? This is designed for local networks, like Ethernet or Wi-Fi. I would not expect to use that on a long distance link, let alone a satellite link. Can you try to model your test network as a set of links with fixed addresses, plus a set of routes?

KathyNJU commented 6 months ago

OK, in our actual satellite scenario, the IP addresses are indeed fixed. I will modify my network model. I have just discovered through experimentation that the issue of the initial packets not being sent out was indeed due to MAC address negotiation. I will modify the network model and then take another look at the specific situation.

Additionally, after resolving the issue with the initial packets, I repeated the 0RTT experiment and encountered some problems. The 0RTT packets do not seem to be correctly received, and what is being implemented is still 1RTT transmission. 1704603109590 0RTT_repeat.zip

huitema commented 6 months ago

The 0RTT issue appears similar to your previous traces. The way the code works is:

In the next session, the client retrieves the encrypted ticket from the store, and then:

The traces show that the issue happens in the last steps of this process:

The means that TLS could not verify some of the ticket parameters. The code doing that is in static int try_psk_handshake() in picotls.c in the picotls sources. It does a series of tests, and obviously one of them is failing:

I don't know which of these tests is failing, but one of them clearly is. Quick checks:

KathyNJU commented 6 months ago

Based on your suggestion, I conducted some tests today:

  1. The QUIC context of the client indeed has the "ticket store file name" set.
  2. The server name in the ticket matches the server name SNI parameter requested by the client. image image
  3. The "alpn" parameter in the ticket matches the "alpn" parameter asked by the client. image image
  4. I also tried to match the server name with the alpn, but encountered the same issue.
  5. I did not restart the server between the tests.

An unusual phenomenon has emerged in our simulation: (Our server and client are simulated using two virtual machines under the same VMware.) The picoquic server is continuously running, and the picoquic client has previously successfully communicated with the server. The current phenomenon is: when there is a 40-minute interval between the client's current and previous requests, 0RTT transmission is successfully achieved. However, when the interval is 30 minutes or less, the server displays key_unavailable, indicating a failure in receiving the 0RTT key, thus resulting in 1RTT communication. I suspect there may be some issues with timer settings involved?

huitema commented 6 months ago

I am looking at the code for possible issues.

KathyNJU commented 6 months ago

Apart from 0RTT experiment, if I want to conduct performance testing with Picoquic, I would use the picoquic_sample example program. What parameters can I modify to perform different types of performance tests? Currently, I have implemented flow control on both the server and client sides.

huitema commented 6 months ago

The obvious parameters are the latency and throughput of the simulated data links. You may also want to retrieve files of various sizes, or series of files.

There may be a need to simulate variable latency, for example if the spacecraft it on its way from Earth to the Moon.

There is a research issue regarding "suspension". Suppose for example that the space craft is in orbit around the Moon. At some point in every orbit, it might move behind the moon and become unreachable.

huitema commented 6 months ago

I looked at the 0RTT issue. I think you have found a limitation of TLS 1.3. The session tickets include an "obfuscated age", which is a mechanism to limit 0RTT replay attacks. The client obfuscate a "ticket age", set to the time at which the ticket was used minus the time at which the ticket was received. The server then uses that to check whether:

(ticket_issued_time - now) - ticket_age < max_ticket_reuse_delay

If that check fails, the connection is accepted but 0RTT is disabled. Picoquic uses "picotls" for the TLS1.3 stack. Picotls hardwires the max_ticket_reuse_delay to 10 seconds. But the formula depends on the transmission delay:

If the transmission delay is long, it is very possible that this obfuscated ticket age check fails.

I filed a bug about that in the picotls repo.

I also sent a message describing the problem to the TLS working group.

huitema commented 6 months ago

The weirdness issue that you found between 40 minutes and 20 minutes may be due to an integer overflow bug in picotls.