Release 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.
This commit is contained in:
Dmitri Tikhonov 2020-09-29 08:56:43 -04:00
parent 2e1429b465
commit b62ec17fd2
33 changed files with 653 additions and 426 deletions

View file

@ -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.

View file

@ -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!

View file

@ -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
-------------

View file

@ -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] <https://tools.ietf.org/html/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.

View file

@ -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 ---------------------------------------------------

View file

@ -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

View file

@ -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);
};
/**

View file

@ -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()

View file

@ -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 \

View file

@ -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)
{

View file

@ -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);

View file

@ -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.
*/

View file

@ -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 )

View file

@ -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.
*/

View file

@ -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)
{

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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 */

View file

@ -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",

View file

@ -51,7 +51,6 @@ enum lsquic_logger_module {
LSQLM_EVENT,
LSQLM_ENGINE,
LSQLM_CONN,
LSQLM_RECHIST,
LSQLM_STREAM,
LSQLM_PARSE,
LSQLM_CFCW,

View file

@ -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);

View file

@ -1,152 +0,0 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packints.c -- Packet intervals implementation.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#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);
}

View file

@ -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 <sys/queue.h>
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

View file

@ -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,

View file

@ -4,126 +4,355 @@
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -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

View file

@ -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",
};

View file

@ -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)

View file

@ -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);
}

View file

@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#ifndef WIN32
#include <sys/time.h>
#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)

View file

@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#ifndef WIN32
#include <sys/time.h>
#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)

View file

@ -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;
}