Release 2.12.0

- [FEATURE] QUIC timestamps extension.
- [API] New: ea_alpn that is used when not in HTTP mode.
- [BUGFIX] SNI is mandatory only for HTTP/3 and gQUIC.
- [BUGFIX] Benign double-free -- issue #110.
- [BUGFIX] Printing of transport parameters.
This commit is contained in:
Dmitri Tikhonov 2020-03-02 08:53:41 -05:00
parent fa4561dcea
commit afe3d36359
24 changed files with 279 additions and 34 deletions

View File

@ -1,3 +1,11 @@
2020-03-02
- 2.12.0
- [FEATURE] QUIC timestamps extension.
- [API] New: ea_alpn that is used when not in HTTP mode.
- [BUGFIX] SNI is mandatory only for HTTP/3 and gQUIC.
- [BUGFIX] Benign double-free -- issue #110.
- [BUGFIX] Printing of transport parameters.
2020-02-24 2020-02-24
- 2.11.1 - 2.11.1
- [FEATURE] QUIC and HTTP/3 Internet Draft 27 support. - [FEATURE] QUIC and HTTP/3 Internet Draft 27 support.

View File

@ -239,7 +239,7 @@ optional members.
.. member:: struct ssl_ctx_st * (*ea_get_ssl_ctx)(void *peer_ctx) .. member:: struct ssl_ctx_st * (*ea_get_ssl_ctx)(void *peer_ctx)
Get SSL_CTX associated with a peer context. Mandatory in server Get SSL_CTX associated with a peer context. Mandatory in server
mode. mode. This is use for default values for SSL instantiation.
.. member:: const struct lsquic_hset_if *ea_hsi_if .. member:: const struct lsquic_hset_if *ea_hsi_if
.. member:: void *ea_hsi_ctx .. member:: void *ea_hsi_ctx
@ -268,6 +268,8 @@ optional members.
In a multi-process setup, it may be useful to observe the CID In a multi-process setup, it may be useful to observe the CID
lifecycle. This optional set of callbacks makes it possible. lifecycle. This optional set of callbacks makes it possible.
.. _apiref-engine-settings:
Engine Settings Engine Settings
--------------- ---------------
@ -348,7 +350,7 @@ settings structure:
.. member:: unsigned es_max_header_list_size .. member:: unsigned es_max_header_list_size
This corresponds to SETTINGS_MAX_HEADER_LIST_SIZE This corresponds to SETTINGS_MAX_HEADER_LIST_SIZE
(RFC 7540, Section 6.5.2). 0 means no limit. Defaults (:rfc:`7540#section-6.5.2`). 0 means no limit. Defaults
to :func:`LSQUIC_DF_MAX_HEADER_LIST_SIZE`. to :func:`LSQUIC_DF_MAX_HEADER_LIST_SIZE`.
.. member:: const char *es_ua .. member:: const char *es_ua
@ -685,6 +687,12 @@ settings structure:
Default value is :macro:`LSQUIC_DF_DELAYED_ACKS` Default value is :macro:`LSQUIC_DF_DELAYED_ACKS`
.. member:: int es_timestamps
Enable timestamps extension. Allowed values are 0 and 1.
Default value is @ref LSQUIC_DF_TIMESTAMPS
To initialize the settings structure to library defaults, use the following To initialize the settings structure to library defaults, use the following
convenience function: convenience function:
@ -940,7 +948,7 @@ that the library uses to send packets.
.. member:: int ecn .. member:: int ecn
ECN: Valid values are 0 - 3. See RFC 3168. ECN: Valid values are 0 - 3. See :rfc:`3168`.
ECN may be set by IETF QUIC connections if ``es_ecn`` is set. ECN may be set by IETF QUIC connections if ``es_ecn`` is set.
@ -1465,7 +1473,7 @@ fields yourself. In that case, the header set must be "read" from the stream vi
Get header set associated with the stream. The header set is created by Get header set associated with the stream. The header set is created by
``hsi_create_header_set()`` callback. After this call, the ownership of ``hsi_create_header_set()`` callback. After this call, the ownership of
the header set is trasnferred to the caller. the header set is transferred to the caller.
This call must precede calls to :func:`lsquic_stream_read()`, This call must precede calls to :func:`lsquic_stream_read()`,
:func:`lsquic_stream_readv()`, and :func:`lsquic_stream_readf()`. :func:`lsquic_stream_readv()`, and :func:`lsquic_stream_readf()`.

View File

@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
author = u'LiteSpeed Technologies' author = u'LiteSpeed Technologies'
# The short X.Y version # The short X.Y version
version = u'2.11' version = u'2.12'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = u'2.11.1' release = u'2.12.0'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View File

@ -1,2 +1,71 @@
Getting Started with LSQUIC Getting Started
=========================== ===============
Supported Platforms
-------------------
LSQUIC compiles and runs on Linux, FreeBSD, and Mac OS. It has been
tested on i386, x86_64, as well as Raspberry Pi.
Windows support is on the TODO list.
Dependencies
------------
LSQUIC library uses:
- zlib_;
- BoringSSL_; and
- `ls-qpack`_ (as a Git submodule).
The accompanying demo command-line tools use libevent_.
What's in the box
-----------------
- ``src/liblsquic`` -- the library
- ``test`` -- demo client and server programs
- ``test/unittests`` -- unit tests
Building
--------
To build the library, follow instructions in the README_ file.
Demo Examples
-------------
Fetch Google home page:
::
./http_client -s www.google.com -p / -o version=Q050
Run your own server (it does not touch the filesystem, don't worry):
::
./http_server -c www.example.com,fullchain.pem,privkey.pem -s 0.0.0.0:4433
Grab a page from your server:
::
./http_client -H www.example.com -s 127.0.0.1:4433 -p /
You can play with various options, of which there are many. Use
the ``-h`` command-line flag to see them.
Next steps
----------
If you want to use LSQUIC in your program, check out the :doc:`tutorial` and
the :doc:`apiref`.
:doc:`internals` covers some library internals.
.. _zlib: https://www.zlib.net/
.. _BoringSSL: https://boringssl.googlesource.com/boringssl/
.. _`ls-qpack`: https://github.com/litespeedtech/ls-qpack
.. _libevent: https://libevent.org/
.. _README: https://github.com/litespeedtech/lsquic/blob/master/README.md

View File

@ -3,9 +3,6 @@ LSQUIC Documentation
This is the documentation for LSQUIC_ |release|, last updated |today|. This is the documentation for LSQUIC_ |release|, last updated |today|.
Introduction
------------
LiteSpeed QUIC (LSQUIC) Library is an open-source implementation of QUIC LiteSpeed QUIC (LSQUIC) Library is an open-source implementation of QUIC
and HTTP/3 functionality for servers and clients. LSQUIC is: and HTTP/3 functionality for servers and clients. LSQUIC is:
@ -38,7 +35,9 @@ Contents
:maxdepth: 2 :maxdepth: 2
gettingstarted gettingstarted
tutorial
apiref apiref
internals
Indices and tables Indices and tables
================== ==================

11
docs/internals.rst Normal file
View File

@ -0,0 +1,11 @@
*********
Internals
*********
Connection Management
=====================
References to connections can exist in six different places in an
engine.
.. image:: lsquic-engine-conns.png

View File

@ -0,0 +1 @@
<mxfile host="www.draw.io" modified="2020-02-21T20:56:15.366Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0" etag="X7KTg7RjNxutHAnzRWEE" version="12.7.3"><diagram id="g-XUBOnqw-Le0S7dI5B8" name="Page-1">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</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 11 #define LSQUIC_MINOR_VERSION 12
#define LSQUIC_PATCH_VERSION 1 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -205,6 +205,9 @@ struct ssl_st;
* constructor. * constructor.
*/ */
/* `sni' may be NULL if engine is not HTTP mode and client TLS transport
* parameters did not include the SNI.
*/
typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)( typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni); void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni);
@ -330,6 +333,9 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
/** Turn off delayed ACKs extension by default */ /** Turn off delayed ACKs extension by default */
#define LSQUIC_DF_DELAYED_ACKS 0 #define LSQUIC_DF_DELAYED_ACKS 0
/** Turn on timestamp extension by default */
#define LSQUIC_DF_TIMESTAMPS 1
/* 1: Cubic; 2: BBR */ /* 1: Cubic; 2: BBR */
#define LSQUIC_DF_CC_ALGO 1 #define LSQUIC_DF_CC_ALGO 1
@ -724,6 +730,13 @@ struct lsquic_engine_settings {
* Default value is @ref LSQUIC_DF_DELAYED_ACKS * Default value is @ref LSQUIC_DF_DELAYED_ACKS
*/ */
int es_delayed_acks; int es_delayed_acks;
/**
* Enable timestamps extension. Allowed values are 0 and 1.
*
* Default value is @ref LSQUIC_DF_TIMESTAMPS
*/
int es_timestamps;
}; };
/* Initialize `settings' to default values */ /* Initialize `settings' to default values */
@ -1312,7 +1325,7 @@ lsquic_stream_send_headers (lsquic_stream_t *s,
/** /**
* Get header set associated with the stream. The header set is created by * Get header set associated with the stream. The header set is created by
* @ref hsi_create_header_set() callback. After this call, the ownership of * @ref hsi_create_header_set() callback. After this call, the ownership of
* the header set is trasnferred to the caller. * the header set is transferred to the caller.
* *
* This call must precede calls to @ref lsquic_stream_read() and * This call must precede calls to @ref lsquic_stream_read() and
* @ref lsquic_stream_readv(). * @ref lsquic_stream_readv().

View File

@ -558,6 +558,8 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
params.tp_numerics[TPI_MIN_ACK_DELAY] = 10000; /* TODO: make into a constant? make configurable? */ params.tp_numerics[TPI_MIN_ACK_DELAY] = 10000; /* TODO: make into a constant? make configurable? */
params.tp_set |= 1 << TPI_MIN_ACK_DELAY; params.tp_set |= 1 << TPI_MIN_ACK_DELAY;
} }
if (settings->es_timestamps)
params.tp_set |= 1 << TPI_TIMESTAMPS;
len = (enc_sess->esi_conn->cn_version == LSQVER_ID25 ? lsquic_tp_encode_id25 : len = (enc_sess->esi_conn->cn_version == LSQVER_ID25 ? lsquic_tp_encode_id25 :
lsquic_tp_encode)(&params, enc_sess->esi_flags & ESI_SERVER, buf, bufsz); lsquic_tp_encode)(&params, enc_sess->esi_flags & ESI_SERVER, buf, bufsz);
@ -1035,8 +1037,10 @@ iquic_lookup_cert (SSL *ssl, void *arg)
#endif #endif
if (!server_name) if (!server_name)
{ {
LSQ_DEBUG("cert lookup: server name is not set, skip"); LSQ_DEBUG("cert lookup: server name is not set");
return 1; /* SNI is required in HTTP/3 */
if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP)
return 1;
} }
path = enc_sess->esi_conn->cn_if->ci_get_path(enc_sess->esi_conn, NULL); path = enc_sess->esi_conn->cn_if->ci_get_path(enc_sess->esi_conn, NULL);
@ -1049,7 +1053,8 @@ iquic_lookup_cert (SSL *ssl, void *arg)
{ {
if (SSL_set_SSL_CTX(enc_sess->esi_ssl, ssl_ctx)) if (SSL_set_SSL_CTX(enc_sess->esi_ssl, ssl_ctx))
{ {
LSQ_DEBUG("looked up cert for %s", server_name); LSQ_DEBUG("looked up cert for %s", server_name
? server_name : "<no SNI>");
if (enc_sess->esi_enpub->enp_kli) if (enc_sess->esi_enpub->enp_kli)
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
SSL_set_verify(enc_sess->esi_ssl, SSL_set_verify(enc_sess->esi_ssl,
@ -1070,7 +1075,8 @@ iquic_lookup_cert (SSL *ssl, void *arg)
} }
else else
{ {
LSQ_DEBUG("could not look up cert for %s", server_name); LSQ_DEBUG("could not look up cert for %s", server_name
? server_name : "<no SNI>");
return 0; return 0;
} }
} }

View File

@ -335,6 +335,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_ql_bits = LSQUIC_DF_QL_BITS; settings->es_ql_bits = LSQUIC_DF_QL_BITS;
settings->es_spin = LSQUIC_DF_SPIN; settings->es_spin = LSQUIC_DF_SPIN;
settings->es_delayed_acks = LSQUIC_DF_DELAYED_ACKS; settings->es_delayed_acks = LSQUIC_DF_DELAYED_ACKS;
settings->es_timestamps = LSQUIC_DF_TIMESTAMPS;
} }
@ -1636,7 +1637,7 @@ remove_conn_from_hash (lsquic_engine_t *engine, lsquic_conn_t *conn)
static void static void
refflags2str (enum lsquic_conn_flags flags, char s[6]) refflags2str (enum lsquic_conn_flags flags, char s[7])
{ {
*s = 'C'; s += !!(flags & LSCONN_CLOSING); *s = 'C'; s += !!(flags & LSCONN_CLOSING);
*s = 'H'; s += !!(flags & LSCONN_HASHED); *s = 'H'; s += !!(flags & LSCONN_HASHED);

View File

@ -133,6 +133,7 @@ enum ifull_conn_flags
IFC_MIGRA = 1 << 27, IFC_MIGRA = 1 << 27,
IFC_SPIN = 1 << 28, /* Spin bits are enabled */ IFC_SPIN = 1 << 28, /* Spin bits are enabled */
IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */ IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */
IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */
}; };
@ -334,6 +335,7 @@ struct ietf_full_conn
struct transport_params ifc_peer_param; struct transport_params ifc_peer_param;
STAILQ_HEAD(, stream_id_to_ss) STAILQ_HEAD(, stream_id_to_ss)
ifc_stream_ids_to_ss; ifc_stream_ids_to_ss;
lsquic_time_t ifc_created;
lsquic_time_t ifc_saved_ack_received; lsquic_time_t ifc_saved_ack_received;
lsquic_packno_t ifc_max_ack_packno[N_PNS]; lsquic_packno_t ifc_max_ack_packno[N_PNS];
lsquic_packno_t ifc_max_non_probing; lsquic_packno_t ifc_max_non_probing;
@ -1215,6 +1217,7 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
LSQ_DEBUG("negotiating version %s", LSQ_DEBUG("negotiating version %s",
lsquic_ver2str[conn->ifc_u.cli.ifcli_ver_neg.vn_ver]); lsquic_ver2str[conn->ifc_u.cli.ifcli_ver_neg.vn_ver]);
conn->ifc_process_incoming_packet = process_incoming_packet_verneg; conn->ifc_process_incoming_packet = process_incoming_packet_verneg;
conn->ifc_created = now;
LSQ_DEBUG("logging using %s SCID", LSQ_DEBUG("logging using %s SCID",
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "client" : "server"); LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "client" : "server");
return &conn->ifc_conn; return &conn->ifc_conn;
@ -1431,6 +1434,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
if (0 != handshake_ok(&conn->ifc_conn)) if (0 != handshake_ok(&conn->ifc_conn))
goto err3; goto err3;
conn->ifc_created = imc->imc_created;
if (conn->ifc_idle_to) if (conn->ifc_idle_to)
lsquic_alarmset_set(&conn->ifc_alset, AL_IDLE, lsquic_alarmset_set(&conn->ifc_alset, AL_IDLE,
imc->imc_created + conn->ifc_idle_to); imc->imc_created + conn->ifc_idle_to);
@ -1506,6 +1510,33 @@ ietf_full_conn_ci_cancel_pending_streams (struct lsquic_conn *lconn, unsigned n)
} }
/* Best effort. If timestamp frame does not fit, oh well */
static void
generate_timestamp_frame (struct ietf_full_conn *conn,
struct lsquic_packet_out *packet_out, lsquic_time_t now)
{
uint64_t timestamp;
int w;
timestamp = (now - conn->ifc_created) >> TP_DEF_ACK_DELAY_EXP;
w = conn->ifc_conn.cn_pf->pf_gen_timestamp_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), timestamp);
if (w < 0)
{
LSQ_DEBUG("could not generate TIMESTAMP frame");
return;
}
LSQ_DEBUG("generated TIMESTAMP(%"PRIu64" us) frame",
timestamp << TP_DEF_ACK_DELAY_EXP);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated TIMESTAMP(%"
PRIu64" us) frame", timestamp << TP_DEF_ACK_DELAY_EXP);
packet_out->po_frame_types |= 1 << QUIC_FRAME_TIMESTAMP;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w;
}
static int static int
generate_ack_frame_for_pns (struct ietf_full_conn *conn, generate_ack_frame_for_pns (struct ietf_full_conn *conn,
struct lsquic_packet_out *packet_out, enum packnum_space pns, struct lsquic_packet_out *packet_out, enum packnum_space pns,
@ -1561,6 +1592,9 @@ generate_ack_frame_for_pns (struct ietf_full_conn *conn,
lsquic_send_ctl_sanity_check(&conn->ifc_send_ctl); lsquic_send_ctl_sanity_check(&conn->ifc_send_ctl);
LSQ_DEBUG("%s ACK state reset", lsquic_pns2str[pns]); LSQ_DEBUG("%s ACK state reset", lsquic_pns2str[pns]);
if (pns == PNS_APP && (conn->ifc_flags & IFC_TIMESTAMPS))
generate_timestamp_frame(conn, packet_out, now);
return 0; return 0;
} }
@ -3013,6 +3047,12 @@ handshake_ok (struct lsquic_conn *lconn)
LSQ_DEBUG("delayed ACKs enabled"); LSQ_DEBUG("delayed ACKs enabled");
conn->ifc_flags |= IFC_DELAYED_ACKS; conn->ifc_flags |= IFC_DELAYED_ACKS;
} }
if (conn->ifc_settings->es_timestamps
&& (params->tp_set & (1 << TPI_TIMESTAMPS)))
{
LSQ_DEBUG("timestamps enabled");
conn->ifc_flags |= IFC_TIMESTAMPS;
}
conn->ifc_max_peer_ack_usec = params->tp_max_ack_delay * 1000; conn->ifc_max_peer_ack_usec = params->tp_max_ack_delay * 1000;
@ -5583,6 +5623,36 @@ process_ack_frequency_frame (struct ietf_full_conn *conn,
} }
static unsigned
process_timestamp_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
uint64_t timestamp;
int parsed_len;
if (!(conn->ifc_flags & IFC_TIMESTAMPS))
{
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
"Received unexpected TIMESTAMP frame (not negotiated)");
return 0;
}
parsed_len = conn->ifc_conn.cn_pf->pf_parse_timestamp_frame(p, len,
&timestamp);
if (parsed_len < 0)
return 0;
timestamp <<= conn->ifc_cfg.ack_exp;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "TIMESTAMP(%"PRIu64" us)", timestamp);
LSQ_DEBUG("TIMESTAMP(%"PRIu64" us) (%"PRIu64" << %"PRIu8")", timestamp,
timestamp >> conn->ifc_cfg.ack_exp, conn->ifc_cfg.ack_exp);
/* We don't do anything with the timestamp */
return parsed_len;
}
typedef unsigned (*process_frame_f)( typedef unsigned (*process_frame_f)(
struct ietf_full_conn *, struct lsquic_packet_in *, struct ietf_full_conn *, struct lsquic_packet_in *,
const unsigned char *p, size_t); const unsigned char *p, size_t);
@ -5611,6 +5681,7 @@ static process_frame_f const process_frames[N_QUIC_FRAMES] =
[QUIC_FRAME_CRYPTO] = process_crypto_frame, [QUIC_FRAME_CRYPTO] = process_crypto_frame,
[QUIC_FRAME_HANDSHAKE_DONE] = process_handshake_done_frame, [QUIC_FRAME_HANDSHAKE_DONE] = process_handshake_done_frame,
[QUIC_FRAME_ACK_FREQUENCY] = process_ack_frequency_frame, [QUIC_FRAME_ACK_FREQUENCY] = process_ack_frequency_frame,
[QUIC_FRAME_TIMESTAMP] = process_timestamp_frame,
}; };

View File

@ -1049,6 +1049,8 @@ static unsigned (*const imico_process_frames[N_QUIC_FRAMES])
/* STREAM frame can only come in the App PNS and we delay those packets: */ /* STREAM frame can only come in the App PNS and we delay those packets: */
[QUIC_FRAME_STREAM] = imico_process_invalid_frame, [QUIC_FRAME_STREAM] = imico_process_invalid_frame,
[QUIC_FRAME_HANDSHAKE_DONE] = imico_process_invalid_frame, [QUIC_FRAME_HANDSHAKE_DONE] = imico_process_invalid_frame,
[QUIC_FRAME_ACK_FREQUENCY] = imico_process_invalid_frame,
[QUIC_FRAME_TIMESTAMP] = imico_process_invalid_frame,
}; };

View File

@ -384,8 +384,6 @@ lsquic_mm_put_packet_out (struct lsquic_mm *mm,
poolst_freed(&mm->packet_out_bstats[idx]); poolst_freed(&mm->packet_out_bstats[idx]);
if (poolst_has_new_sample(&mm->packet_out_bstats[idx])) if (poolst_has_new_sample(&mm->packet_out_bstats[idx]))
maybe_shrink_packet_out_bufs(mm, idx); maybe_shrink_packet_out_bufs(mm, idx);
if (packet_out->po_bwp_state)
lsquic_malo_put(packet_out->po_bwp_state);
#else #else
free(packet_out->po_data); free(packet_out->po_data);
#endif #endif

View File

@ -35,6 +35,7 @@ enum quic_frame_type
QUIC_FRAME_NEW_TOKEN, /* I */ QUIC_FRAME_NEW_TOKEN, /* I */
QUIC_FRAME_HANDSHAKE_DONE, /* I */ QUIC_FRAME_HANDSHAKE_DONE, /* I */
QUIC_FRAME_ACK_FREQUENCY, /* I */ QUIC_FRAME_ACK_FREQUENCY, /* I */
QUIC_FRAME_TIMESTAMP, /* I */
N_QUIC_FRAMES N_QUIC_FRAMES
}; };
@ -64,6 +65,7 @@ enum quic_ft_bit {
QUIC_FTBIT_RETIRE_CONNECTION_ID = 1 << QUIC_FRAME_RETIRE_CONNECTION_ID, QUIC_FTBIT_RETIRE_CONNECTION_ID = 1 << QUIC_FRAME_RETIRE_CONNECTION_ID,
QUIC_FTBIT_HANDSHAKE_DONE = 1 << QUIC_FRAME_HANDSHAKE_DONE, QUIC_FTBIT_HANDSHAKE_DONE = 1 << QUIC_FRAME_HANDSHAKE_DONE,
QUIC_FTBIT_ACK_FREQUENCY = 1 << QUIC_FRAME_ACK_FREQUENCY, QUIC_FTBIT_ACK_FREQUENCY = 1 << QUIC_FRAME_ACK_FREQUENCY,
QUIC_FTBIT_TIMESTAMP = 1 << QUIC_FRAME_TIMESTAMP,
}; };
static const char * const frame_type_2_str[N_QUIC_FRAMES] = { static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
@ -92,6 +94,7 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
[QUIC_FRAME_RETIRE_CONNECTION_ID] = "QUIC_FRAME_RETIRE_CONNECTION_ID", [QUIC_FRAME_RETIRE_CONNECTION_ID] = "QUIC_FRAME_RETIRE_CONNECTION_ID",
[QUIC_FRAME_HANDSHAKE_DONE] = "QUIC_FRAME_HANDSHAKE_DONE", [QUIC_FRAME_HANDSHAKE_DONE] = "QUIC_FRAME_HANDSHAKE_DONE",
[QUIC_FRAME_ACK_FREQUENCY] = "QUIC_FRAME_ACK_FREQUENCY", [QUIC_FRAME_ACK_FREQUENCY] = "QUIC_FRAME_ACK_FREQUENCY",
[QUIC_FRAME_TIMESTAMP] = "QUIC_FRAME_TIMESTAMP",
}; };
#define QUIC_FRAME_PRELEN (sizeof("QUIC_FRAME_")) #define QUIC_FRAME_PRELEN (sizeof("QUIC_FRAME_"))
@ -128,6 +131,7 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
QUIC_FRAME_SLEN(QUIC_FRAME_NEW_TOKEN) + 1 + \ QUIC_FRAME_SLEN(QUIC_FRAME_NEW_TOKEN) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_HANDSHAKE_DONE) + 1 + \ QUIC_FRAME_SLEN(QUIC_FRAME_HANDSHAKE_DONE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_ACK_FREQUENCY) + 1 + \ QUIC_FRAME_SLEN(QUIC_FRAME_ACK_FREQUENCY) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_TIMESTAMP) + 1 + \
0 0
@ -217,6 +221,7 @@ extern const char *const lsquic_pns2str[];
| QUIC_FTBIT_NEW_TOKEN \ | QUIC_FTBIT_NEW_TOKEN \
| QUIC_FTBIT_HANDSHAKE_DONE \ | QUIC_FTBIT_HANDSHAKE_DONE \
| QUIC_FTBIT_ACK_FREQUENCY \ | QUIC_FTBIT_ACK_FREQUENCY \
| QUIC_FTBIT_TIMESTAMP \
| QUIC_FTBIT_CRYPTO ) | QUIC_FTBIT_CRYPTO )
/* [draft-ietf-quic-transport-24] Section 1.2 */ /* [draft-ietf-quic-transport-24] Section 1.2 */
@ -231,7 +236,7 @@ extern const char *const lsquic_pns2str[];
*/ */
#define IQUIC_FRAME_RETX_MASK ( \ #define IQUIC_FRAME_RETX_MASK ( \
ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_PADDING|QUIC_FTBIT_PATH_RESPONSE \ ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_PADDING|QUIC_FTBIT_PATH_RESPONSE \
|QUIC_FTBIT_PATH_CHALLENGE|QUIC_FTBIT_ACK)) |QUIC_FTBIT_PATH_CHALLENGE|QUIC_FTBIT_ACK|QUIC_FTBIT_TIMESTAMP))
extern const enum quic_ft_bit lsquic_legal_frames_by_level[]; extern const enum quic_ft_bit lsquic_legal_frames_by_level[];

View File

@ -18,7 +18,7 @@ enum PACKET_PUBLIC_FLAGS
#define GQUIC_FRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \ #define GQUIC_FRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_PATH_CHALLENGE) | (1 << QUIC_FRAME_PATH_RESPONSE) \ | (1 << QUIC_FRAME_PATH_CHALLENGE) | (1 << QUIC_FRAME_PATH_RESPONSE) \
| (1 << QUIC_FRAME_STOP_WAITING)) | (1 << QUIC_FRAME_STOP_WAITING) | (1 << QUIC_FRAME_TIMESTAMP))
#define GQUIC_FRAME_REGENERATE(frame_type) ((1 << (frame_type)) & GQUIC_FRAME_REGEN_MASK) #define GQUIC_FRAME_REGENERATE(frame_type) ((1 << (frame_type)) & GQUIC_FRAME_REGEN_MASK)

View File

@ -310,6 +310,10 @@ struct parse_funcs
unsigned unsigned
(*pf_ack_frequency_frame_size) (uint64_t seqno, uint64_t pack_tol, (*pf_ack_frequency_frame_size) (uint64_t seqno, uint64_t pack_tol,
uint64_t upd_mad); uint64_t upd_mad);
int
(*pf_gen_timestamp_frame) (unsigned char *buf, size_t buf_len, uint64_t);
int
(*pf_parse_timestamp_frame) (const unsigned char *buf, size_t, uint64_t *);
}; };

View File

@ -243,5 +243,7 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_ENC_LEVS] =
| QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING | QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING
| QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE | QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE
| QUIC_FTBIT_HANDSHAKE_DONE | QUIC_FTBIT_ACK_FREQUENCY | QUIC_FTBIT_HANDSHAKE_DONE | QUIC_FTBIT_ACK_FREQUENCY
| QUIC_FTBIT_RETIRE_CONNECTION_ID | QUIC_FTBIT_NEW_TOKEN, | QUIC_FTBIT_RETIRE_CONNECTION_ID | QUIC_FTBIT_NEW_TOKEN
| QUIC_FTBIT_TIMESTAMP
,
}; };

View File

@ -46,6 +46,9 @@
#define CHECK_SPACE(need, pstart, pend) \ #define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0) do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
#define FRAME_TYPE_ACK_FREQUENCY 0xAF
#define FRAME_TYPE_TIMESTAMP 0x2F5
static int static int
ietf_v1_gen_one_varint (unsigned char *, size_t, unsigned char, uint64_t); ietf_v1_gen_one_varint (unsigned char *, size_t, unsigned char, uint64_t);
@ -1109,7 +1112,8 @@ ietf_v1_parse_frame_type (const unsigned char *buf, size_t len)
if (s > 0 && (unsigned) s == (1u << vint_val2bits(val))) if (s > 0 && (unsigned) s == (1u << vint_val2bits(val)))
switch (val) switch (val)
{ {
case 0xAF: return QUIC_FRAME_ACK_FREQUENCY; case FRAME_TYPE_ACK_FREQUENCY: return QUIC_FRAME_ACK_FREQUENCY;
case FRAME_TYPE_TIMESTAMP: return QUIC_FRAME_TIMESTAMP;
default: break; default: break;
} }
@ -2059,7 +2063,7 @@ ietf_v1_gen_ack_frequency_frame (unsigned char *buf, size_t buf_len,
uint64_t seqno, uint64_t pack_tol, uint64_t upd_mad) uint64_t seqno, uint64_t pack_tol, uint64_t upd_mad)
{ {
return ietf_v1_gen_frame_with_varints(buf, buf_len, 4, return ietf_v1_gen_frame_with_varints(buf, buf_len, 4,
(uint64_t[]){ 0xAF, seqno, pack_tol, upd_mad }); (uint64_t[]){ FRAME_TYPE_ACK_FREQUENCY, seqno, pack_tol, upd_mad });
} }
@ -2068,7 +2072,8 @@ ietf_v1_parse_ack_frequency_frame (const unsigned char *buf, size_t buf_len,
uint64_t *seqno, uint64_t *pack_tol, uint64_t *upd_mad) uint64_t *seqno, uint64_t *pack_tol, uint64_t *upd_mad)
{ {
return ietf_v1_parse_frame_with_varints(buf, buf_len, return ietf_v1_parse_frame_with_varints(buf, buf_len,
0xAF, 3, (uint64_t *[]) { seqno, pack_tol, upd_mad }); FRAME_TYPE_ACK_FREQUENCY,
3, (uint64_t *[]) { seqno, pack_tol, upd_mad });
} }
@ -2077,7 +2082,7 @@ ietf_v1_ack_frequency_frame_size (uint64_t seqno, uint64_t pack_tol,
uint64_t upd_mad) uint64_t upd_mad)
{ {
return ietf_v1_frame_with_varints_size(4, return ietf_v1_frame_with_varints_size(4,
(uint64_t[]){ 0xAF, seqno, pack_tol, upd_mad }); (uint64_t[]){ FRAME_TYPE_ACK_FREQUENCY, seqno, pack_tol, upd_mad });
} }
@ -2088,6 +2093,24 @@ ietf_v1_handshake_done_frame_size (void)
} }
static int
ietf_v1_gen_timestamp_frame (unsigned char *buf, size_t buf_len,
uint64_t timestamp)
{
return ietf_v1_gen_frame_with_varints(buf, buf_len, 2,
(uint64_t[]){ FRAME_TYPE_TIMESTAMP, timestamp });
}
static int
ietf_v1_parse_timestamp_frame (const unsigned char *buf, size_t buf_len,
uint64_t *timestamp)
{
return ietf_v1_parse_frame_with_varints(buf, buf_len,
FRAME_TYPE_TIMESTAMP, 1, (uint64_t *[]) { timestamp });
}
const struct parse_funcs lsquic_parse_funcs_ietf_v1 = const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
{ {
.pf_gen_reg_pkt_header = ietf_v1_gen_reg_pkt_header, .pf_gen_reg_pkt_header = ietf_v1_gen_reg_pkt_header,
@ -2156,4 +2179,6 @@ const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
.pf_gen_ack_frequency_frame = ietf_v1_gen_ack_frequency_frame, .pf_gen_ack_frequency_frame = ietf_v1_gen_ack_frequency_frame,
.pf_parse_ack_frequency_frame = ietf_v1_parse_ack_frequency_frame, .pf_parse_ack_frequency_frame = ietf_v1_parse_ack_frequency_frame,
.pf_ack_frequency_frame_size = ietf_v1_ack_frequency_frame_size, .pf_ack_frequency_frame_size = ietf_v1_ack_frequency_frame_size,
.pf_gen_timestamp_frame = ietf_v1_gen_timestamp_frame,
.pf_parse_timestamp_frame = ietf_v1_parse_timestamp_frame,
}; };

View File

@ -52,6 +52,7 @@ tpi_val_2_enum (uint64_t tpi_val)
#endif #endif
case 0x1057: return TPI_LOSS_BITS; case 0x1057: return TPI_LOSS_BITS;
case 0xDE1A: return TPI_MIN_ACK_DELAY; case 0xDE1A: return TPI_MIN_ACK_DELAY;
case 0x7157: return TPI_TIMESTAMPS;
default: return INT_MAX; default: return INT_MAX;
} }
} }
@ -79,6 +80,7 @@ static const unsigned enum_2_tpi_val[LAST_TPI + 1] =
#endif #endif
[TPI_LOSS_BITS] = 0x1057, [TPI_LOSS_BITS] = 0x1057,
[TPI_MIN_ACK_DELAY] = 0xDE1A, [TPI_MIN_ACK_DELAY] = 0xDE1A,
[TPI_TIMESTAMPS] = 0x7157,
}; };
@ -104,6 +106,7 @@ static const char * const tpi2str[LAST_TPI + 1] =
#endif #endif
[TPI_LOSS_BITS] = "loss_bits", [TPI_LOSS_BITS] = "loss_bits",
[TPI_MIN_ACK_DELAY] = "min_ack_delay", [TPI_MIN_ACK_DELAY] = "min_ack_delay",
[TPI_TIMESTAMPS] = "timestamps",
}; };
@ -378,6 +381,7 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
sizeof(params->tp_preferred_address.srst)); sizeof(params->tp_preferred_address.srst));
break; break;
case TPI_DISABLE_ACTIVE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
*p++ = 0; *p++ = 0;
break; break;
#if LSQUIC_TEST_QUANTUM_READINESS #if LSQUIC_TEST_QUANTUM_READINESS
@ -500,6 +504,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
} }
break; break;
case TPI_DISABLE_ACTIVE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
EXPECT_LEN(0); EXPECT_LEN(0);
break; break;
case TPI_STATELESS_RESET_TOKEN: case TPI_STATELESS_RESET_TOKEN:
@ -615,7 +620,7 @@ lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
return; return;
} }
for (; tpi <= MAX_EMPTY_TPI; ++tpi) for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (params->tp_set & (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL)) if (params->tp_set & (1 << tpi))
{ {
nw = snprintf(buf, end - buf, "%.*s%s", nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[tpi]); (buf + sz > end) << 1, "; ", tpi2str[tpi]);
@ -825,6 +830,7 @@ lsquic_tp_encode_id25 (const struct transport_params *params, int is_server,
sizeof(params->tp_preferred_address.srst)); sizeof(params->tp_preferred_address.srst));
break; break;
case TPI_DISABLE_ACTIVE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
WRITE_UINT_TO_P(0, 16); WRITE_UINT_TO_P(0, 16);
break; break;
#if LSQUIC_TEST_QUANTUM_READINESS #if LSQUIC_TEST_QUANTUM_READINESS
@ -948,6 +954,7 @@ lsquic_tp_decode_id25 (const unsigned char *const buf, size_t bufsz,
} }
break; break;
case TPI_DISABLE_ACTIVE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_TIMESTAMPS:
EXPECT_LEN(0); EXPECT_LEN(0);
break; break;
case TPI_STATELESS_RESET_TOKEN: case TPI_STATELESS_RESET_TOKEN:

View File

@ -38,6 +38,7 @@ enum transport_param_id
/* /*
* Empty transport parameters: * Empty transport parameters:
*/ */
TPI_TIMESTAMPS,
TPI_DISABLE_ACTIVE_MIGRATION, MAX_EMPTY_TPI = TPI_DISABLE_ACTIVE_MIGRATION, TPI_DISABLE_ACTIVE_MIGRATION, MAX_EMPTY_TPI = TPI_DISABLE_ACTIVE_MIGRATION,
/* /*

View File

@ -371,7 +371,10 @@ prog_connect (struct prog *prog, unsigned char *zero_rtt, size_t zero_rtt_len)
if (NULL == lsquic_engine_connect(prog->prog_engine, N_LSQVER, if (NULL == lsquic_engine_connect(prog->prog_engine, N_LSQVER,
(struct sockaddr *) &sport->sp_local_addr, (struct sockaddr *) &sport->sp_local_addr,
(struct sockaddr *) &sport->sas, sport, NULL, (struct sockaddr *) &sport->sas, sport, NULL,
prog->prog_hostname ? prog->prog_hostname : sport->host, prog->prog_hostname ? prog->prog_hostname
/* SNI is required for HTTP */
: prog->prog_engine_flags & LSENG_HTTP ? sport->host
: NULL,
prog->prog_max_packet_size, zero_rtt, zero_rtt_len, prog->prog_max_packet_size, zero_rtt, zero_rtt_len,
sport->sp_token_buf, sport->sp_token_sz)) sport->sp_token_buf, sport->sp_token_sz))
return -1; return -1;

View File

@ -134,21 +134,27 @@ struct ssl_ctx_st *
lookup_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED, lookup_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED,
const char *sni) const char *sni)
{ {
struct lsquic_hash_elem *el;
struct server_cert *server_cert;
if (!cert_lu_ctx) if (!cert_lu_ctx)
return NULL; return NULL;
if (!sni)
if (sni)
el = lsquic_hash_find(cert_lu_ctx, sni, strlen(sni));
else
{ {
LSQ_INFO("SNI is not set"); LSQ_INFO("SNI is not set");
return NULL; el = lsquic_hash_first(cert_lu_ctx);
} }
struct lsquic_hash_elem *el = lsquic_hash_find(cert_lu_ctx, sni, strlen(sni));
struct server_cert *server_cert = NULL;
if (el) if (el)
{ {
server_cert = lsquic_hashelem_getdata(el); server_cert = lsquic_hashelem_getdata(el);
if (server_cert) if (server_cert)
return server_cert->ce_ssl_ctx; return server_cert->ce_ssl_ctx;
} }
return NULL; return NULL;
} }

View File

@ -1802,6 +1802,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_honor_prst = atoi(val); settings->es_honor_prst = atoi(val);
return 0; return 0;
} }
if (0 == strncmp(name, "timestamps", 10))
{
settings->es_timestamps = atoi(val);
return 0;
}
break; break;
case 11: case 11:
if (0 == strncmp(name, "ping_period", 11)) if (0 == strncmp(name, "ping_period", 11))