QUIC TXP: Allow TXP to generate probes

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19925)
This commit is contained in:
Hugo Landau 2022-12-16 10:57:11 +00:00 committed by Tomas Mraz
parent e2212b20bc
commit fee8f48e35
2 changed files with 172 additions and 16 deletions

View File

@ -313,9 +313,11 @@ static void on_regen_notify(uint64_t frame_type, uint64_t stream_id,
static int sstream_is_pending(QUIC_SSTREAM *sstream);
static int txp_el_pending(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
uint32_t archetype,
int cc_can_send,
uint32_t *conn_close_enc_level);
static int txp_generate_for_el(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
uint32_t archetype,
int cc_can_send,
int is_last_in_dgram,
int dgram_contains_initial,
int chosen_for_conn_close);
@ -476,14 +478,16 @@ int ossl_quic_tx_packetiser_has_pending(OSSL_QUIC_TX_PACKETISER *txp,
{
uint32_t enc_level, conn_close_enc_level = QUIC_ENC_LEVEL_NUM;
int bypass_cc = ((flags & TX_PACKETISER_BYPASS_CC) != 0);
int cc_can_send;
if (!bypass_cc && !txp->args.cc_method->can_send(txp->args.cc_data))
return 0;
cc_can_send
= (bypass_cc || txp->args.cc_method->can_send(txp->args.cc_data));
for (enc_level = QUIC_ENC_LEVEL_INITIAL;
enc_level < QUIC_ENC_LEVEL_NUM;
++enc_level)
if (txp_el_pending(txp, enc_level, archetype, &conn_close_enc_level))
if (txp_el_pending(txp, enc_level, archetype, cc_can_send,
&conn_close_enc_level))
return 1;
return 0;
@ -498,17 +502,20 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp,
uint32_t archetype)
{
uint32_t enc_level, conn_close_enc_level = QUIC_ENC_LEVEL_NUM;
int have_pkt_for_el[QUIC_ENC_LEVEL_NUM], is_last_in_dgram;
int have_pkt_for_el[QUIC_ENC_LEVEL_NUM], is_last_in_dgram, cc_can_send;
size_t num_el_in_dgram = 0, pkts_done = 0;
int rc;
if (!txp->args.cc_method->can_send(txp->args.cc_data))
return TX_PACKETISER_RES_NO_PKT;
/*
* If CC says we cannot send we still may be able to send any queued probes.
*/
cc_can_send = txp->args.cc_method->can_send(txp->args.cc_data);
for (enc_level = QUIC_ENC_LEVEL_INITIAL;
enc_level < QUIC_ENC_LEVEL_NUM;
++enc_level) {
have_pkt_for_el[enc_level] = txp_el_pending(txp, enc_level, archetype,
cc_can_send,
&conn_close_enc_level);
if (have_pkt_for_el[enc_level])
++num_el_in_dgram;
@ -530,7 +537,8 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp,
continue;
is_last_in_dgram = (pkts_done + 1 == num_el_in_dgram);
rc = txp_generate_for_el(txp, enc_level, archetype, is_last_in_dgram,
rc = txp_generate_for_el(txp, enc_level, archetype, cc_can_send,
is_last_in_dgram,
have_pkt_for_el[QUIC_ENC_LEVEL_INITIAL],
enc_level == conn_close_enc_level);
@ -739,6 +747,7 @@ static int txp_get_archetype_data(uint32_t enc_level,
*/
static int txp_el_pending(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
uint32_t archetype,
int cc_can_send,
uint32_t *conn_close_enc_level)
{
struct archetype_data a;
@ -754,6 +763,23 @@ static int txp_el_pending(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
if (!txp_get_archetype_data(enc_level, archetype, &a))
return 0;
/* Do we need to send a PTO probe? */
if (a.allow_force_ack_eliciting) {
OSSL_ACKM_PROBE_INFO *probe_info
= ossl_ackm_get_probe_request(txp->args.ackm);
if ((enc_level == QUIC_ENC_LEVEL_INITIAL
&& probe_info->anti_deadlock_initial > 0)
|| (enc_level == QUIC_ENC_LEVEL_HANDSHAKE
&& probe_info->anti_deadlock_handshake > 0)
|| probe_info->pto[pn_space] > 0)
return 1;
}
if (!cc_can_send)
/* If CC says we cannot currently send, we can only send probes. */
return 0;
/* Does the crypto stream for this EL want to produce anything? */
if (a.allow_crypto && sstream_is_pending(txp->args.crypto[pn_space]))
return 1;
@ -859,6 +885,7 @@ static int sstream_is_pending(QUIC_SSTREAM *sstream)
*/
static int txp_generate_for_el(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
uint32_t archetype,
int cc_can_send,
int is_last_in_dgram,
int dgram_contains_initial,
int chosen_for_conn_close)
@ -877,11 +904,20 @@ static int txp_generate_for_el(OSSL_QUIC_TX_PACKETISER *txp, uint32_t enc_level,
time_since_last = ossl_time_subtract(txp->args.now(txp->args.now_arg),
txp->last_tx_time);
cc_limit_ = txp->args.cc_method->get_send_allowance(txp->args.cc_data,
time_since_last,
ossl_time_is_zero(time_since_last));
if (!cc_can_send) {
/*
* If we are called when we cannot send, this must be because we want
* to generate a probe. In this circumstance, don't clamp based on CC.
*/
cc_limit = SIZE_MAX;
} else {
/* Allow CC to clamp how much we can send. */
cc_limit_ = txp->args.cc_method->get_send_allowance(txp->args.cc_data,
time_since_last,
ossl_time_is_zero(time_since_last));
cc_limit = (cc_limit_ > SIZE_MAX ? SIZE_MAX : (size_t)cc_limit_);
cc_limit = (cc_limit_ > SIZE_MAX ? SIZE_MAX : (size_t)cc_limit_);
}
/* Assemble packet header. */
phdr.type = ossl_quic_enc_level_to_pkt_type(enc_level);
@ -1781,18 +1817,30 @@ static int txp_generate_for_el_actual(OSSL_QUIC_TX_PACKETISER *txp,
uint32_t pn_space = ossl_quic_enc_level_to_pn_space(enc_level);
struct tx_helper h;
int have_helper = 0, have_ack_eliciting = 0, done_pre_token = 0;
int require_ack_eliciting;
int require_ack_eliciting = 0;
QUIC_CFQ_ITEM *cfq_item;
QUIC_TXPIM_PKT *tpkt = NULL;
OSSL_QTX_PKT pkt;
QUIC_STREAM *tmp_head = NULL, *stream;
OSSL_ACKM_PROBE_INFO *probe_info
= ossl_ackm_get_probe_request(txp->args.ackm);
if (!txp_get_archetype_data(enc_level, archetype, &a))
goto fatal_err;
require_ack_eliciting
= (a.allow_force_ack_eliciting
&& (txp->force_ack_eliciting & (1UL << pn_space)));
if (a.allow_force_ack_eliciting) {
/*
* Make this packet ACK-eliciting if it has been explicitly requested,
* or if ACKM has requested a probe for this PN space.
*/
if ((txp->force_ack_eliciting & (1UL << pn_space)) != 0
|| (enc_level == QUIC_ENC_LEVEL_INITIAL
&& probe_info->anti_deadlock_initial > 0)
|| (enc_level == QUIC_ENC_LEVEL_HANDSHAKE
&& probe_info->anti_deadlock_handshake > 0)
|| probe_info->pto[pn_space] > 0)
require_ack_eliciting = 1;
}
/* Minimum cannot be bigger than maximum. */
if (min_ppl > max_ppl)
@ -2158,6 +2206,24 @@ static int txp_generate_for_el_actual(OSSL_QUIC_TX_PACKETISER *txp,
if (tpkt->had_ack_frame)
txp->want_ack &= ~(1UL << pn_space);
/*
* Decrement probe request counts if we have sent a packet that meets
* the requirement of a probe, namely being ACK-eliciting.
*/
if (have_ack_eliciting) {
if (enc_level == QUIC_ENC_LEVEL_INITIAL
&& probe_info->anti_deadlock_initial > 0)
--probe_info->anti_deadlock_initial;
if (enc_level == QUIC_ENC_LEVEL_HANDSHAKE
&& probe_info->anti_deadlock_handshake > 0)
--probe_info->anti_deadlock_handshake;
if (a.allow_force_ack_eliciting /* (i.e., not for 0-RTT) */
&& probe_info->pto[pn_space] > 0)
--probe_info->pto[pn_space];
}
/* Done. */
tx_helper_cleanup(&h);
return rc;

View File

@ -1086,6 +1086,93 @@ static const struct script_op script_14[] = {
OP_END
};
/* 15. INITIAL, Anti-Deadlock Probe Simulation */
static int gen_probe_initial(struct helper *h)
{
OSSL_ACKM_PROBE_INFO *probe = ossl_ackm_get_probe_request(h->args.ackm);
/*
* Pretend the ACKM asked for an anti-deadlock Initial probe.
* We test output of this in the ACKM unit tests.
*/
++probe->anti_deadlock_initial;
return 1;
}
static const struct script_op script_15[] = {
OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_INITIAL, QRL_SUITE_AES128GCM, secret_1)
OP_TXP_GENERATE_NONE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_CHECK(gen_probe_initial)
OP_TXP_GENERATE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_RX_PKT()
OP_EXPECT_DGRAM_LEN(1200, 1200)
OP_NEXT_FRAME()
OP_EXPECT_FRAME(OSSL_QUIC_FRAME_TYPE_PING)
OP_EXPECT_NO_FRAME()
OP_RX_PKT_NONE()
OP_TXP_GENERATE_NONE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_END
};
/* 16. HANDSHAKE, Anti-Deadlock Probe Simulation */
static int gen_probe_handshake(struct helper *h)
{
OSSL_ACKM_PROBE_INFO *probe = ossl_ackm_get_probe_request(h->args.ackm);
/*
* Pretend the ACKM asked for an anti-deadlock Handshake probe.
* We test output of this in the ACKM unit tests.
*/
++probe->anti_deadlock_handshake;
return 1;
}
static const struct script_op script_16[] = {
OP_DISCARD_EL(QUIC_ENC_LEVEL_INITIAL)
OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, QRL_SUITE_AES128GCM, secret_1)
OP_TXP_GENERATE_NONE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_CHECK(gen_probe_handshake)
OP_TXP_GENERATE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_RX_PKT()
OP_EXPECT_DGRAM_LEN(21, 512)
OP_NEXT_FRAME()
OP_EXPECT_FRAME(OSSL_QUIC_FRAME_TYPE_PING)
OP_EXPECT_NO_FRAME()
OP_RX_PKT_NONE()
OP_TXP_GENERATE_NONE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_END
};
/* 17. 1-RTT, Probe Simulation */
static int gen_probe_1rtt(struct helper *h)
{
OSSL_ACKM_PROBE_INFO *probe = ossl_ackm_get_probe_request(h->args.ackm);
/*
* Pretend the ACKM asked for a 1-RTT PTO probe.
* We test output of this in the ACKM unit tests.
*/
++probe->pto[QUIC_PN_SPACE_APP];
return 1;
}
static const struct script_op script_17[] = {
OP_DISCARD_EL(QUIC_ENC_LEVEL_INITIAL)
OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE)
OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, QRL_SUITE_AES128GCM, secret_1)
OP_TXP_GENERATE_NONE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_CHECK(gen_probe_1rtt)
OP_TXP_GENERATE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_RX_PKT()
OP_EXPECT_DGRAM_LEN(21, 512)
OP_NEXT_FRAME()
OP_EXPECT_FRAME(OSSL_QUIC_FRAME_TYPE_PING)
OP_EXPECT_NO_FRAME()
OP_RX_PKT_NONE()
OP_TXP_GENERATE_NONE(TX_PACKETISER_ARCHETYPE_NORMAL)
OP_END
};
static const struct script_op *const scripts[] = {
script_1,
script_2,
@ -1100,7 +1187,10 @@ static const struct script_op *const scripts[] = {
script_11,
script_12,
script_13,
script_14
script_14,
script_15,
script_16,
script_17
};
static void skip_padding(struct helper *h)