diff --git a/CHANGELOG b/CHANGELOG index 471c520..897b242 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +2020-09-29 + - 2.21.0 + - [FEATURE] QUIC and HTTP/3 Internet Draft 31 support. + - [API] Let user generate Souce Connection IDs. + - [FEATURE] Allow building lsquic as shared library. + - [OPTIMIZATION] Receive history: use a single contiguous memory + block for everything. + - Deprecate QUIC versions ID-27 and ID-30. + 2020-09-25 - 2.20.2 - [BUGFIX] Memory leak: free pushed promise when refcnt is zero. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index c0c5ea2..6b62bf9 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -1,6 +1,6 @@ # Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. In addition to the LiteSpeed QUIC Team, the following people contributed -to the LiteSpeed Client Library: +to the LiteSpeed QUIC and HTTP/3 Library: - Brian Prodoehl -- Docker file - Amol Desphande -- Windows support @@ -10,6 +10,8 @@ to the LiteSpeed Client Library: - Omar Roth -- Alpine Linux build and Crystal language bindings - initlife (?) -- XCode build - Rahul Jadhav -- Android support + - Victor Stewart -- Generate SCIDs API (connection ID steering) + - Aaron France -- Shared library support and Lisp bindings Thank you! diff --git a/README.md b/README.md index c5927c1..f91bfa1 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,12 @@ Description LiteSpeed QUIC (LSQUIC) Library is an open-source implementation of QUIC and HTTP/3 functionality for servers and clients. Most of the code in this distribution is used in our own products: LiteSpeed Web Server, LiteSpeed ADC, -and OpenLiteSpeed. We think it is free of major problems. Nevertheless, do -not hesitate to report bugs back to us. Even better, send us fixes and -improvements! +and OpenLiteSpeed. Do not hesitate to report bugs back to us. Even better, +send us fixes and improvements! Currently supported QUIC versions are Q043, Q046, Q050, ID-27, ID-28, ID-29, -and ID-30. Support for newer versions will be added soon after they are -released. +ID-30, and ID-31. Support for newer versions will be added soon after they +are released. Documentation ------------- diff --git a/docs/apiref.rst b/docs/apiref.rst index bce76eb..8aeb180 100644 --- a/docs/apiref.rst +++ b/docs/apiref.rst @@ -48,7 +48,7 @@ developed by the IETF. Both types are included in a single enum: .. member:: LSQVER_ID27 - IETF QUIC version ID (Internet-Draft) 27 + IETF QUIC version ID (Internet-Draft) 27; this version is deprecated. .. member:: LSQVER_ID28 @@ -58,10 +58,14 @@ developed by the IETF. Both types are included in a single enum: IETF QUIC version ID 29 - .. member:: LSQVER_ID30 + .. member:: LSQVER_ID30; this version is deprecated. IETF QUIC version ID 30 + .. member:: LSQVER_ID31 + + IETF QUIC version ID 31 + .. member:: N_LSQVER Special value indicating the number of versions in the enum. It @@ -294,6 +298,10 @@ optional members. The optional ALPN string is used by the client if :macro:`LSENG_HTTP` is not set. + .. member:: void (*ea_generate_scid)(lsquic_conn_t *, lsquic_cid_t *, unsigned) + + Optional interface to control the creation of connection IDs. + .. _apiref-engine-settings: Engine Settings @@ -786,7 +794,7 @@ settings structure: .. member:: unsigned es_mtu_probe_timer This value specifies how long the DPLPMTUD probe timer is, in - milliseconds. `[draft-ietf-tsvwg-datagram-plpmtud-22] `_ says: + milliseconds. :rfc:`8899` says: PROBE_TIMER: The PROBE_TIMER is configured to expire after a period longer than the maximum time to receive an acknowledgment to a @@ -2169,7 +2177,6 @@ The following log modules are defined: - *qlog*: QLOG output. At the moment, it is out of date. - *qpack-dec*: QPACK decoder. - *qpack-enc*: QPACK encoder. -- *rechist*: Receive history. - *sendctl*: Send controller. - *sfcw*: Stream flow control window. - *spi*: Stream priority iterator. diff --git a/docs/conf.py b/docs/conf.py index 060c74d..47ac4f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies' author = u'LiteSpeed Technologies' # The short X.Y version -version = u'2.20' +version = u'2.21' # The full version, including alpha/beta/rc tags -release = u'2.20.2' +release = u'2.21.0' # -- General configuration --------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index b6542ca..45a20e7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,7 +17,7 @@ Most of the code in this distribution has been used in our own products since 2017. Currently supported QUIC versions are Q043, Q046, Q050, ID-27, ID-28, -ID-29, and ID-30. +ID-29, ID-30, and ID-31. Support for newer versions will be added soon after they are released. LSQUIC is licensed under the `MIT License`_; see LICENSE in the source diff --git a/include/lsquic.h b/include/lsquic.h index 82d48af..1f20b74 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -24,8 +24,8 @@ extern "C" { #endif #define LSQUIC_MAJOR_VERSION 2 -#define LSQUIC_MINOR_VERSION 20 -#define LSQUIC_PATCH_VERSION 2 +#define LSQUIC_MINOR_VERSION 21 +#define LSQUIC_PATCH_VERSION 0 /** * Engine flags: @@ -96,6 +96,11 @@ enum lsquic_version */ LSQVER_ID30, + /** + * IETF QUIC Draft-31 + */ + LSQVER_ID31, + /** * Special version to trigger version negotiation. * [draft-ietf-quic-transport-11], Section 3. @@ -107,7 +112,7 @@ enum lsquic_version /** * We currently support versions 43, 46, 50, Draft-27, Draft-28, Draft-29, - * and Draft-30. + * Draft-30, and Draft-31. * @see lsquic_version */ #define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1) @@ -120,15 +125,18 @@ enum lsquic_version #define LSQUIC_EXPERIMENTAL_VERSIONS ( \ (1 << LSQVER_VERNEG) | LSQUIC_EXPERIMENTAL_Q098) -#define LSQUIC_DEPRECATED_VERSIONS (1 << LSQVER_ID28) +#define LSQUIC_DEPRECATED_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \ + | (1 << LSQVER_ID30)) #define LSQUIC_GQUIC_HEADER_VERSIONS (1 << LSQVER_043) #define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \ - | (1 << LSQVER_ID29) | (1 << LSQVER_ID30) | (1 << LSQVER_VERNEG)) + | (1 << LSQVER_ID29) | (1 << LSQVER_ID30) \ + | (1 << LSQVER_ID31) | (1 << LSQVER_VERNEG)) #define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \ - | (1 << LSQVER_ID29) | (1 << LSQVER_ID30) | (1 << LSQVER_VERNEG)) + | (1 << LSQVER_ID29) | (1 << LSQVER_ID30) \ + | (1 << LSQVER_ID31) | (1 << LSQVER_VERNEG)) enum lsquic_hsk_status { @@ -1186,12 +1194,6 @@ struct lsquic_engine_api */ const struct lsquic_packout_mem_if *ea_pmi; void *ea_pmi_ctx; - - /** - * Optional interface to control the creation of connection IDs - */ - void (*es_generate_scid)(lsquic_conn_t *, lsquic_cid_t *, unsigned); - /** * Optional interface to report new and old source connection IDs. */ @@ -1240,6 +1242,12 @@ struct lsquic_engine_api * is not set. */ const char *ea_alpn; + + /** + * Optional interface to control the creation of connection IDs + */ + void (*ea_generate_scid)(lsquic_conn_t *, + lsquic_cid_t *, unsigned); }; /** diff --git a/src/liblsquic/CMakeLists.txt b/src/liblsquic/CMakeLists.txt index 88921be..a8ee083 100644 --- a/src/liblsquic/CMakeLists.txt +++ b/src/liblsquic/CMakeLists.txt @@ -50,7 +50,6 @@ SET(lsquic_STAT_SRCS lsquic_packet_in.c lsquic_packet_out.c lsquic_packet_resize.c - lsquic_packints.c lsquic_parse_Q046.c lsquic_parse_Q050.c lsquic_parse_common.c @@ -115,3 +114,4 @@ IF(LSQUIC_SHARED_LIB) ELSE() add_library(lsquic STATIC ${lsquic_STAT_SRCS}) ENDIF() + diff --git a/src/liblsquic/Makefile.am b/src/liblsquic/Makefile.am index 8d8b1d3..75de528 100644 --- a/src/liblsquic/Makefile.am +++ b/src/liblsquic/Makefile.am @@ -68,7 +68,6 @@ liblsquic_a_SOURCES = ls-qpack/lsqpack.c \ lsquic_qdec_hdl.c \ lsquic_qenc_hdl.c \ lsquic_qlog.c \ - lsquic_rechist.c \ lsquic_rtt.c \ lsquic_send_ctl.c \ lsquic_senhist.c \ diff --git a/src/liblsquic/lsquic_conn.c b/src/liblsquic/lsquic_conn.c index 80f576e..8059e46 100644 --- a/src/liblsquic/lsquic_conn.c +++ b/src/liblsquic/lsquic_conn.c @@ -238,6 +238,14 @@ lsquic_generate_cid (lsquic_cid_t *cid, size_t len) } +void +lsquic_generate_scid (struct lsquic_conn *lconn, lsquic_cid_t *scid, + unsigned len) +{ + lsquic_generate_cid(scid, len); +} + + void lsquic_generate_cid_gquic (lsquic_cid_t *cid) { diff --git a/src/liblsquic/lsquic_conn.h b/src/liblsquic/lsquic_conn.h index b7f9374..b0d0bd1 100644 --- a/src/liblsquic/lsquic_conn.h +++ b/src/liblsquic/lsquic_conn.h @@ -386,6 +386,10 @@ lsquic_generate_cid (lsquic_cid_t *cid, size_t len); void lsquic_generate_cid_gquic (lsquic_cid_t *cid); +void +lsquic_generate_scid (struct lsquic_conn *lconn, lsquic_cid_t *scid, + unsigned len); + void lsquic_conn_retire_cid (lsquic_conn_t *lconn); diff --git a/src/liblsquic/lsquic_di_hash.c b/src/liblsquic/lsquic_di_hash.c index 2cf92b3..b1b44ef 100644 --- a/src/liblsquic/lsquic_di_hash.c +++ b/src/liblsquic/lsquic_di_hash.c @@ -6,8 +6,7 @@ * which makes it a good choice when we have a lot of stream frames * coming in. * - * Another difference is that it does not check for frame overlap, which - * is something that is present in Chrome, but it is not required by QUIC. + * Another difference is that incoming STREAM frames are allowed to overlap. */ diff --git a/src/liblsquic/lsquic_enc_sess.h b/src/liblsquic/lsquic_enc_sess.h index 200eba4..b0b8691 100644 --- a/src/liblsquic/lsquic_enc_sess.h +++ b/src/liblsquic/lsquic_enc_sess.h @@ -343,6 +343,7 @@ extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1; ver == LSQVER_ID28 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_ID29 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_ID30 ? &lsquic_enc_session_common_ietf_v1 : \ + ver == LSQVER_ID31 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \ &lsquic_enc_session_common_gquic_1 ) diff --git a/src/liblsquic/lsquic_enc_sess_ietf.c b/src/liblsquic/lsquic_enc_sess_ietf.c index 59d5c7e..9f4718a 100644 --- a/src/liblsquic/lsquic_enc_sess_ietf.c +++ b/src/liblsquic/lsquic_enc_sess_ietf.c @@ -75,7 +75,8 @@ static const struct alpn_map { { LSQVER_ID28, (unsigned char *) "\x05h3-28", }, { LSQVER_ID29, (unsigned char *) "\x05h3-29", }, { LSQVER_ID30, (unsigned char *) "\x05h3-30", }, - { LSQVER_VERNEG, (unsigned char *) "\x05h3-30", }, + { LSQVER_ID31, (unsigned char *) "\x05h3-31", }, + { LSQVER_VERNEG, (unsigned char *) "\x05h3-31", }, }; struct enc_sess_iquic; @@ -554,12 +555,9 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf, cce->cce_seqno = seqno + 1; cce->cce_flags = CCE_SEQNO; - if (enc_sess->esi_enpub->enp_generate_scid) - enc_sess->esi_enpub->enp_generate_scid(enc_sess->esi_conn, &cce->cce_cid, enc_sess->esi_enpub->enp_settings.es_scid_len); - else - lsquic_generate_cid(&cce->cce_cid, enc_sess->esi_enpub->enp_settings.es_scid_len); - - + enc_sess->esi_enpub->enp_generate_scid(enc_sess->esi_conn, + &cce->cce_cid, enc_sess->esi_enpub->enp_settings.es_scid_len); + /* Don't add to hash: migration must not start until *after* * handshake is complete. */ diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index a5303e8..ccd8a9b 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -577,7 +577,10 @@ lsquic_engine_new (unsigned flags, engine->pub.enp_cert_lu_ctx = api->ea_cert_lu_ctx; engine->pub.enp_get_ssl_ctx = api->ea_get_ssl_ctx; - engine->pub.enp_generate_scid = api->es_generate_scid; + if (api->ea_generate_scid) + engine->pub.enp_generate_scid = api->ea_generate_scid; + else + engine->pub.enp_generate_scid = lsquic_generate_scid; if (api->ea_shi) { diff --git a/src/liblsquic/lsquic_engine_public.h b/src/liblsquic/lsquic_engine_public.h index 7a63d4e..57cee75 100644 --- a/src/liblsquic/lsquic_engine_public.h +++ b/src/liblsquic/lsquic_engine_public.h @@ -10,6 +10,7 @@ #ifndef LSQUIC_ENGINE_PUBLIC_H #define LSQUIC_ENGINE_PUBLIC_H 1 +struct lsquic_cid; struct lsquic_conn; struct lsquic_engine; struct stack_st_X509; @@ -44,7 +45,8 @@ struct lsquic_engine_public { void *enp_stream_if_ctx; const struct lsquic_hset_if *enp_hsi_if; void *enp_hsi_ctx; - void (*enp_generate_scid)(lsquic_conn_t *, lsquic_cid_t *, unsigned); + void (*enp_generate_scid)(struct lsquic_conn *, + struct lsquic_cid *, unsigned); int (*enp_verify_cert)(void *verify_ctx, struct stack_st_X509 *chain); void *enp_verify_ctx; diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index 96e63ba..92462ef 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -660,7 +660,7 @@ new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub, conn->fc_pub.all_streams = lsquic_hash_create(); if (!conn->fc_pub.all_streams) goto cleanup_on_error; - lsquic_rechist_init(&conn->fc_rechist, &conn->fc_conn, 0); + lsquic_rechist_init(&conn->fc_rechist, 0); if (conn->fc_flags & FC_HTTP) { conn->fc_pub.u.gquic.hs = lsquic_headers_stream_new( @@ -4418,7 +4418,7 @@ lsquic_gquic_full_conn_srej (struct lsquic_conn *lconn) /* Reset receive history */ lsquic_rechist_cleanup(&conn->fc_rechist); - lsquic_rechist_init(&conn->fc_rechist, &conn->fc_conn, 0); + lsquic_rechist_init(&conn->fc_rechist, 0); /* Reset send controller state */ lsquic_send_ctl_cleanup(&conn->fc_send_ctl); diff --git a/src/liblsquic/lsquic_full_conn_ietf.c b/src/liblsquic/lsquic_full_conn_ietf.c index 7769dbe..53fc71f 100644 --- a/src/liblsquic/lsquic_full_conn_ietf.c +++ b/src/liblsquic/lsquic_full_conn_ietf.c @@ -1086,12 +1086,8 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn, } if (enpub->enp_settings.es_scid_len) - { - if (enpub->enp_generate_scid) - enpub->enp_generate_scid(lconn, &cce->cce_cid, enpub->enp_settings.es_scid_len); - else - lsquic_generate_cid(&cce->cce_cid, enpub->enp_settings.es_scid_len); - } + enpub->enp_generate_scid(lconn, &cce->cce_cid, + enpub->enp_settings.es_scid_len); cce->cce_seqno = conn->ifc_scid_seqno++; cce->cce_flags |= CCE_SEQNO | flags; @@ -1182,9 +1178,9 @@ ietf_full_conn_init (struct ietf_full_conn *conn, lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_PATH_CHAL_3, path_chal_alarm_expired, conn); lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_BLOCKED_KA, blocked_ka_alarm_expired, conn); lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_MTU_PROBE, mtu_probe_alarm_expired, conn); - lsquic_rechist_init(&conn->ifc_rechist[PNS_INIT], &conn->ifc_conn, 1); - lsquic_rechist_init(&conn->ifc_rechist[PNS_HSK], &conn->ifc_conn, 1); - lsquic_rechist_init(&conn->ifc_rechist[PNS_APP], &conn->ifc_conn, 1); + lsquic_rechist_init(&conn->ifc_rechist[PNS_INIT], 1); + lsquic_rechist_init(&conn->ifc_rechist[PNS_HSK], 1); + lsquic_rechist_init(&conn->ifc_rechist[PNS_APP], 1); lsquic_send_ctl_init(&conn->ifc_send_ctl, &conn->ifc_alset, enpub, flags & IFC_SERVER ? &server_ver_neg : &conn->ifc_u.cli.ifcli_ver_neg, &conn->ifc_pub, SC_IETF|SC_NSTP|(ecn ? SC_ECN : 0)); @@ -6734,7 +6730,7 @@ process_regular_packet (struct ietf_full_conn *conn, enum received_st st; enum dec_packin dec_packin; enum was_missing was_missing; - unsigned n_rechist_packets; + int is_rechist_empty; unsigned char saved_path_id; if (HETY_RETRY == packet_in->pi_header_type) @@ -6837,7 +6833,7 @@ process_regular_packet (struct ietf_full_conn *conn, EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in); - n_rechist_packets = lsquic_rechist_n_packets(&conn->ifc_rechist[pns]); + is_rechist_empty = lsquic_rechist_is_empty(&conn->ifc_rechist[pns]); st = lsquic_rechist_received(&conn->ifc_rechist[pns], packet_in->pi_packno, packet_in->pi_received); switch (st) { @@ -6882,7 +6878,7 @@ process_regular_packet (struct ietf_full_conn *conn, if (packet_in->pi_packno > conn->ifc_max_ackable_packno_in) { was_missing = (enum was_missing) /* WM_MAX_GAP is 1 */ - n_rechist_packets /* Don't count very first packno */ + !is_rechist_empty /* Don't count very first packno */ && conn->ifc_max_ackable_packno_in + 1 < packet_in->pi_packno && holes_after(&conn->ifc_rechist[PNS_APP], @@ -8641,6 +8637,7 @@ hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream) break; case (0 << 8) | LSQVER_ID29: case (0 << 8) | LSQVER_ID30: + case (0 << 8) | LSQVER_ID31: callbacks = &hcsi_callbacks_client_29; break; default: @@ -8648,6 +8645,7 @@ hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream) /* fallthru */ case (1 << 8) | LSQVER_ID29: case (1 << 8) | LSQVER_ID30: + case (1 << 8) | LSQVER_ID31: callbacks = &hcsi_callbacks_server_29; break; } diff --git a/src/liblsquic/lsquic_ietf.h b/src/liblsquic/lsquic_ietf.h index d25b789..18485b9 100644 --- a/src/liblsquic/lsquic_ietf.h +++ b/src/liblsquic/lsquic_ietf.h @@ -4,7 +4,7 @@ /* Things specific to the IETF version of QUIC that do not fit anywhere else */ -/* [draft-ietf-quic-transport-28] Section 20 */ +/* [draft-ietf-quic-transport-31] Section 20 */ enum trans_error_code { TEC_NO_ERROR = 0x0, @@ -21,6 +21,8 @@ enum trans_error_code TEC_INVALID_TOKEN = 0xB, TEC_APPLICATION_ERROR = 0xC, TEC_CRYPTO_BUFFER_EXCEEDED = 0xD, + TEC_KEY_UPDATE_ERROR = 0xE, + TEC_AEAD_LIMIT_REACHED = 0xF, }; /* Must be at least two */ diff --git a/src/liblsquic/lsquic_logger.c b/src/liblsquic/lsquic_logger.c index fcec7a2..282a8d2 100644 --- a/src/liblsquic/lsquic_logger.c +++ b/src/liblsquic/lsquic_logger.c @@ -60,7 +60,6 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = { [LSQLM_EVENT] = LSQ_LOG_WARN, [LSQLM_ENGINE] = LSQ_LOG_WARN, [LSQLM_CONN] = LSQ_LOG_WARN, - [LSQLM_RECHIST] = LSQ_LOG_WARN, [LSQLM_STREAM] = LSQ_LOG_WARN, [LSQLM_PARSE] = LSQ_LOG_WARN, [LSQLM_CFCW] = LSQ_LOG_WARN, @@ -104,7 +103,6 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = { [LSQLM_EVENT] = "event", [LSQLM_ENGINE] = "engine", [LSQLM_CONN] = "conn", - [LSQLM_RECHIST] = "rechist", [LSQLM_STREAM] = "stream", [LSQLM_PARSE] = "parse", [LSQLM_CFCW] = "cfcw", diff --git a/src/liblsquic/lsquic_logger.h b/src/liblsquic/lsquic_logger.h index 5186b18..4a1b3d7 100644 --- a/src/liblsquic/lsquic_logger.h +++ b/src/liblsquic/lsquic_logger.h @@ -51,7 +51,6 @@ enum lsquic_logger_module { LSQLM_EVENT, LSQLM_ENGINE, LSQLM_CONN, - LSQLM_RECHIST, LSQLM_STREAM, LSQLM_PARSE, LSQLM_CFCW, diff --git a/src/liblsquic/lsquic_mini_conn_ietf.c b/src/liblsquic/lsquic_mini_conn_ietf.c index 02291de..e584b76 100644 --- a/src/liblsquic/lsquic_mini_conn_ietf.c +++ b/src/liblsquic/lsquic_mini_conn_ietf.c @@ -495,11 +495,8 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub, /* Generate new SCID. Since is not the original SCID, it is given * a sequence number (0) and therefore can be retired by the client. */ - if (enpub->enp_settings.es_scid_len && enpub->enp_generate_scid) - enpub->enp_generate_scid(&conn->imc_conn, &conn->imc_conn.cn_cces[1].cce_cid, enpub->enp_settings.es_scid_len); - else - lsquic_generate_cid(&conn->imc_conn.cn_cces[1].cce_cid, enpub->enp_settings.es_scid_len); - + enpub->enp_generate_scid(&conn->imc_conn, + &conn->imc_conn.cn_cces[1].cce_cid, enpub->enp_settings.es_scid_len); LSQ_DEBUGC("generated SCID %"CID_FMT" at index %u, switching to it", CID_BITS(&conn->imc_conn.cn_cces[1].cce_cid), 1); diff --git a/src/liblsquic/lsquic_packints.c b/src/liblsquic/lsquic_packints.c deleted file mode 100644 index 3912bc7..0000000 --- a/src/liblsquic/lsquic_packints.c +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ -/* - * lsquic_packints.c -- Packet intervals implementation. - */ - -#include -#include -#include -#include - -#include "lsquic_int_types.h" -#include "lsquic_packints.h" - - -void -lsquic_packints_init (struct packints *pints) -{ - TAILQ_INIT(&pints->pk_intervals); - pints->pk_cur = NULL; -} - - -void -lsquic_packints_cleanup (struct packints *pints) -{ - struct packet_interval *pi, *next; - for (pi = TAILQ_FIRST(&pints->pk_intervals); pi; pi = next) - { - next = TAILQ_NEXT(pi, next_pi); - free(pi); - } -} - - -static int -grow_pi (struct packet_interval *pi, lsquic_packno_t packno) -{ - if (pi->range.low - 1 == packno) { - --pi->range.low; - return 1; - } - if (pi->range.high + 1 == packno) { - ++pi->range.high; - return 1; - } - return 0; -} - - -#if LSQUIC_PACKINTS_SANITY_CHECK -void -lsquic_packints_sanity_check (const struct packints *packints) -{ - struct packet_interval *pi; - uint64_t prev_high; - - prev_high = 0; - - TAILQ_FOREACH(pi, &packints->pk_intervals, next_pi) - { - if (prev_high) - { - assert(pi->range.high + 1 < prev_high); - assert(pi->range.high >= pi->range.low); - } - else - prev_high = pi->range.high; - } -} -#endif - - -enum packints_status -lsquic_packints_add (struct packints *pints, lsquic_packno_t packno) -{ - struct packet_interval *pi, *prev; - - prev = NULL; - TAILQ_FOREACH(pi, &pints->pk_intervals, next_pi) - { - if (packno <= pi->range.high) - { - if (packno >= pi->range.low) - return PACKINTS_DUP; - } else { - if (packno > pi->range.high) - break; - } - prev = pi; - } - - if ((prev && grow_pi(prev, packno)) || (pi && grow_pi(pi, packno))) - { - if (prev && pi && (prev->range.low - 1 == pi->range.high)) { - prev->range.low = pi->range.low; - TAILQ_REMOVE(&pints->pk_intervals, pi, next_pi); - free(pi); - } - } - else - { - struct packet_interval *newpi = malloc(sizeof(*newpi)); - if (!newpi) - return PACKINTS_ERR; - newpi->range.low = newpi->range.high = packno; - if (pi) - TAILQ_INSERT_BEFORE(pi, newpi, next_pi); - else - TAILQ_INSERT_TAIL(&pints->pk_intervals, newpi, next_pi); - } - - lsquic_packints_sanity_check(pints); - return PACKINTS_OK; -} - - -const struct lsquic_packno_range * -lsquic_packints_first (struct packints *pints) -{ - pints->pk_cur = TAILQ_FIRST(&pints->pk_intervals); - return lsquic_packints_next(pints); -} - - -const struct lsquic_packno_range * -lsquic_packints_next (struct packints *pints) -{ - const struct lsquic_packno_range *range; - - if (pints->pk_cur) - { - range = &pints->pk_cur->range; - pints->pk_cur = TAILQ_NEXT(pints->pk_cur, next_pi); - return range; - } - else - return NULL; -} - - -size_t -lsquic_packints_mem_used (const struct packints *packints) -{ - const struct packet_interval *pi; - unsigned count; - - count = 0; - TAILQ_FOREACH(pi, &packints->pk_intervals, next_pi) - ++count; - - return count * sizeof(*pi); -} diff --git a/src/liblsquic/lsquic_packints.h b/src/liblsquic/lsquic_packints.h deleted file mode 100644 index 6b021b0..0000000 --- a/src/liblsquic/lsquic_packints.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ -/* - * lsquic_packints.h -- Ordered (high to low) list of packet intervals. - */ - -#ifndef LSQUIC_PACKINTS_H -#define LSQUIC_PACKINTS_H 1 - -#define LSQUIC_PACKINTS_SANITY_CHECK 0 - -#include - -struct packet_interval { - TAILQ_ENTRY(packet_interval) next_pi; - struct lsquic_packno_range range; -}; - -TAILQ_HEAD(pinhead, packet_interval); - -struct packints { - struct pinhead pk_intervals; - struct packet_interval *pk_cur; -}; - -void -lsquic_packints_init (struct packints *); - -void -lsquic_packints_cleanup (struct packints *); - -enum packints_status { PACKINTS_OK, PACKINTS_DUP, PACKINTS_ERR, }; - -enum packints_status -lsquic_packints_add (struct packints *, lsquic_packno_t); - -const struct lsquic_packno_range * -lsquic_packints_first (struct packints *); - -const struct lsquic_packno_range * -lsquic_packints_next (struct packints *); - -#if LSQUIC_PACKINTS_SANITY_CHECK -void -lsquic_packints_sanity_check (const struct packints *); -#else -# define lsquic_packints_sanity_check(pints) -#endif - -size_t -lsquic_packints_mem_used (const struct packints *); - -#endif diff --git a/src/liblsquic/lsquic_parse_common.c b/src/liblsquic/lsquic_parse_common.c index 18e2530..6536930 100644 --- a/src/liblsquic/lsquic_parse_common.c +++ b/src/liblsquic/lsquic_parse_common.c @@ -220,6 +220,36 @@ lsquic_cid_from_packet (const unsigned char *buf, size_t bufsz, /* See [draft-ietf-quic-transport-28], Section 12.4 (Table 3) */ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] = { + [LSQVER_ID31] = { + [ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING + | QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE, + [ENC_LEV_EARLY] = QUIC_FTBIT_PADDING | QUIC_FTBIT_PING + | QUIC_FTBIT_STREAM | QUIC_FTBIT_RST_STREAM + | QUIC_FTBIT_BLOCKED | QUIC_FTBIT_CONNECTION_CLOSE + | QUIC_FTBIT_MAX_DATA | QUIC_FTBIT_MAX_STREAM_DATA + | QUIC_FTBIT_MAX_STREAMS | QUIC_FTBIT_STREAM_BLOCKED + | QUIC_FTBIT_STREAMS_BLOCKED + | QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING + | QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE + | QUIC_FTBIT_DATAGRAM + | QUIC_FTBIT_RETIRE_CONNECTION_ID, + [ENC_LEV_INIT] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING + | QUIC_FTBIT_ACK| QUIC_FTBIT_CONNECTION_CLOSE, + [ENC_LEV_FORW] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING + | QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE + | QUIC_FTBIT_STREAM | QUIC_FTBIT_RST_STREAM + | QUIC_FTBIT_BLOCKED + | QUIC_FTBIT_MAX_DATA | QUIC_FTBIT_MAX_STREAM_DATA + | QUIC_FTBIT_MAX_STREAMS | QUIC_FTBIT_STREAM_BLOCKED + | QUIC_FTBIT_STREAMS_BLOCKED + | QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING + | QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE + | QUIC_FTBIT_HANDSHAKE_DONE | QUIC_FTBIT_ACK_FREQUENCY + | QUIC_FTBIT_RETIRE_CONNECTION_ID | QUIC_FTBIT_NEW_TOKEN + | QUIC_FTBIT_TIMESTAMP + | QUIC_FTBIT_DATAGRAM + , + }, [LSQVER_ID30] = { [ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING | QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE, diff --git a/src/liblsquic/lsquic_rechist.c b/src/liblsquic/lsquic_rechist.c index 0281be7..afab217 100644 --- a/src/liblsquic/lsquic_rechist.c +++ b/src/liblsquic/lsquic_rechist.c @@ -4,126 +4,355 @@ */ #include -#include -#include +#include +#include +#include #include #include #include "lsquic_int_types.h" -#include "lsquic_types.h" #include "lsquic_rechist.h" -#define LSQUIC_LOGGER_MODULE LSQLM_RECHIST -#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(rechist->rh_conn) -#include "lsquic_logger.h" + +#define BITS_PER_MASK (sizeof(uintptr_t) * 8) + +#if UINTPTR_MAX == 18446744073709551615UL +#define LOG2_BITS 6 +#else +#define LOG2_BITS 5 +#endif void -lsquic_rechist_init (struct lsquic_rechist *rechist, - const struct lsquic_conn *conn, int ietf) +lsquic_rechist_init (struct lsquic_rechist *rechist, int ietf) { memset(rechist, 0, sizeof(*rechist)); - rechist->rh_conn = conn; rechist->rh_cutoff = ietf ? 0 : 1; - lsquic_packints_init(&rechist->rh_pints); - LSQ_DEBUG("instantiated received packet history"); -#if LSQUIC_ACK_ATTACK - const char *s = getenv("LSQUIC_ACK_ATTACK"); - if (s && atoi(s)) - { - LSQ_NOTICE("ACK attack mode ON!"); - rechist->rh_flags |= RH_ACK_ATTACK; - } -#endif } void lsquic_rechist_cleanup (lsquic_rechist_t *rechist) { - lsquic_packints_cleanup(&rechist->rh_pints); + free(rechist->rh_elems); memset(rechist, 0, sizeof(*rechist)); } +static void +rechist_free_elem (struct lsquic_rechist *rechist, unsigned idx) +{ + rechist->rh_masks[idx >> LOG2_BITS] &= + ~(1ull << (idx & ((1u << LOG2_BITS) - 1))); + --rechist->rh_n_used; +} + + +#define RE_HIGH(el_) ((el_)->re_low + (el_)->re_count - 1) + + +static unsigned +find_free_slot (uintptr_t slots) +{ +#if __GNUC__ + if (slots) +#if UINTPTR_MAX == 18446744073709551615UL + return __builtin_ctzll(~slots); +#else + return __builtin_ctzl(~slots); +#endif + else + return 0; +#else + unsigned n; + + slots =~ slots; + n = 0; + +#if UINTPTR_MAX == 18446744073709551615UL + if (0 == (slots & ((1ULL << 32) - 1))) { n += 32; slots >>= 32; } +#endif + if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; } + if (0 == (slots & ((1ULL << 8) - 1))) { n += 8; slots >>= 8; } + if (0 == (slots & ((1ULL << 4) - 1))) { n += 4; slots >>= 4; } + if (0 == (slots & ((1ULL << 2) - 1))) { n += 2; slots >>= 2; } + if (0 == (slots & ((1ULL << 1) - 1))) { n += 1; slots >>= 1; } + return n; +#endif +} + + +static int +rechist_grow (struct lsquic_rechist *rechist) +{ + unsigned n_masks, nelems; + size_t size; + ptrdiff_t moff; + char *mem; + + moff = (char *) rechist->rh_masks - (char *) rechist->rh_elems; + if (rechist->rh_n_alloced) + nelems = rechist->rh_n_alloced * 2; + else + nelems = 4; + n_masks = (nelems + (-nelems & (BITS_PER_MASK - 1))) / BITS_PER_MASK; + size = sizeof(struct rechist_elem) * nelems + sizeof(uintptr_t) * n_masks; + mem = realloc(rechist->rh_elems, size); + if (!mem) + return -1; + if (moff) + memcpy(mem + size - n_masks * sizeof(rechist->rh_masks[0]), + (char *) mem + moff, + rechist->rh_n_masks * sizeof(rechist->rh_masks[0])); + if (rechist->rh_n_masks < n_masks) + memset(mem + nelems * sizeof(rechist->rh_elems[0]) + + rechist->rh_n_masks * sizeof(rechist->rh_masks[0]), + 0, (n_masks - rechist->rh_n_masks) * sizeof(rechist->rh_masks[0])); + rechist->rh_n_alloced = nelems; + rechist->rh_n_masks = n_masks; + rechist->rh_elems = (void *) mem; + rechist->rh_masks = (void *) (mem + size + - n_masks * sizeof(rechist->rh_masks[0])); + return 0; +} + + +static int +rechist_alloc_elem (struct lsquic_rechist *rechist) +{ + unsigned i, idx; + uintptr_t *mask; + + if (rechist->rh_n_used == rechist->rh_n_alloced + && 0 != rechist_grow(rechist)) + return -1; + + for (mask = rechist->rh_masks; *mask == UINTPTR_MAX; ++mask) + ; + + i = mask - rechist->rh_masks; + assert(i < rechist->rh_n_masks); + + idx = find_free_slot(*mask); + *mask |= 1ull << idx; + ++rechist->rh_n_used; + return idx + i * BITS_PER_MASK; +} + + +#if LSQUIC_TEST +/* When compiled as unit test, run sanity check every 127 operations + * (127 is better than 128, as the latter aligns too well with the + * regular rechist data structure sizes). + */ +static void +rechist_test_sanity (const struct lsquic_rechist *rechist) +{ + const struct rechist_elem *el; + ptrdiff_t idx; + uint64_t *masks; + unsigned n_elems; + + masks = calloc(rechist->rh_n_masks, sizeof(masks[0])); + + n_elems = 0; + if (rechist->rh_n_used) + { + el = &rechist->rh_elems[rechist->rh_head]; + while (1) + { + ++n_elems; + idx = el - rechist->rh_elems; + masks[idx >> LOG2_BITS] |= 1ull << (idx & ((1u << LOG2_BITS) - 1)); + if (el->re_next != UINT_MAX) + el = &rechist->rh_elems[el->re_next]; + else + break; + } + } + + assert(rechist->rh_n_used == n_elems); + assert(0 == memcmp(masks, rechist->rh_masks, + sizeof(masks[0]) * rechist->rh_n_masks)); + free(masks); +} +#define rechist_sanity_check(rechist_) do { \ + if (0 == ++(rechist_)->rh_n_ops % 127) \ + rechist_test_sanity(rechist_); \ +} while (0) +#else +#define rechist_sanity_check(rechist) +#endif + + enum received_st lsquic_rechist_received (lsquic_rechist_t *rechist, lsquic_packno_t packno, lsquic_time_t now) { - const struct lsquic_packno_range *first_range; + struct rechist_elem *el, *prev; + ptrdiff_t next_idx, prev_idx; + int idx; + + if (rechist->rh_n_alloced == 0) + goto first_elem; - LSQ_DEBUG("received %"PRIu64, packno); if (packno < rechist->rh_cutoff) - { - if (packno) - return REC_ST_DUP; - else - return REC_ST_ERR; - } + return REC_ST_DUP; - first_range = lsquic_packints_first(&rechist->rh_pints); - if (!first_range || packno > first_range->high) + el = &rechist->rh_elems[rechist->rh_head]; + prev = NULL; + + if (packno > RE_HIGH(el)) rechist->rh_largest_acked_received = now; - switch (lsquic_packints_add(&rechist->rh_pints, packno)) + while (1) { - case PACKINTS_OK: - ++rechist->rh_n_packets; - return REC_ST_OK; - case PACKINTS_DUP: - return REC_ST_DUP; - default: - assert(0); - case PACKINTS_ERR: - return REC_ST_ERR; + if (packno > RE_HIGH(el) + 1) + goto insert_before; + if (packno == el->re_low - 1) + { + --el->re_low; + ++el->re_count; + if (el->re_next != UINT_MAX + && el->re_low == RE_HIGH(&rechist->rh_elems[el->re_next]) + 1) + { + rechist_free_elem(rechist, el->re_next); + el->re_count += rechist->rh_elems[el->re_next].re_count; + el->re_low = rechist->rh_elems[el->re_next].re_low; + el->re_next = rechist->rh_elems[el->re_next].re_next; + } + rechist_sanity_check(rechist); + return REC_ST_OK; + } + if (packno == RE_HIGH(el) + 1) + { + ++el->re_count; + rechist_sanity_check(rechist); + return REC_ST_OK; + } + if (packno >= el->re_low && packno <= RE_HIGH(el)) + return REC_ST_DUP; + if (el->re_next == UINT_MAX) + break; /* insert tail */ + prev = el; + el = &rechist->rh_elems[el->re_next]; } + + prev_idx = el - rechist->rh_elems; + idx = rechist_alloc_elem(rechist); + if (idx < 0) + return REC_ST_ERR; + + rechist->rh_elems[idx].re_low = packno; + rechist->rh_elems[idx].re_count = 1; + rechist->rh_elems[idx].re_next = UINT_MAX; + rechist->rh_elems[prev_idx].re_next = idx; + rechist_sanity_check(rechist); + return REC_ST_OK; + + first_elem: + if (packno < rechist->rh_cutoff) + return REC_ST_ERR; + idx = rechist_alloc_elem(rechist); + if (idx < 0) + return REC_ST_ERR; + + rechist->rh_elems[idx].re_low = packno; + rechist->rh_elems[idx].re_count = 1; + rechist->rh_elems[idx].re_next = UINT_MAX; + rechist->rh_head = idx; + rechist->rh_largest_acked_received = now; + rechist_sanity_check(rechist); + return REC_ST_OK; + + insert_before: + prev_idx = prev - rechist->rh_elems; + next_idx = el - rechist->rh_elems; + idx = rechist_alloc_elem(rechist); + if (idx < 0) + return REC_ST_ERR; + + rechist->rh_elems[idx].re_low = packno; + rechist->rh_elems[idx].re_count = 1; + rechist->rh_elems[idx].re_next = next_idx; + if (next_idx == rechist->rh_head) + rechist->rh_head = idx; + else + rechist->rh_elems[prev_idx].re_next = idx; + + rechist_sanity_check(rechist); + return REC_ST_OK; } void lsquic_rechist_stop_wait (lsquic_rechist_t *rechist, lsquic_packno_t cutoff) { - LSQ_INFO("stop wait: %"PRIu64, cutoff); + struct rechist_elem *el, *prev; if (rechist->rh_flags & RH_CUTOFF_SET) { assert(cutoff >= rechist->rh_cutoff); /* Check performed in full_conn */ - if (cutoff == rechist->rh_cutoff) + if (cutoff <= rechist->rh_cutoff) return; } rechist->rh_cutoff = cutoff; rechist->rh_flags |= RH_CUTOFF_SET; - struct packet_interval *pi, *next; - for (pi = TAILQ_FIRST(&rechist->rh_pints.pk_intervals); pi; pi = next) + + if (rechist->rh_n_used == 0) + return; + + el = &rechist->rh_elems[rechist->rh_head]; + prev = NULL; + while (1) { - next = TAILQ_NEXT(pi, next_pi); - if (pi->range.low < cutoff) + if (cutoff > RE_HIGH(el)) { - if (pi->range.high < cutoff) + if (prev) + prev->re_next = UINT_MAX; + break; + } + else if (cutoff > el->re_low) + { + el->re_count = RE_HIGH(el) - cutoff + 1; + el->re_low = cutoff; + if (el->re_next != UINT_MAX) { - rechist->rh_n_packets -= (unsigned)(pi->range.high - pi->range.low + 1); - TAILQ_REMOVE(&rechist->rh_pints.pk_intervals, pi, next_pi); - free(pi); + prev = el; + el = &rechist->rh_elems[el->re_next]; + prev->re_next = UINT_MAX; + break; } else - { - rechist->rh_n_packets -= (unsigned)(cutoff - pi->range.low); - pi->range.low = cutoff; - } + goto end; } + else if (el->re_next == UINT_MAX) + goto end; + prev = el; + el = &rechist->rh_elems[el->re_next]; } - lsquic_packints_sanity_check(&rechist->rh_pints); + + assert(el); + while (1) + { + rechist_free_elem(rechist, el - rechist->rh_elems); + if (el->re_next != UINT_MAX) + el = &rechist->rh_elems[el->re_next]; + else + break; + } + + end: + rechist_sanity_check(rechist); } lsquic_packno_t lsquic_rechist_largest_packno (const lsquic_rechist_t *rechist) { - const struct packet_interval *pi = - TAILQ_FIRST(&rechist->rh_pints.pk_intervals); - if (pi) - return pi->range.high; + if (rechist->rh_n_used) + return RE_HIGH(&rechist->rh_elems[rechist->rh_head]); else return 0; /* Don't call this function if history is empty */ } @@ -149,35 +378,36 @@ lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist) const struct lsquic_packno_range * lsquic_rechist_first (lsquic_rechist_t *rechist) { -#if LSQUIC_ACK_ATTACK - if (rechist->rh_flags & RH_ACK_ATTACK) - { - /* This only performs the lazy variant of the attack. An aggressive - * attack would increase the value of high number. - */ - const struct lsquic_packno_range *range; + unsigned idx; - range = lsquic_packints_first(&rechist->rh_pints); - if (!range) - return NULL; - rechist->rh_first = *range; - range = &TAILQ_LAST(&rechist->rh_pints.pk_intervals, pinhead)->range; - rechist->rh_first.low = range->low; - return &rechist->rh_first; + if (rechist->rh_n_used) + { + idx = rechist->rh_head; + rechist->rh_iter.range.low = rechist->rh_elems[idx].re_low; + rechist->rh_iter.range.high = RE_HIGH(&rechist->rh_elems[idx]); + rechist->rh_iter.next = rechist->rh_elems[idx].re_next; + return &rechist->rh_iter.range; } -#endif - return lsquic_packints_first(&rechist->rh_pints); + else + return NULL; } const struct lsquic_packno_range * lsquic_rechist_next (lsquic_rechist_t *rechist) { -#if LSQUIC_ACK_ATTACK - if (rechist->rh_flags & RH_ACK_ATTACK) + unsigned idx; + + idx = rechist->rh_iter.next; + if (idx != UINT_MAX) + { + rechist->rh_iter.range.low = rechist->rh_elems[idx].re_low; + rechist->rh_iter.range.high = RE_HIGH(&rechist->rh_elems[idx]); + rechist->rh_iter.next = rechist->rh_elems[idx].re_next; + return &rechist->rh_iter.range; + } + else return NULL; -#endif - return lsquic_packints_next(&rechist->rh_pints); } @@ -185,19 +415,22 @@ size_t lsquic_rechist_mem_used (const struct lsquic_rechist *rechist) { return sizeof(*rechist) - - sizeof(rechist->rh_pints) - + lsquic_packints_mem_used(&rechist->rh_pints); + + rechist->rh_n_alloced * sizeof(rechist->rh_elems[0]) + + rechist->rh_n_masks * sizeof(rechist->rh_masks[0]); } const struct lsquic_packno_range * -lsquic_rechist_peek (const struct lsquic_rechist *rechist) +lsquic_rechist_peek (struct lsquic_rechist *rechist) { - const struct packet_interval *pint; - - pint = TAILQ_FIRST(&rechist->rh_pints.pk_intervals); - if (pint) - return &pint->range; + if (rechist->rh_n_used) + { + rechist->rh_iter.range.low + = rechist->rh_elems[rechist->rh_head].re_low; + rechist->rh_iter.range.high + = RE_HIGH(&rechist->rh_elems[rechist->rh_head]); + return &rechist->rh_iter.range; + } else return NULL; } diff --git a/src/liblsquic/lsquic_rechist.h b/src/liblsquic/lsquic_rechist.h index 96e8dbf..29d95a7 100644 --- a/src/liblsquic/lsquic_rechist.h +++ b/src/liblsquic/lsquic_rechist.h @@ -8,34 +8,42 @@ #ifndef LSQUIC_RECHIST_H #define LSQUIC_RECHIST_H 1 -struct lsquic_conn; -#include "lsquic_packints.h" +/* Structure is exposed to facilitate some manipulations in unit tests. */ +struct rechist_elem { + lsquic_packno_t re_low; + unsigned re_count; + unsigned re_next; /* UINT_MAX means no next element */ +}; + struct lsquic_rechist { - struct packints rh_pints; + /* elems and masks are allocated in contiguous memory */ + struct rechist_elem *rh_elems; + uintptr_t *rh_masks; lsquic_packno_t rh_cutoff; lsquic_time_t rh_largest_acked_received; - const struct lsquic_conn *rh_conn; /* Used for logging */ - /* Chromium limits the number of tracked packets (see - * kMaxTrackedPackets). We could do this, too. - */ - unsigned rh_n_packets; + unsigned rh_n_masks; + unsigned rh_n_alloced; + unsigned rh_n_used; + unsigned rh_head; enum { RH_CUTOFF_SET = (1 << 0), -#if LSQUIC_ACK_ATTACK - RH_ACK_ATTACK = (1 << 1), -#endif } rh_flags; -#if LSQUIC_ACK_ATTACK - struct lsquic_packno_range rh_first; + struct + { + struct lsquic_packno_range range; + unsigned next; + } rh_iter; +#if LSQUIC_TEST + unsigned rh_n_ops; #endif }; typedef struct lsquic_rechist lsquic_rechist_t; void -lsquic_rechist_init (struct lsquic_rechist *, const struct lsquic_conn *, int); +lsquic_rechist_init (struct lsquic_rechist *, int is_ietf); void lsquic_rechist_cleanup (struct lsquic_rechist *); @@ -53,12 +61,6 @@ lsquic_rechist_received (lsquic_rechist_t *, lsquic_packno_t, void lsquic_rechist_stop_wait (lsquic_rechist_t *, lsquic_packno_t); -/* Returns number of bytes written on success, -1 on failure */ -int -lsquic_rechist_make_ackframe (lsquic_rechist_t *, - void *outbuf, size_t outbuf_sz, int *has_missing, - lsquic_time_t now); - const struct lsquic_packno_range * lsquic_rechist_first (lsquic_rechist_t *); @@ -78,8 +80,8 @@ size_t lsquic_rechist_mem_used (const struct lsquic_rechist *); const struct lsquic_packno_range * -lsquic_rechist_peek (const struct lsquic_rechist *); +lsquic_rechist_peek (struct lsquic_rechist *); -#define lsquic_rechist_n_packets(rechist_) (+(rechist_)->rh_n_packets) +#define lsquic_rechist_is_empty(rechist_) ((rechist_)->rh_n_used == 0) #endif diff --git a/src/liblsquic/lsquic_version.c b/src/liblsquic/lsquic_version.c index b81a202..d134bd9 100644 --- a/src/liblsquic/lsquic_version.c +++ b/src/liblsquic/lsquic_version.c @@ -22,6 +22,7 @@ static const unsigned char version_tags[N_LSQVER][4] = [LSQVER_ID28] = { 0xFF, 0, 0, 28, }, [LSQVER_ID29] = { 0xFF, 0, 0, 29, }, [LSQVER_ID30] = { 0xFF, 0, 0, 30, }, + [LSQVER_ID31] = { 0xFF, 0, 0, 31, }, [LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, }, }; @@ -62,6 +63,7 @@ const char *const lsquic_ver2str[N_LSQVER] = { [LSQVER_ID28] = "FF00001C", [LSQVER_ID29] = "FF00001D", [LSQVER_ID30] = "FF00001E", + [LSQVER_ID31] = "FF00001F", [LSQVER_VERNEG] = "FAFAFAFA", }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1727e3b..39554cb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,7 +54,6 @@ SET(TESTS purga qlog quic_be_floats - rechist reg_pkt_headergen rst_stream_gquic_be rtt @@ -127,3 +126,6 @@ ADD_TEST(malo_nopool test_malo_nopool) ADD_EXECUTABLE(test_minmax test_minmax.c ../src/liblsquic/lsquic_minmax.c) ADD_TEST(minmax test_minmax) + +ADD_EXECUTABLE(test_rechist test_rechist.c ../src/liblsquic/lsquic_rechist.c) +ADD_TEST(rechist test_rechist) diff --git a/tests/test_ackgen_gquic_be.c b/tests/test_ackgen_gquic_be.c index f69b935..ddb1647 100644 --- a/tests/test_ackgen_gquic_be.c +++ b/tests/test_ackgen_gquic_be.c @@ -30,7 +30,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */ lsquic_time_t now = lsquic_time_now(); lsquic_packno_t largest = 0; - lsquic_rechist_init(&rechist, 0, 0); + lsquic_rechist_init(&rechist, 0); unsigned i; for (i = 1; i <= 0x1234; ++i) @@ -69,7 +69,7 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus lsquic_rechist_t rechist; lsquic_time_t now = lsquic_time_now(); - lsquic_rechist_init(&rechist, 0, 0); + lsquic_rechist_init(&rechist, 0); /* Encode the following ranges: * high low @@ -128,7 +128,7 @@ test3 (void) lsquic_rechist_t rechist; lsquic_time_t now = lsquic_time_now(); - lsquic_rechist_init(&rechist, 0, 0); + lsquic_rechist_init(&rechist, 0); /* Encode the following ranges: * high low @@ -174,7 +174,7 @@ test4 (void) lsquic_rechist_t rechist; int i; - lsquic_rechist_init(&rechist, 0, 0); + lsquic_rechist_init(&rechist, 0); lsquic_time_t now = lsquic_time_now(); lsquic_rechist_received(&rechist, 1, now); @@ -242,18 +242,17 @@ test_4byte_packnos (void) { lsquic_packno_t packno; lsquic_rechist_t rechist; - struct packet_interval *pint; lsquic_time_t now = lsquic_time_now(); - lsquic_rechist_init(&rechist, 0, 0); + lsquic_rechist_init(&rechist, 0); packno = 0x23456789; (void) lsquic_rechist_received(&rechist, packno - 33, now); - pint = TAILQ_FIRST(&rechist.rh_pints.pk_intervals); (void) lsquic_rechist_received(&rechist, packno, now); /* Adjust: */ - pint->range.low = 1; + rechist.rh_elems[0].re_low = 1; + rechist.rh_elems[0].re_count = packno - 33; const unsigned char expected_ack_frame[] = { 0x60 @@ -288,23 +287,52 @@ test_4byte_packnos (void) } +/* lsquic_rechist no longer supports ranges that require integers + * wider than four bytes -- modify the test to use a custom receive + * history. + */ +static const struct lsquic_packno_range test_6byte_ranges[] = { + { .high = 0xABCD23456789, .low = 0xABCD23456789, }, + { .high = 0xABCD23456789 - 33, .low = 1, }, +}; + + +static const struct lsquic_packno_range * +test_6byte_rechist_first (void *rechist) +{ + int *next = rechist; + *next = 1; + return &test_6byte_ranges[0]; +}; + + +static const struct lsquic_packno_range * +test_6byte_rechist_next (void *rechist) +{ + int *next = rechist; + if (*next == 1) + { + ++*next; + return &test_6byte_ranges[1]; + } + else + return NULL; +} + + +static lsquic_time_t s_test_6byte_now; +static lsquic_time_t +test_6byte_rechist_largest_recv (void *rechist) +{ + return s_test_6byte_now; +} + + static void test_6byte_packnos (void) { - lsquic_packno_t packno; - lsquic_rechist_t rechist; - struct packet_interval *pint; - lsquic_time_t now = lsquic_time_now(); - - lsquic_rechist_init(&rechist, 0, 0); - - packno = 0xABCD23456789; - (void) lsquic_rechist_received(&rechist, packno - 33, now); - pint = TAILQ_FIRST(&rechist.rh_pints.pk_intervals); - (void) lsquic_rechist_received(&rechist, packno, now); - - /* Adjust: */ - pint->range.low = 1; + int rechist = 0; + s_test_6byte_now = lsquic_time_now(); const unsigned char expected_ack_frame[] = { 0x60 @@ -324,18 +352,16 @@ test_6byte_packnos (void) int has_missing = -1; lsquic_packno_t largest = 0; int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf), - (gaf_rechist_first_f) lsquic_rechist_first, - (gaf_rechist_next_f) lsquic_rechist_next, - (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv, - &rechist, now, &has_missing, &largest, NULL); + test_6byte_rechist_first, + test_6byte_rechist_next, + test_6byte_rechist_largest_recv, + &rechist, s_test_6byte_now, &has_missing, &largest, NULL); assert(("ACK frame generation successful", w > 0)); assert(("ACK frame length is correct", w == sizeof(expected_ack_frame))); assert(("ACK frame contents are as expected", 0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame)))); assert(("ACK frame has missing packets", has_missing > 0)); assert(largest == 0xABCD23456789ULL); - - lsquic_rechist_cleanup(&rechist); } diff --git a/tests/test_ackparse_gquic_be.c b/tests/test_ackparse_gquic_be.c index 1e4ac78..de7c28b 100644 --- a/tests/test_ackparse_gquic_be.c +++ b/tests/test_ackparse_gquic_be.c @@ -3,6 +3,7 @@ #include #include #include +#include #ifndef WIN32 #include #else @@ -342,7 +343,7 @@ test_max_ack (void) unsigned char buf[1500]; struct ack_info acki; - lsquic_rechist_init(&rechist, &lconn, 0); + lsquic_rechist_init(&rechist, 0); now = lsquic_time_now(); for (i = 1; i <= 300; ++i) @@ -395,7 +396,7 @@ test_ack_truncation (void) struct ack_info acki; size_t bufsz; - lsquic_rechist_init(&rechist, &lconn, 0); + lsquic_rechist_init(&rechist, 0); now = lsquic_time_now(); for (i = 1; i <= 300; ++i) diff --git a/tests/test_ackparse_ietf.c b/tests/test_ackparse_ietf.c index 8f17801..1e8be97 100644 --- a/tests/test_ackparse_ietf.c +++ b/tests/test_ackparse_ietf.c @@ -3,6 +3,7 @@ #include #include #include +#include #ifndef WIN32 #include #else @@ -33,7 +34,7 @@ test_max_ack (void) unsigned char buf[1500]; struct ack_info acki; - lsquic_rechist_init(&rechist, &lconn, 0); + lsquic_rechist_init(&rechist, 0); now = lsquic_time_now(); for (i = 1; i <= 300; ++i) @@ -86,7 +87,7 @@ test_ack_truncation (void) struct ack_info acki; size_t bufsz; - lsquic_rechist_init(&rechist, &lconn, 0); + lsquic_rechist_init(&rechist, 0); now = lsquic_time_now(); for (i = 1; i <= 300; ++i) diff --git a/tests/test_rechist.c b/tests/test_rechist.c index f935579..bb678a7 100644 --- a/tests/test_rechist.c +++ b/tests/test_rechist.c @@ -9,18 +9,9 @@ #include "vc_compat.h" #endif -#include "lsquic_types.h" #include "lsquic_int_types.h" #include "lsquic_rechist.h" -#include "lsquic_parse.h" #include "lsquic_util.h" -#include "lsquic_logger.h" -#include "lsquic.h" -#include "lsquic_hash.h" -#include "lsquic_conn.h" - - -static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0); static void @@ -30,7 +21,7 @@ test4 (void) const struct lsquic_packno_range *range; lsquic_packno_t packno; - lsquic_rechist_init(&rechist, &lconn, 0); + lsquic_rechist_init(&rechist, 0); for (packno = 11917; packno <= 11941; ++packno) lsquic_rechist_received(&rechist, packno, 0); @@ -131,7 +122,7 @@ test5 (void) lsquic_rechist_t rechist; char buf[100]; - lsquic_rechist_init(&rechist, &lconn, 0); + lsquic_rechist_init(&rechist, 0); lsquic_rechist_received(&rechist, 1, 0); /* Packet 2 omitted because it could not be decrypted */ @@ -173,6 +164,97 @@ test5 (void) } +static void +test_rand_sequence (unsigned seed) +{ + struct lsquic_rechist rechist; + const struct lsquic_packno_range *range; + lsquic_packno_t prev_low; + enum received_st st; + unsigned i; + + lsquic_rechist_init(&rechist, 1); + srand(seed); + + for (i = 0; i < 10000; ++i) + { + st = lsquic_rechist_received(&rechist, (unsigned) rand(), 0); + assert(st == REC_ST_OK || st == REC_ST_DUP); + } + + range = lsquic_rechist_first(&rechist); + assert(range); + assert(range->high >= range->low); + prev_low = range->low; + + while (range = lsquic_rechist_next(&rechist), range != NULL) + { + assert(range->high >= range->low); + assert(range->high < prev_low); + prev_low = range->low; + } + + lsquic_rechist_cleanup(&rechist); +} + + +struct shuffle_elem { + unsigned packno; + int rand; +}; + + +static int +comp_els (const void *a_p, const void *b_p) +{ + const struct shuffle_elem *a = a_p, *b = b_p; + if (a->rand < b->rand) + return -1; + if (a->rand > b->rand) + return 1; + return (a->packno > b->packno) - (b->packno > a->packno); +} + + +static void +test_shuffle_1000 (unsigned seed) +{ + struct lsquic_rechist rechist; + const struct lsquic_packno_range *range; + enum received_st st; + unsigned i; + struct shuffle_elem *els; + + els = malloc(sizeof(els[0]) * 10000); + lsquic_rechist_init(&rechist, 1); + srand(seed); + + for (i = 0; i < 10000; ++i) + { + els[i].packno = i; + els[i].rand = rand(); + } + + qsort(els, 10000, sizeof(els[0]), comp_els); + + for (i = 0; i < 10000; ++i) + { + st = lsquic_rechist_received(&rechist, els[i].packno, 0); + assert(st == REC_ST_OK || st == REC_ST_DUP); + } + + range = lsquic_rechist_first(&rechist); + assert(range); + assert(range->high == 9999); + assert(range->low == 0); + range = lsquic_rechist_next(&rechist); + assert(!range); + + lsquic_rechist_cleanup(&rechist); + free(els); +} + + int main (void) { @@ -181,15 +263,9 @@ main (void) unsigned i; const struct lsquic_packno_range *range; - lsquic_global_init(LSQUIC_GLOBAL_SERVER); + lsquic_rechist_init(&rechist, 0); - lsquic_log_to_fstream(stderr, 0); - lsq_log_levels[LSQLM_PARSE] = LSQ_LOG_DEBUG; - lsq_log_levels[LSQLM_RECHIST] = LSQ_LOG_DEBUG; - - lsquic_rechist_init(&rechist, &lconn, 0); - - lsquic_time_t now = lsquic_time_now(); + lsquic_time_t now = 1234; st = lsquic_rechist_received(&rechist, 0, now); assert(("inserting packet number zero results in error", st == REC_ST_ERR)); @@ -254,11 +330,36 @@ main (void) range = lsquic_rechist_next(&rechist); assert(("third range does not exist", !range)); + lsquic_rechist_stop_wait(&rechist, 5); + + range = lsquic_rechist_first(&rechist); + range = lsquic_rechist_next(&rechist); + assert(("second range returned correctly", range)); + assert(("second range low value checks out", range->low == 5)); + assert(("second range high value checks out", range->high == 5)); + range = lsquic_rechist_next(&rechist); + assert(("third range does not exist", !range)); + + lsquic_rechist_stop_wait(&rechist, 8); + + range = lsquic_rechist_first(&rechist); + assert(("first range returned correctly", range)); + assert(("first range low value checks out", range->low == 8)); + assert(("first range high value checks out", range->high == 9)); + range = lsquic_rechist_next(&rechist); + assert(("second range does not exist", !range)); + lsquic_rechist_cleanup(&rechist); test4(); test5(); + for (i = 0; i < 10; ++i) + test_rand_sequence(i); + + for (i = 0; i < 10; ++i) + test_shuffle_1000(i); + return 0; }