QUIC TXP/CHANNEL: Generate MAX_STREAMS using RXFC
authorHugo Landau <hlandau@openssl.org>
Tue, 18 Apr 2023 18:30:55 +0000 (19:30 +0100)
committerHugo Landau <hlandau@openssl.org>
Fri, 12 May 2023 13:47:12 +0000 (14:47 +0100)
Though the RXFC was designed for stream flow control, its logic
is generic enough to use to control MAX_STREAMS generation.

Control of when _we_ can open streams is already done in a bespoke
fashion and doesn't use a TXFC, however (see
ossl_quic_stream_map_update_state).

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20765)

include/internal/quic_txp.h
ssl/quic/quic_channel.c
ssl/quic/quic_channel_local.h
ssl/quic/quic_txp.c
test/quic_txp_test.c

index fb553f97e5951481a9e194865a673b6e6fe52df7..0e4fd5cc49d03d5374ce77117226b136ea208461 100644 (file)
@@ -42,6 +42,8 @@ typedef struct ossl_quic_tx_packetiser_args_st {
     QUIC_STREAM_MAP *qsm;       /* QUIC Streams Map */
     QUIC_TXFC       *conn_txfc; /* QUIC Connection-Level TX Flow Controller */
     QUIC_RXFC       *conn_rxfc; /* QUIC Connection-Level RX Flow Controller */
+    QUIC_RXFC       *max_streams_bidi_rxfc; /* QUIC RXFC for MAX_STREAMS generation */
+    QUIC_RXFC       *max_streams_uni_rxfc;
     const OSSL_CC_METHOD *cc_method; /* QUIC Congestion Controller */
     OSSL_CC_DATA    *cc_data;   /* QUIC Congestion Controller Instance */
     OSSL_TIME       (*now)(void *arg);  /* Callback to get current time. */
index 3e099e9d93967a2fdfc9ce186f598f428c22c4cd..c68916680522259ff1f65dfc4de756d2280352bf 100644 (file)
@@ -111,6 +111,8 @@ static int gen_rand_conn_id(OSSL_LIB_CTX *libctx, size_t len, QUIC_CONN_ID *cid)
 #define DEFAULT_INIT_STREAM_RXFC_WND    (2 * 1024 * 1024)
 #define DEFAULT_STREAM_RXFC_MAX_WND_MUL 5
 
+#define DEFAULT_INIT_CONN_MAX_STREAMS           100
+
 static int ch_init(QUIC_CHANNEL *ch)
 {
     OSSL_QUIC_TX_PACKETISER_ARGS txp_args = {0};
@@ -160,6 +162,16 @@ static int ch_init(QUIC_CHANNEL *ch)
                              get_time, ch))
         goto err;
 
+    if (!ossl_quic_rxfc_init_for_stream_count(&ch->max_streams_bidi_rxfc,
+                                              DEFAULT_INIT_CONN_MAX_STREAMS,
+                                              get_time, ch))
+        goto err;
+
+    if (!ossl_quic_rxfc_init_for_stream_count(&ch->max_streams_uni_rxfc,
+                                             DEFAULT_INIT_CONN_MAX_STREAMS,
+                                             get_time, ch))
+        goto err;
+
     if (!ossl_statm_init(&ch->statm))
         goto err;
 
@@ -172,25 +184,29 @@ static int ch_init(QUIC_CHANNEL *ch)
                                   ch->cc_method, ch->cc_data)) == NULL)
         goto err;
 
-    if (!ossl_quic_stream_map_init(&ch->qsm, get_stream_limit, ch))
+    if (!ossl_quic_stream_map_init(&ch->qsm, get_stream_limit, ch,
+                                   &ch->max_streams_bidi_rxfc,
+                                   &ch->max_streams_uni_rxfc))
         goto err;
 
     ch->have_qsm = 1;
 
     /* We use a zero-length SCID. */
-    txp_args.cur_dcid           = ch->init_dcid;
-    txp_args.ack_delay_exponent = 3;
-    txp_args.qtx                = ch->qtx;
-    txp_args.txpim              = ch->txpim;
-    txp_args.cfq                = ch->cfq;
-    txp_args.ackm               = ch->ackm;
-    txp_args.qsm                = &ch->qsm;
-    txp_args.conn_txfc          = &ch->conn_txfc;
-    txp_args.conn_rxfc          = &ch->conn_rxfc;
-    txp_args.cc_method          = ch->cc_method;
-    txp_args.cc_data            = ch->cc_data;
-    txp_args.now                = get_time;
-    txp_args.now_arg            = ch;
+    txp_args.cur_dcid               = ch->init_dcid;
+    txp_args.ack_delay_exponent     = 3;
+    txp_args.qtx                    = ch->qtx;
+    txp_args.txpim                  = ch->txpim;
+    txp_args.cfq                    = ch->cfq;
+    txp_args.ackm                   = ch->ackm;
+    txp_args.qsm                    = &ch->qsm;
+    txp_args.conn_txfc              = &ch->conn_txfc;
+    txp_args.conn_rxfc              = &ch->conn_rxfc;
+    txp_args.max_streams_bidi_rxfc  = &ch->max_streams_bidi_rxfc;
+    txp_args.max_streams_uni_rxfc   = &ch->max_streams_uni_rxfc;
+    txp_args.cc_method              = ch->cc_method;
+    txp_args.cc_data                = ch->cc_data;
+    txp_args.now                    = get_time;
+    txp_args.now_arg                = ch;
     for (pn_space = QUIC_PN_SPACE_INITIAL; pn_space < QUIC_PN_SPACE_NUM; ++pn_space) {
         ch->crypto_send[pn_space] = ossl_quic_sstream_new(INIT_CRYPTO_BUF_LEN);
         if (ch->crypto_send[pn_space] == NULL)
@@ -1215,13 +1231,12 @@ static int ch_generate_transport_params(QUIC_CHANNEL *ch)
                                                    ch->tx_init_max_stream_data_uni))
         goto err;
 
-    /* TODO(QUIC): MAX_STREAMS modelling */
     if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_INITIAL_MAX_STREAMS_BIDI,
-                                                   ch->is_server ? 100 : 100))
+                                                   ossl_quic_rxfc_get_cwm(&ch->max_streams_bidi_rxfc)))
         goto err;
 
     if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_INITIAL_MAX_STREAMS_UNI,
-                                                   100))
+                                                   ossl_quic_rxfc_get_cwm(&ch->max_streams_uni_rxfc)))
         goto err;
 
     if (!WPACKET_get_total_written(&wpkt, &buf_len))
index f13d0118ee30c1e220835382da15cb019fd4e5c6..379528b516dabff22ecc1396e5e1eba527b99414 100644 (file)
@@ -69,9 +69,13 @@ struct quic_channel_st {
     OSSL_QUIC_TX_PACKETISER         *txp;
     QUIC_TXPIM                      *txpim;
     QUIC_CFQ                        *cfq;
-    /* Connection level FC. */
+    /*
+     * Connection level FC. The stream_count RXFCs is used to manage
+     * MAX_STREAMS signalling.
+     */
     QUIC_TXFC                       conn_txfc;
     QUIC_RXFC                       conn_rxfc;
+    QUIC_RXFC                       max_streams_bidi_rxfc, max_streams_uni_rxfc;
     QUIC_STREAM_MAP                 qsm;
     OSSL_STATM                      statm;
     OSSL_CC_DATA                    *cc_data;
index 97a88c3ff1f5b8fea8918ccb3e0c41a12064e1ef..10a1a5f18eac583a8f02cd5b496ade00e6b885c5 100644 (file)
@@ -351,7 +351,9 @@ OSSL_QUIC_TX_PACKETISER *ossl_quic_tx_packetiser_new(const OSSL_QUIC_TX_PACKETIS
         || args->ackm == NULL
         || args->qsm == NULL
         || args->conn_txfc == NULL
-        || args->conn_rxfc == NULL) {
+        || args->conn_rxfc == NULL
+        || args->max_streams_bidi_rxfc == NULL
+        || args->max_streams_uni_rxfc == NULL) {
         ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
         return NULL;
     }
@@ -807,8 +809,13 @@ static int txp_el_pending(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
         return 1;
 
     /* Do we want to produce a MAX_STREAMS frame? */
-    if (a.allow_conn_fc && (txp->want_max_streams_bidi
-                            || txp->want_max_streams_uni))
+    if (a.allow_conn_fc
+        && (txp->want_max_streams_bidi
+            || ossl_quic_rxfc_has_cwm_changed(txp->args.max_streams_bidi_rxfc,
+                                              0)
+            || txp->want_max_streams_uni
+            || ossl_quic_rxfc_has_cwm_changed(txp->args.max_streams_uni_rxfc,
+                                              0)))
         return 1;
 
     /* Do we want to produce a HANDSHAKE_DONE frame? */
@@ -1927,15 +1934,13 @@ static int txp_generate_for_el_actual(OSSL_QUIC_TX_PACKETISER *txp,
     }
 
     /* MAX_STREAMS_BIDI (Regenerate) */
-    /*
-     * TODO(STREAMS): Once we support multiple streams, add stream count FC
-     * and plug this in.
-     */
     if (a.allow_conn_fc
-        && txp->want_max_streams_bidi
+        && (txp->want_max_streams_bidi
+            || ossl_quic_rxfc_has_cwm_changed(txp->args.max_streams_bidi_rxfc, 0))
         && tx_helper_get_space_left(&h) >= MIN_FRAME_SIZE_MAX_STREAMS_BIDI) {
         WPACKET *wpkt = tx_helper_begin(&h);
-        uint64_t max_streams = 1; /* TODO */
+        uint64_t max_streams
+            = ossl_quic_rxfc_get_cwm(txp->args.max_streams_bidi_rxfc);
 
         if (wpkt == NULL)
             goto fatal_err;
@@ -1956,10 +1961,12 @@ static int txp_generate_for_el_actual(OSSL_QUIC_TX_PACKETISER *txp,
 
     /* MAX_STREAMS_UNI (Regenerate) */
     if (a.allow_conn_fc
-        && txp->want_max_streams_uni
+        && (txp->want_max_streams_uni
+            || ossl_quic_rxfc_has_cwm_changed(txp->args.max_streams_uni_rxfc, 0))
         && tx_helper_get_space_left(&h) >= MIN_FRAME_SIZE_MAX_STREAMS_UNI) {
         WPACKET *wpkt = tx_helper_begin(&h);
-        uint64_t max_streams = 0; /* TODO */
+        uint64_t max_streams
+            = ossl_quic_rxfc_get_cwm(txp->args.max_streams_uni_rxfc);
 
         if (wpkt == NULL)
             goto fatal_err;
@@ -2209,11 +2216,15 @@ static int txp_generate_for_el_actual(OSSL_QUIC_TX_PACKETISER *txp,
         ossl_quic_rxfc_has_cwm_changed(txp->args.conn_rxfc, 1);
     }
 
-    if (tpkt->had_max_streams_bidi_frame)
+    if (tpkt->had_max_streams_bidi_frame) {
         txp->want_max_streams_bidi = 0;
+        ossl_quic_rxfc_has_cwm_changed(txp->args.max_streams_bidi_rxfc, 1);
+    }
 
-    if (tpkt->had_max_streams_uni_frame)
+    if (tpkt->had_max_streams_uni_frame) {
         txp->want_max_streams_uni = 0;
+        ossl_quic_rxfc_has_cwm_changed(txp->args.max_streams_uni_rxfc, 1);
+    }
 
     if (tpkt->had_ack_frame)
         txp->want_ack &= ~(1UL << pn_space);
index 265c23fabcb0ef1089bc225f793cdb6dfbabbbef..dd85bb5692226d9a8e4e81ea25b834188934a910 100644 (file)
@@ -47,6 +47,7 @@ struct helper {
     BIO                             *bio1, *bio2;
     QUIC_TXFC                       conn_txfc;
     QUIC_RXFC                       conn_rxfc, stream_rxfc;
+    QUIC_RXFC                       max_streams_bidi_rxfc, max_streams_uni_rxfc;
     OSSL_STATM                      statm;
     OSSL_CC_DATA                    *cc_data;
     const OSSL_CC_METHOD            *cc_method;
@@ -147,6 +148,17 @@ static int helper_init(struct helper *h)
                                        NULL)))
         goto err;
 
+    if (!TEST_true(ossl_quic_rxfc_init(&h->max_streams_bidi_rxfc, NULL,
+                                       100, 100,
+                                       fake_now,
+                                       NULL)))
+        goto err;
+
+    if (!TEST_true(ossl_quic_rxfc_init(&h->max_streams_uni_rxfc, NULL,
+                                       100, 100,
+                                       fake_now,
+                                       NULL)))
+
     if (!TEST_true(ossl_statm_init(&h->statm)))
         goto err;
 
@@ -171,14 +183,16 @@ static int helper_init(struct helper *h)
         if (!TEST_ptr(h->args.crypto[i] = ossl_quic_sstream_new(4096)))
             goto err;
 
-    h->args.cur_scid   = scid_1;
-    h->args.cur_dcid   = dcid_1;
-    h->args.qsm        = &h->qsm;
-    h->args.conn_txfc  = &h->conn_txfc;
-    h->args.conn_rxfc  = &h->conn_rxfc;
-    h->args.cc_method  = h->cc_method;
-    h->args.cc_data    = h->cc_data;
-    h->args.now        = fake_now;
+    h->args.cur_scid                = scid_1;
+    h->args.cur_dcid                = dcid_1;
+    h->args.qsm                     = &h->qsm;
+    h->args.conn_txfc               = &h->conn_txfc;
+    h->args.conn_rxfc               = &h->conn_rxfc;
+    h->args.max_streams_bidi_rxfc   = &h->max_streams_bidi_rxfc;
+    h->args.max_streams_uni_rxfc    = &h->max_streams_uni_rxfc;
+    h->args.cc_method               = h->cc_method;
+    h->args.cc_data                 = h->cc_data;
+    h->args.now                     = fake_now;
 
     if (!TEST_ptr(h->txp = ossl_quic_tx_packetiser_new(&h->args)))
         goto err;