diff --git a/CHANGELOG b/CHANGELOG index 7bc790cc0..e8e3eaeaf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +2020-06-24 + - 2.17.2 + - [BUGFIX] Infinite loop in stream: advance read offset when discarding + data. + - [OPTIMIZATION] Header protection: only initialize cipher once. + - [OPTIMIZATION] Batch header protection application. + 2020-06-18 - 2.17.1 - [FEATURE] QUIC and HTTP/3 Internet Draft 29 support. diff --git a/docs/conf.py b/docs/conf.py index 617502ffd..16c517d81 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = u'2.17' # The full version, including alpha/beta/rc tags -release = u'2.17.1' +release = u'2.17.2' # -- General configuration --------------------------------------------------- diff --git a/include/lsquic.h b/include/lsquic.h index 50b52f442..7010e8c35 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -25,7 +25,7 @@ extern "C" { #define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MINOR_VERSION 17 -#define LSQUIC_PATCH_VERSION 1 +#define LSQUIC_PATCH_VERSION 2 /** * Engine flags: diff --git a/src/liblsquic/lsquic_enc_sess.h b/src/liblsquic/lsquic_enc_sess.h index 982b454df..d5d8d5db1 100644 --- a/src/liblsquic/lsquic_enc_sess.h +++ b/src/liblsquic/lsquic_enc_sess.h @@ -123,6 +123,12 @@ struct enc_session_funcs_common void (*esf_set_conn) (enc_session_t *, struct lsquic_conn *); + /* Optional. This function gets called after packets are encrypted, + * batched, and are about to be sent. + */ + void + (*esf_flush_encryption) (enc_session_t *); + unsigned esf_tag_len; }; diff --git a/src/liblsquic/lsquic_enc_sess_ietf.c b/src/liblsquic/lsquic_enc_sess_ietf.c index 9adb638fa..9e38c208a 100644 --- a/src/liblsquic/lsquic_enc_sess_ietf.c +++ b/src/liblsquic/lsquic_enc_sess_ietf.c @@ -106,22 +106,26 @@ no_sess_ticket (enum alarm_id alarm_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now); +#define SAMPLE_SZ 16 + typedef void (*gen_hp_mask_f)(struct enc_sess_iquic *, - const struct header_prot *, unsigned rw, - const unsigned char *sample, unsigned char mask[16]); + struct header_prot *, unsigned rw, + const unsigned char *sample, unsigned char *mask, size_t sz); +#define CHACHA20_KEY_LENGTH 32 struct header_prot { - const EVP_CIPHER *hp_cipher; gen_hp_mask_f hp_gen_mask; enum enc_level hp_enc_level; enum { HP_CAN_READ = 1 << 0, HP_CAN_WRITE = 1 << 1, } hp_flags; - unsigned hp_sz; - unsigned char hp_buf[2][EVP_MAX_KEY_LENGTH]; + union { + EVP_CIPHER_CTX cipher_ctx[2]; /* AES */ + unsigned char buf[2][CHACHA20_KEY_LENGTH]; /* ChaCha */ + } hp_u; }; #define header_prot_inited(hp_, rw_) ((hp_)->hp_flags & (1 << (rw_))) @@ -176,20 +180,6 @@ init_crypto_ctx (struct crypto_ctx *crypto_ctx, const EVP_MD *md, } -static void -derive_hp_secrets (struct header_prot *hp, const EVP_MD *md, - const EVP_AEAD *aead, size_t secret_sz, - const unsigned char *client_secret, const unsigned char *server_secret) -{ - hp->hp_sz = EVP_AEAD_key_length(aead); - hp->hp_flags = HP_CAN_READ | HP_CAN_WRITE; - lsquic_qhkdf_expand(md, client_secret, secret_sz, PN_LABEL, PN_LABEL_SZ, - hp->hp_buf[0], hp->hp_sz); - lsquic_qhkdf_expand(md, server_secret, secret_sz, PN_LABEL, PN_LABEL_SZ, - hp->hp_buf[1], hp->hp_sz); -} - - static void cleanup_crypto_ctx (struct crypto_ctx *crypto_ctx) { @@ -201,6 +191,8 @@ cleanup_crypto_ctx (struct crypto_ctx *crypto_ctx) } +#define HP_BATCH_SIZE 8 + struct enc_sess_iquic { struct lsquic_engine_public @@ -275,23 +267,24 @@ struct enc_sess_iquic struct lsquic_alarmset *esi_alset; unsigned esi_max_streams_uni; + unsigned esi_hp_batch_idx; + unsigned esi_hp_batch_packno_len[HP_BATCH_SIZE]; + unsigned esi_hp_batch_packno_off[HP_BATCH_SIZE]; + struct lsquic_packet_out * + esi_hp_batch_packets[HP_BATCH_SIZE]; + unsigned char esi_hp_batch_samples[HP_BATCH_SIZE][SAMPLE_SZ]; }; static void gen_hp_mask_aes (struct enc_sess_iquic *enc_sess, - const struct header_prot *hp, unsigned rw, - const unsigned char *sample, unsigned char mask[EVP_MAX_BLOCK_LENGTH]) + struct header_prot *hp, unsigned rw, + const unsigned char *sample, unsigned char *mask, size_t sz) { - EVP_CIPHER_CTX hp_ctx; int out_len; - EVP_CIPHER_CTX_init(&hp_ctx); - if (EVP_EncryptInit_ex(&hp_ctx, hp->hp_cipher, NULL, hp->hp_buf[rw], 0) - && EVP_EncryptUpdate(&hp_ctx, mask, &out_len, sample, 16)) - { - assert(out_len >= 5); - } + if (EVP_EncryptUpdate(&hp->hp_u.cipher_ctx[rw], mask, &out_len, sample, sz)) + assert(out_len >= (int) sz); else { LSQ_WARN("cannot generate hp mask, error code: %"PRIu32, @@ -299,15 +292,13 @@ gen_hp_mask_aes (struct enc_sess_iquic *enc_sess, enc_sess->esi_conn->cn_if->ci_internal_error(enc_sess->esi_conn, "cannot generate hp mask, error code: %"PRIu32, ERR_get_error()); } - - (void) EVP_CIPHER_CTX_cleanup(&hp_ctx); } static void gen_hp_mask_chacha20 (struct enc_sess_iquic *enc_sess, - const struct header_prot *hp, unsigned rw, - const unsigned char *sample, unsigned char mask[EVP_MAX_BLOCK_LENGTH]) + struct header_prot *hp, unsigned rw, + const unsigned char *sample, unsigned char *mask, size_t sz) { const uint8_t *nonce; uint32_t counter; @@ -319,19 +310,17 @@ gen_hp_mask_chacha20 (struct enc_sess_iquic *enc_sess, #endif nonce = sample + sizeof(counter); CRYPTO_chacha_20(mask, (unsigned char [5]) { 0, 0, 0, 0, 0, }, 5, - hp->hp_buf[rw], nonce, counter); + hp->hp_u.buf[rw], nonce, counter); } static void -apply_hp (struct enc_sess_iquic *enc_sess, - const struct header_prot *hp, - unsigned char *dst, unsigned packno_off, unsigned packno_len) +apply_hp (struct enc_sess_iquic *enc_sess, struct header_prot *hp, + unsigned char *dst, const unsigned char *mask, + unsigned packno_off, unsigned packno_len) { - unsigned char mask[EVP_MAX_BLOCK_LENGTH]; char mask_str[5 * 2 + 1]; - hp->hp_gen_mask(enc_sess, hp, 1, dst + packno_off + 4, mask); LSQ_DEBUG("apply header protection using mask %s", HEXSTR(mask, 5, mask_str)); if (enc_sess->esi_flags & ESI_SEND_QL_BITS) @@ -355,6 +344,64 @@ apply_hp (struct enc_sess_iquic *enc_sess, } +static void +apply_hp_immediately (struct enc_sess_iquic *enc_sess, + struct header_prot *hp, struct lsquic_packet_out *packet_out, + unsigned packno_off, unsigned packno_len) +{ + unsigned char mask[SAMPLE_SZ]; + + hp->hp_gen_mask(enc_sess, hp, 1, + packet_out->po_enc_data + packno_off + 4, mask, SAMPLE_SZ); + apply_hp(enc_sess, hp, packet_out->po_enc_data, mask, packno_off, + packno_len); +#ifndef NDEBUG + packet_out->po_lflags |= POL_HEADER_PROT; +#endif +} + + +static void +flush_hp_batch (struct enc_sess_iquic *enc_sess) +{ + unsigned i; + unsigned char mask[HP_BATCH_SIZE][SAMPLE_SZ]; + + enc_sess->esi_hp.hp_gen_mask(enc_sess, &enc_sess->esi_hp, 1, + (unsigned char *) enc_sess->esi_hp_batch_samples, + (unsigned char *) mask, + enc_sess->esi_hp_batch_idx * SAMPLE_SZ); + for (i = 0; i < enc_sess->esi_hp_batch_idx; ++i) + { + apply_hp(enc_sess, &enc_sess->esi_hp, + enc_sess->esi_hp_batch_packets[i]->po_enc_data, + mask[i], + enc_sess->esi_hp_batch_packno_off[i], + enc_sess->esi_hp_batch_packno_len[i]); +#ifndef NDEBUG + enc_sess->esi_hp_batch_packets[i]->po_lflags |= POL_HEADER_PROT; +#endif + } + enc_sess->esi_hp_batch_idx = 0; +} + + +static void +apply_hp_batch (struct enc_sess_iquic *enc_sess, + struct header_prot *hp, struct lsquic_packet_out *packet_out, + unsigned packno_off, unsigned packno_len) +{ + memcpy(enc_sess->esi_hp_batch_samples[enc_sess->esi_hp_batch_idx], + packet_out->po_enc_data + packno_off + 4, SAMPLE_SZ); + enc_sess->esi_hp_batch_packno_off[enc_sess->esi_hp_batch_idx] = packno_off; + enc_sess->esi_hp_batch_packno_len[enc_sess->esi_hp_batch_idx] = packno_len; + enc_sess->esi_hp_batch_packets[enc_sess->esi_hp_batch_idx] = packet_out; + ++enc_sess->esi_hp_batch_idx; + if (enc_sess->esi_hp_batch_idx == HP_BATCH_SIZE) + flush_hp_batch(enc_sess); +} + + static lsquic_packno_t decode_packno (lsquic_packno_t max_packno, lsquic_packno_t packno, unsigned shift) @@ -382,17 +429,17 @@ decode_packno (lsquic_packno_t max_packno, lsquic_packno_t packno, static lsquic_packno_t strip_hp (struct enc_sess_iquic *enc_sess, - const struct header_prot *hp, + struct header_prot *hp, const unsigned char *iv, unsigned char *dst, unsigned packno_off, unsigned *packno_len) { enum packnum_space pns; lsquic_packno_t packno; unsigned shift; - unsigned char mask[EVP_MAX_BLOCK_LENGTH]; + unsigned char mask[SAMPLE_SZ]; char mask_str[5 * 2 + 1]; - hp->hp_gen_mask(enc_sess, hp, 0, iv, mask); + hp->hp_gen_mask(enc_sess, hp, 0, iv, mask, SAMPLE_SZ); LSQ_DEBUG("strip header protection using mask %s", HEXSTR(mask, 5, mask_str)); if (enc_sess->esi_flags & ESI_RECV_QL_BITS) @@ -892,30 +939,23 @@ log_crypto_pair (const struct enc_sess_iquic *enc_sess, } -static void -log_hp (const struct enc_sess_iquic *enc_sess, - const struct header_prot *hp, const char *name) -{ - char hexbuf[EVP_MAX_MD_SIZE * 2 + 1]; - LSQ_DEBUG("read %s hp: %s", name, - HEXSTR(hp->hp_buf[0], hp->hp_sz, hexbuf)); - LSQ_DEBUG("write %s hp: %s", name, - HEXSTR(hp->hp_buf[1], hp->hp_sz, hexbuf)); -} - - /* [draft-ietf-quic-tls-12] Section 5.3.2 */ static int setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid) { const EVP_MD *const md = EVP_sha256(); const EVP_AEAD *const aead = EVP_aead_aes_128_gcm(); + /* [draft-ietf-quic-tls-12] Section 5.6.1: AEAD_AES_128_GCM implies + * 128-bit AES-CTR. + */ + const EVP_CIPHER *const cipher = EVP_aes_128_ecb(); struct crypto_ctx_pair *pair; struct header_prot *hp; - size_t hsk_secret_sz; - unsigned cliser; + size_t hsk_secret_sz, key_len; + unsigned cliser, i; unsigned char hsk_secret[EVP_MAX_MD_SIZE]; unsigned char secret[2][SHA256_DIGEST_LENGTH]; /* client, server */ + unsigned char key[2][EVP_MAX_KEY_LENGTH]; char hexbuf[EVP_MAX_MD_SIZE * 2 + 1]; if (!enc_sess->esi_hsk_pairs) @@ -965,18 +1005,29 @@ setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid) sizeof(secret[1]), rw2dir(cliser))) goto err; - /* [draft-ietf-quic-tls-12] Section 5.6.1: AEAD_AES_128_GCM implies - * 128-bit AES-CTR. - */ - hp->hp_cipher = EVP_aes_128_ecb(); hp->hp_gen_mask = gen_hp_mask_aes; hp->hp_enc_level = ENC_LEV_CLEAR; - derive_hp_secrets(hp, md, aead, sizeof(secret[0]), secret[!cliser], secret[cliser]); - + key_len = EVP_AEAD_key_length(aead); + lsquic_qhkdf_expand(md, secret[!cliser], sizeof(secret[0]), PN_LABEL, + PN_LABEL_SZ, key[0], key_len); + lsquic_qhkdf_expand(md, secret[cliser], sizeof(secret[0]), PN_LABEL, + PN_LABEL_SZ, key[1], key_len); if (enc_sess->esi_flags & ESI_LOG_SECRETS) { log_crypto_pair(enc_sess, pair, "handshake"); - log_hp(enc_sess, hp, "handshake"); + LSQ_DEBUG("read handshake hp: %s", HEXSTR(key[0], key_len, hexbuf)); + LSQ_DEBUG("write handshake hp: %s", HEXSTR(key[1], key_len, hexbuf)); + } + for (i = 0; i < 2; ++i) + { + EVP_CIPHER_CTX_init(&hp->hp_u.cipher_ctx[i]); + if (EVP_EncryptInit_ex(&hp->hp_u.cipher_ctx[i], cipher, NULL, key[i], 0)) + hp->hp_flags |= 1 << i; + else + { + LSQ_ERROR("%s: cannot initialize cipher %u", __func__, i); + goto err; + } } return 0; @@ -988,10 +1039,23 @@ setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid) } +static void +cleanup_hp (struct header_prot *hp) +{ + unsigned rw; + + if (hp->hp_gen_mask == gen_hp_mask_aes) + for (rw = 0; rw < 2; ++rw) + if (hp->hp_flags & (1 << rw)) + (void) EVP_CIPHER_CTX_cleanup(&hp->hp_u.cipher_ctx[rw]); +} + + static void free_handshake_keys (struct enc_sess_iquic *enc_sess) { struct crypto_ctx_pair *pair; + unsigned i; if (enc_sess->esi_hsk_pairs) { @@ -1004,6 +1068,8 @@ free_handshake_keys (struct enc_sess_iquic *enc_sess) } free(enc_sess->esi_hsk_pairs); enc_sess->esi_hsk_pairs = NULL; + for (i = 0; i < N_HSK_PAIRS; ++i) + cleanup_hp(&enc_sess->esi_hsk_hps[i]); free(enc_sess->esi_hsk_hps); enc_sess->esi_hsk_hps = NULL; } @@ -1776,6 +1842,7 @@ iquic_esfi_destroy (enc_session_t *enc_session_p) SSL_free(enc_sess->esi_ssl); free_handshake_keys(enc_sess); + cleanup_hp(&enc_sess->esi_hp); free(enc_sess->esi_zero_rtt_buf); free(enc_sess->esi_hostname); @@ -1813,7 +1880,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p, unsigned char *dst; const struct crypto_ctx_pair *pair; const struct crypto_ctx *crypto_ctx; - const struct header_prot *hp; + struct header_prot *hp; enum enc_level enc_level; unsigned char nonce_buf[ sizeof(crypto_ctx->yk_iv_buf) + 8 ]; unsigned char *nonce, *begin_xor; @@ -1896,6 +1963,9 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p, #endif *((uint64_t *) begin_xor) ^= packno; + /* TODO: have this call return packno_off and packno_len to avoid + * another function call. + */ header_sz = lconn->cn_pf->pf_gen_reg_pkt_header(lconn, packet_out, dst, dst_sz); if (header_sz < 0) @@ -1927,7 +1997,6 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p, const unsigned sample_off = packno_off + 4; assert(sample_off + IQUIC_TAG_LEN <= dst_sz); #endif - apply_hp(enc_sess, hp, dst, packno_off, packno_len); packet_out->po_enc_data = dst; packet_out->po_enc_data_sz = dst_sz; @@ -1936,6 +2005,12 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p, packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT); lsquic_packet_out_set_enc_level(packet_out, enc_level); lsquic_packet_out_set_kp(packet_out, enc_sess->esi_key_phase); + + if (enc_level == ENC_LEV_FORW && hp->hp_gen_mask != gen_hp_mask_chacha20) + apply_hp_batch(enc_sess, hp, packet_out, packno_off, packno_len); + else + apply_hp_immediately(enc_sess, hp, packet_out, packno_off, packno_len); + return ENCPA_OK; err: @@ -1945,6 +2020,20 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p, } +static void +iquic_esf_flush_encryption (enc_session_t *enc_session_p) +{ + struct enc_sess_iquic *const enc_sess = enc_session_p; + + if (enc_sess->esi_hp_batch_idx) + { + LSQ_DEBUG("flush header protection application, count: %u", + enc_sess->esi_hp_batch_idx); + flush_hp_batch(enc_sess); + } +} + + static struct ku_label { const char *str; @@ -1966,7 +2055,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p, struct enc_sess_iquic *const enc_sess = enc_session_p; unsigned char *dst; struct crypto_ctx_pair *pair; - const struct header_prot *hp; + struct header_prot *hp; struct crypto_ctx *crypto_ctx = NULL; unsigned char nonce_buf[ sizeof(crypto_ctx->yk_iv_buf) + 8 ]; unsigned char *nonce, *begin_xor; @@ -2454,6 +2543,25 @@ const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1 = const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1 = +{ + .esf_encrypt_packet = iquic_esf_encrypt_packet, + .esf_decrypt_packet = iquic_esf_decrypt_packet, + .esf_flush_encryption= iquic_esf_flush_encryption, + .esf_global_cleanup = iquic_esf_global_cleanup, + .esf_global_init = iquic_esf_global_init, + .esf_tag_len = IQUIC_TAG_LEN, + .esf_get_server_cert_chain + = iquic_esf_get_server_cert_chain, + .esf_cipher = iquic_esf_cipher, + .esf_keysize = iquic_esf_keysize, + .esf_alg_keysize = iquic_esf_alg_keysize, + .esf_is_zero_rtt_enabled = iquic_esf_zero_rtt_enabled, + .esf_set_conn = iquic_esf_set_conn, +}; + + +static +const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1_no_flush = { .esf_encrypt_packet = iquic_esf_encrypt_packet, .esf_decrypt_packet = iquic_esf_decrypt_packet, @@ -2553,7 +2661,9 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level, int have_alpn; const unsigned char *alpn; unsigned alpn_len; + size_t key_len; const enum enc_level enc_level = (enum enc_level) level; + unsigned char key[EVP_MAX_KEY_LENGTH]; char errbuf[ERR_ERROR_STRING_BUF_LEN]; #define hexbuf errbuf @@ -2602,6 +2712,13 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level, memcpy(enc_sess->esi_traffic_secrets[rw], secret, secret_len); enc_sess->esi_md = crypa.md; enc_sess->esi_aead = crypa.aead; + if (!(hp->hp_flags & (HP_CAN_READ|HP_CAN_WRITE)) + && crypa.aead == EVP_aead_chacha20_poly1305()) + { + LSQ_DEBUG("turn off header protection batching (chacha not " + "supported)"); + enc_sess->esi_conn->cn_esf_c = &lsquic_enc_session_common_ietf_v1_no_flush; + } } pair->ykp_thresh = IQUIC_INVALID_PACKNO; @@ -2620,25 +2737,36 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level, /* Sanity check that the two sides end up with the same header * protection logic, as they should. */ - assert(hp->hp_cipher == crypa.hp); assert(hp->hp_gen_mask == crypa.gen_hp_mask); } else { hp->hp_enc_level = enc_level; - hp->hp_cipher = crypa.hp; hp->hp_gen_mask = crypa.gen_hp_mask; - hp->hp_sz = EVP_AEAD_key_length(crypa.aead); } - lsquic_qhkdf_expand(crypa.md, secret, secret_len, PN_LABEL, PN_LABEL_SZ, - hp->hp_buf[rw], hp->hp_sz); + key_len = EVP_AEAD_key_length(crypa.aead); + if (hp->hp_gen_mask == gen_hp_mask_aes) + { + lsquic_qhkdf_expand(crypa.md, secret, secret_len, PN_LABEL, PN_LABEL_SZ, + key, key_len); + EVP_CIPHER_CTX_init(&hp->hp_u.cipher_ctx[rw]); + if (!EVP_EncryptInit_ex(&hp->hp_u.cipher_ctx[rw], crypa.hp, NULL, key, 0)) + { + LSQ_ERROR("cannot initialize cipher on level %u", enc_level); + goto err; + } + } + else + lsquic_qhkdf_expand(crypa.md, secret, secret_len, PN_LABEL, PN_LABEL_SZ, + hp->hp_u.buf[rw], key_len); hp->hp_flags |= 1 << rw; if (enc_sess->esi_flags & ESI_LOG_SECRETS) { log_crypto_ctx(enc_sess, &pair->ykp_ctx[rw], "new", rw); LSQ_DEBUG("%s hp: %s", rw2str[rw], - HEXSTR(hp->hp_buf[rw], hp->hp_sz, hexbuf)); + HEXSTR(hp->hp_gen_mask == gen_hp_mask_aes ? key : hp->hp_u.buf[rw], + key_len, hexbuf)); } return 1; diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 3a4a28fb5..127bc2b17 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -2160,6 +2160,20 @@ close_conn_on_send_error (struct lsquic_engine *engine, } +static void +apply_hp (struct conns_out_iter *iter) +{ + struct lsquic_conn *conn; + + TAILQ_FOREACH(conn, &iter->coi_active_list, cn_next_out) + if (conn->cn_esf_c->esf_flush_encryption && conn->cn_enc_session) + conn->cn_esf_c->esf_flush_encryption(conn->cn_enc_session); + TAILQ_FOREACH(conn, &iter->coi_inactive_list, cn_next_out) + if (conn->cn_esf_c->esf_flush_encryption && conn->cn_enc_session) + conn->cn_esf_c->esf_flush_encryption(conn->cn_enc_session); +} + + static unsigned send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx, unsigned n_to_send) @@ -2171,6 +2185,7 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx, CONST_BATCH struct out_batch *const batch = sb_ctx->batch; struct lsquic_packet_out *CONST_BATCH *packet_out, *CONST_BATCH *end; + apply_hp(sb_ctx->conns_iter); #if CAN_LOSE_PACKETS if (engine->flags & ENG_LOSE_PACKETS) lose_matching_packets(engine, batch, n_to_send); diff --git a/src/liblsquic/lsquic_full_conn_ietf.c b/src/liblsquic/lsquic_full_conn_ietf.c index 105bb42a1..36342e003 100644 --- a/src/liblsquic/lsquic_full_conn_ietf.c +++ b/src/liblsquic/lsquic_full_conn_ietf.c @@ -6608,6 +6608,9 @@ static void ietf_full_conn_ci_packet_not_sent (struct lsquic_conn *lconn, struct lsquic_packet_out *packet_out) { +#ifndef NDEBUG + assert(packet_out->po_lflags & POL_HEADER_PROT); +#endif struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn; lsquic_send_ctl_delayed_one(&conn->ifc_send_ctl, packet_out); } @@ -6620,6 +6623,9 @@ static void pre_hsk_packet_sent_or_delayed (struct ietf_full_conn *conn, const struct lsquic_packet_out *packet_out) { +#ifndef NDEBUG + assert(packet_out->po_lflags & POL_HEADER_PROT); +#endif /* Once IFC_IGNORE_INIT is set, the pre-hsk wrapper is removed: */ assert(!(conn->ifc_flags & IFC_IGNORE_INIT)); --conn->ifc_u.cli.ifcli_packets_out; diff --git a/src/liblsquic/lsquic_packet_out.h b/src/liblsquic/lsquic_packet_out.h index d64953f01..5a32fdf97 100644 --- a/src/liblsquic/lsquic_packet_out.h +++ b/src/liblsquic/lsquic_packet_out.h @@ -146,6 +146,9 @@ typedef struct lsquic_packet_out POL_LOG_QL_BITS = 1 << 6, POL_SQUARE_BIT = 1 << 7, POL_LOSS_BIT = 1 << 8, +#ifndef NDEBUG + POL_HEADER_PROT = 1 << 9, /* Header protection applied */ +#endif } po_lflags:16; unsigned char *po_data; diff --git a/src/liblsquic/lsquic_stream.c b/src/liblsquic/lsquic_stream.c index 2d936257a..481bd7eb0 100644 --- a/src/liblsquic/lsquic_stream.c +++ b/src/liblsquic/lsquic_stream.c @@ -783,10 +783,13 @@ static int stream_readable_discard (struct lsquic_stream *stream) { struct data_frame *data_frame; + uint64_t toread; while ((data_frame = stream->data_in->di_if->di_get_frame( stream->data_in, stream->read_offset))) { + toread = data_frame->df_size - data_frame->df_read_off; + stream->read_offset += toread; data_frame->df_read_off = data_frame->df_size; stream->data_in->di_if->di_frame_done(stream->data_in, data_frame); }