QUIC RCIDM: Minor updates
authorHugo Landau <hlandau@openssl.org>
Fri, 22 Dec 2023 12:18:19 +0000 (12:18 +0000)
committerTomas Mraz <tomas@openssl.org>
Thu, 11 Jan 2024 10:16:27 +0000 (11:16 +0100)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23022)

doc/designs/quic-design/glossary.md
fuzz/quic-rcidm.c
include/internal/quic_rcidm.h
ssl/quic/quic_rcidm.c
test/quic_rcidm_test.c

index 3993c6f8453ba0a7c066718729e67f3b9c8d806b..1cf0f06cddda446a0dfd26aa4f4485c20fceb817 100644 (file)
@@ -128,6 +128,12 @@ in which API calls can be made on different threads.
 **MSST:** Multi-stream single-thread. Refers to a type of multi-stream QUIC
 usage in which API calls must not be made concurrently.
 
+**NCID:** New Connection ID. Refers to a QUIC `NEW_CONNECTION_ID` frame.
+
+**Numbered CID:** Refers to a Connection ID which has a sequence number assigned
+to it. All CIDs other than Initial ODCIDs and Retry ODCIDs have a sequence
+number assigned. See also Unnumbered CID.
+
 **ODCID:** Original Destination CID. This is the DCID found in the first Initial
 packet sent by a client, and is used to generate the secrets for encrypting
 Initial packets. It is only used temporarily.
@@ -195,7 +201,8 @@ an API object. As such, a `QUIC_CONNECTION` is to a `QUIC_CHANNEL` what a
 `QUIC_XSO` is to a `QUIC_STREAM`.
 
 **RCID:** Remote CID. Refers to a CID which has been provided to us by a peer
-and which we can place in the DCID field of an outgoing packet. See also LCID.
+and which we can place in the DCID field of an outgoing packet. See also LCID,
+Unnumbered CID and Numbered CID.
 
 **RCIDM:** Remote CID Manager. Tracks RCIDs which have been provided to us by a
 peer. See also LCIDM.
@@ -284,6 +291,12 @@ to a *send stream buffer*, and that data is eventually TX'd by the TXP and QTX.)
 **Uni:** Abbreviation of unidirectional, referring to a QUIC unidirectional
 stream.
 
+**Unnumbered CID:** Refers to a CID which does not have a sequence number
+associated with it and therefore cannot be referred to by a `NEW_CONNECTION_ID`
+or `RETIRE_CONNECTION_ID` frame's sequence number fields. The only unnumbered
+CIDs are Initial ODCIDs and Retry ODCIDs. These CIDs are exceptionally retired
+automatically during handshake confirmation. See also Numbered CID.
+
 **URXE:** Unprocessed RX entry. Structure containing yet-undecrypted received
 datagrams pending processing. Stored in a queue known as the URXL.
 Ownership of URXEs is shared between DEMUX and QRX.
index 51919b8d70eb714613b52333ce0713f3f6e3c8c3..825fe0c2fdf8a3eaa3d5a6ac6a09a4da30fc5c42 100644 (file)
@@ -129,7 +129,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len)
                 goto err;
             }
 
-            ossl_quic_rcidm_add_from_initial(rcidm, &arg_cid);
+            ossl_quic_rcidm_add_from_server_retry(rcidm, &arg_cid);
             break;
 
         case CMD_ADD_FROM_NCID:
index 4837fed480cf39a9e0e3ea67cd374984518d42c9..8eeaaf550e533278a58bfb65b3da03463f3862a0 100644 (file)
  * QUIC Remote Connection ID Manager
  * =================================
  *
- * This manages connection IDs for the TX side, which  is to say that it tracks
- * remote CIDs (RCIDs) which a peer has issued to us and which we can use as the
- * DCID of packets we transmit. It is entirely separate from the LCIDM, which
- * handles routing received packets by their DCIDs.
+ * This manages connection IDs for the TX side. The RCIDM tracks remote CIDs
+ * (RCIDs) which a peer has issued to us and which we can use as the DCID of
+ * packets we transmit. It is entirely separate from the LCIDM, which handles
+ * routing received packets by their DCIDs.
  *
  * RCIDs fall into four categories:
  *
@@ -42,8 +42,8 @@ typedef struct quic_rcidm_st QUIC_RCIDM;
 /*
  * Creates a new RCIDM. Returns NULL on failure.
  *
- * For a client, initial_rcid is the client's Initial ODCID.
- * For a server, initial_rcid is NULL.
+ * For a client, initial_odcid is the client's Initial ODCID.
+ * For a server, initial_odcid is NULL.
  */
 QUIC_RCIDM *ossl_quic_rcidm_new(const QUIC_CONN_ID *initial_odcid);
 
@@ -135,12 +135,13 @@ void ossl_quic_rcidm_request_roll(QUIC_RCIDM *rcidm);
  * packets using the RCID may still be in flight. The caller must determine an
  * appropriate delay using knowledge of network conditions (RTT, etc.) which is
  * outside the scope of the RCIDM. The caller is responsible for implementing
- * this delay.
+ * this delay based on the last time a packet was transmitted using the RCID
+ * being retired.
  */
 int ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM *rcid, uint64_t *seq_num);
 
 /*
- * Like ossl_quic_rcidm_pop_retire_seek_num, but does not pop the item from the
+ * Like ossl_quic_rcidm_pop_retire_seq_num, but does not pop the item from the
  * queue. If this call succeeds, the next call to
  * ossl_quic_rcidm_pop_retire_seq_num is guaranteed to output the same sequence
  * number.
index c2fd65960975a443264d506e1108371f1e06524a..90a4b2c2aeba64f17472d8b77542bef29a849e9d 100644 (file)
@@ -37,7 +37,7 @@ static void rcidm_update(QUIC_RCIDM *rcidm);
 static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm,
                                      const QUIC_CONN_ID *rcid);
 
-#define PACKETS_PER_RCID        1000
+#define PACKETS_PER_RCID        10000
 
 #define INITIAL_SEQ_NUM         0
 #define PREF_ADDR_SEQ_NUM       1
@@ -124,7 +124,7 @@ enum {
 };
 
 typedef struct rcid_st {
-    OSSL_LIST_MEMBER(retiring, struct rcid_st); /* valid iff retire == 1 */
+    OSSL_LIST_MEMBER(retiring, struct rcid_st); /* valid iff RETIRING */
 
     QUIC_CONN_ID    cid;        /* The actual CID string for this RCID */
     uint64_t        seq_num;
@@ -162,6 +162,11 @@ struct quic_rcidm_st {
     /*
      * The current RCID we prefer to use (value undefined if
      * !have_preferred_rcid).
+     *
+     * This is preferentially set to a numbered RCID (represented by an RCID
+     * object) if we have one (in which case preferred_rcid == cur_rcid->cid);
+     * otherwise it is set to one of the unnumbered RCIDs (the Initial ODCID or
+     * Retry ODCID) if available (and cur_rcid == NULL).
      */
     QUIC_CONN_ID                preferred_rcid;
 
@@ -189,11 +194,11 @@ struct quic_rcidm_st {
     PRIORITY_QUEUE_OF(RCID)     *rcids;
 
     /*
-     * Current RCID we are using. This may differ from the first item in the
-     * priority queue if we received NCID frames out of order. For example if we
-     * get seq 5, switch to it immediately, then get seq 4, we want to keep
-     * using seq 5 until we decide to roll again rather than immediately switch
-     * to seq 4. Never points to an object on the retiring_list.
+     * Current RCID object we are using. This may differ from the first item in
+     * the priority queue if we received NCID frames out of order. For example
+     * if we get seq 5, switch to it immediately, then get seq 4, we want to
+     * keep using seq 5 until we decide to roll again rather than immediately
+     * switch to seq 4. Never points to an object on the retiring_list.
      */
     RCID                        *cur_rcid;
 
@@ -229,6 +234,14 @@ struct quic_rcidm_st {
     unsigned int    roll_requested                  : 1;
 };
 
+/*
+ * Caller must periodically pop retired RCIDs and handle them. If the caller
+ * fails to do so, fail safely rather than start exhibiting integer rollover.
+ * Limit the total number of numbered RCIDs to an implausibly large but safe
+ * value.
+ */
+#define MAX_NUMBERED_RCIDS      (SIZE_MAX / 2)
+
 static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid,
                                   unsigned int state);
 
@@ -254,7 +267,6 @@ static void rcidm_check_rcid(QUIC_RCIDM *rcidm, RCID *rcid)
             || rcid->state == RCID_STATE_RETIRING);
     assert(rcidm->num_changes == 0 || rcidm->handshake_complete);
     assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0);
-    assert(rcidm->num_retiring < SIZE_MAX / 2);
 }
 
 static int rcid_cmp(const RCID *a, const RCID *b)
@@ -263,10 +275,6 @@ static int rcid_cmp(const RCID *a, const RCID *b)
         return -1;
     if (a->seq_num > b->seq_num)
         return 1;
-    if ((uintptr_t)a < (uintptr_t)b)
-        return -1;
-    if ((uintptr_t)a > (uintptr_t)b)
-        return 1;
     return 0;
 }
 
@@ -337,7 +345,9 @@ static RCID *rcidm_create_rcid(QUIC_RCIDM *rcidm, uint64_t seq_num,
     RCID *rcid;
 
     if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN
-        || seq_num > OSSL_QUIC_VLINT_MAX)
+        || seq_num > OSSL_QUIC_VLINT_MAX
+        || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring
+            > MAX_NUMBERED_RCIDS)
         return NULL;
 
     if ((rcid = OPENSSL_zalloc(sizeof(*rcid))) == NULL)
@@ -351,7 +361,6 @@ static RCID *rcidm_create_rcid(QUIC_RCIDM *rcidm, uint64_t seq_num,
         rcid->state = RCID_STATE_PENDING;
 
         if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) {
-            assert(0);
             OPENSSL_free(rcid);
             return NULL;
         }
@@ -501,7 +510,7 @@ static void rcidm_update(QUIC_RCIDM *rcidm)
      * If there are no RCIDs from NCID frames we can use, go through the various
      * kinds of bootstrapping RCIDs we can use in order of priority.
      */
-    if (rcidm->added_retry_odcid) {
+    if (rcidm->added_retry_odcid && !rcidm->handshake_complete) {
         rcidm_set_preferred_rcid(rcidm, &rcidm->retry_odcid);
         return;
     }
index 3e841bed569aba07b631e82b2f967da0aebf7c5d..ad1c1ac588dc10845ecf29c09adf971dedd56a63 100644 (file)
@@ -16,6 +16,11 @@ static const QUIC_CONN_ID cid8_3 = { 8, { 3 } };
 static const QUIC_CONN_ID cid8_4 = { 8, { 4 } };
 static const QUIC_CONN_ID cid8_5 = { 8, { 5 } };
 
+/*
+ * 0: Client, Initial ODCID
+ * 1: Client, Initial ODCID + Retry ODCID
+ * 2: Server, doesn't start with Initial ODCID
+ */
 static int test_rcidm(int idx)
 {
     int testresult = 0;