**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.
`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.
**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.
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:
* 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:
*
/*
* 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);
* 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.
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
};
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;
/*
* 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;
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;
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);
|| 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)
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;
}
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)
rcid->state = RCID_STATE_PENDING;
if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) {
- assert(0);
OPENSSL_free(rcid);
return NULL;
}
* 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;
}
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;