Release 2.20.0

- [FEATURE] QUIC and HTTP/3 Internet Draft 30 support.
- [FEATURE] Unreliable Datagram Extension support.
- [FEATURE] Adaptive congestion controller.
- [BUGFIX] Do not send MAX_STREAM_DATA frames on crypto streams.
- [BUGFIX] Fail with CRYPTO_BUFFER_EXCEEDED when too much CRYPTO
  data comes in.
- [BUFFIX] Spin bit is now strictly per path; value is reset on
  DCID change.
- [BUGFIX] Check that max value of max_streams_uni and
  max_streams_bidi TPs is 2^60.
- [BUGFIX] Close IETF mini conn immediately if crypto session
  cannot be initialized.
- Deprecate ID-28 (no browser uses it): it's no longer in the
  default versions list.
- New programs duck_server and duck_client that implement the
  experimental siduck-00 protocol.  They quack!
- IETF crypto streams: don't limit ourselves from sending.
- Command-line programs: turn off QL loss bits if -G is used, as
  Wireshark cannot decrypt QUIC packets when this extension is used.
- Turn all h3 framing unit tests back on.
- Fix malo initialization when compiled in no-pool mode.
This commit is contained in:
Dmitri Tikhonov 2020-09-15 16:42:13 -04:00
parent c3c69ba3bb
commit b1a7c3f944
53 changed files with 1745 additions and 161 deletions

View File

@ -1,3 +1,27 @@
2020-09-15
- 2.20.0
- [FEATURE] QUIC and HTTP/3 Internet Draft 30 support.
- [FEATURE] Unreliable Datagram Extension support.
- [FEATURE] Adaptive congestion controller.
- [BUGFIX] Do not send MAX_STREAM_DATA frames on crypto streams.
- [BUGFIX] Fail with CRYPTO_BUFFER_EXCEEDED when too much CRYPTO
data comes in.
- [BUFFIX] Spin bit is now strictly per path; value is reset on
DCID change.
- [BUGFIX] Check that max value of max_streams_uni and
max_streams_bidi TPs is 2^60.
- [BUGFIX] Close IETF mini conn immediately if crypto session
cannot be initialized.
- Deprecate ID-28 (no browser uses it): it's no longer in the
default versions list.
- New programs duck_server and duck_client that implement the
experimental siduck-00 protocol. They quack!
- IETF crypto streams: don't limit ourselves from sending.
- Command-line programs: turn off QL loss bits if -G is used, as
Wireshark cannot decrypt QUIC packets when this extension is used.
- Turn all h3 framing unit tests back on.
- Fix malo initialization when compiled in no-pool mode.
2020-09-08
- 2.19.10
- [FEATURE] Add lsquic_stream_pwritev(). This function allows one to

View File

@ -265,6 +265,8 @@ add_executable(md5_client bin/md5_client.c bin/prog.c bin/test_common.c bin/test
ENDIF()
add_executable(echo_server bin/echo_server.c bin/prog.c bin/test_common.c bin/test_cert.c ${GETOPT_C})
add_executable(echo_client bin/echo_client.c bin/prog.c bin/test_common.c bin/test_cert.c ${GETOPT_C})
add_executable(duck_server bin/duck_server.c bin/prog.c bin/test_common.c bin/test_cert.c ${GETOPT_C})
add_executable(duck_client bin/duck_client.c bin/prog.c bin/test_common.c bin/test_cert.c ${GETOPT_C})
IF (NOT MSVC)
@ -300,6 +302,8 @@ TARGET_LINK_LIBRARIES(md5_client ${LIBS})
ENDIF()
TARGET_LINK_LIBRARIES(echo_server ${LIBS})
TARGET_LINK_LIBRARIES(echo_client ${LIBS})
TARGET_LINK_LIBRARIES(duck_server ${LIBS})
TARGET_LINK_LIBRARIES(duck_client ${LIBS})
add_subdirectory(bin)
ENDIF() # LSQUIC_BIN

View File

@ -32,6 +32,14 @@ See bin/http_{client,server}.c
This pair of programs is to demonstrate how to use HTTP features of QUIC.
HTTP server is interoperable with proto-quic's quic_client.
Duck client and server
----------------------
See bin/duck_{client,server}.c
This pair of programs implement the siduck-00 protocol. They provide an
illustration of using the datagram API.
Usage Examples
--------------

View File

@ -15,8 +15,9 @@ 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!
Currently supported QUIC versions are Q043, Q046, Q050, ID-27, ID-28, and ID-29.
Support for newer versions will be added soon after they are released.
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.
Documentation
-------------

189
bin/duck_client.c Normal file
View File

@ -0,0 +1,189 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* duck_client.c -- The siduck client. See
* https://tools.ietf.org/html/draft-pardue-quic-siduck-00
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <fcntl.h>
#include <unistd.h>
#define Read read
#else
#include "vc_compat.h"
#include "getopt.h"
#include <io.h>
#define Read _read
#define STDIN_FILENO 0
#endif
#include <event2/event.h>
#include "lsquic.h"
#include "test_common.h"
#include "prog.h"
#include "../src/liblsquic/lsquic_logger.h"
/* Expected request and response of the siduck protocol */
#define REQUEST "quack"
#define RESPONSE "quack-ack"
static lsquic_conn_ctx_t *
duck_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
{
LSQ_NOTICE("created a new connection");
return stream_if_ctx;
}
static void
duck_client_on_hsk_done (lsquic_conn_t *conn, enum lsquic_hsk_status s)
{
if (s == LSQ_HSK_OK || s == LSQ_HSK_RESUMED_OK)
{
if (lsquic_conn_want_datagram_write(conn, 1) < 0)
LSQ_ERROR("want_datagram_write failed");
}
}
static void
duck_client_on_conn_closed (lsquic_conn_t *conn)
{
lsquic_conn_ctx_t *ctx = lsquic_conn_get_ctx(conn);
LSQ_NOTICE("Connection closed, stop client");
prog_stop((struct prog *) ctx);
}
static ssize_t
duck_client_on_dg_write (lsquic_conn_t *conn, void *buf, size_t sz)
{
int s;
/* We only write one request */
s = lsquic_conn_want_datagram_write(conn, 0);
assert(s == 1); /* Old value was "yes, we want to write a datagram" */
if (sz >= sizeof(REQUEST) - 1)
{
LSQ_INFO("wrote `%s' in request", REQUEST);
memcpy(buf, REQUEST, sizeof(REQUEST) - 1);
return sizeof(REQUEST) - 1;
}
else
return -1;
}
static void
duck_client_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
{
if (bufsz == sizeof(RESPONSE) - 1
&& 0 == memcmp(buf, RESPONSE, sizeof(RESPONSE) - 1))
{
LSQ_DEBUG("received the expected `%s' response", RESPONSE);
lsquic_conn_close(conn);
}
else
{
LSQ_NOTICE("unexpected request received, abort connection");
lsquic_conn_abort(conn);
}
}
const struct lsquic_stream_if duck_client_stream_if = {
.on_new_conn = duck_client_on_new_conn,
.on_hsk_done = duck_client_on_hsk_done,
.on_conn_closed = duck_client_on_conn_closed,
.on_dg_write = duck_client_on_dg_write,
.on_datagram = duck_client_on_datagram,
};
static void
usage (const char *prog)
{
const char *const slash = strrchr(prog, '/');
if (slash)
prog = slash + 1;
LSQ_NOTICE(
"Usage: %s [opts]\n"
"\n"
"Options:\n"
, prog);
}
int
main (int argc, char **argv)
{
int opt, s;
struct sport_head sports;
struct prog prog;
TAILQ_INIT(&sports);
prog_init(&prog, 0, &sports, &duck_client_stream_if, &prog);
prog.prog_settings.es_datagrams = 1;
prog.prog_settings.es_init_max_data = 0;
prog.prog_settings.es_init_max_streams_bidi = 0;
prog.prog_settings.es_init_max_streams_uni = 0;
prog.prog_settings.es_max_streams_in = 0;
prog.prog_api.ea_alpn = "siduck-00";
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
{
switch (opt) {
case 'h':
usage(argv[0]);
prog_print_common_options(&prog, stdout);
exit(0);
default:
if (0 != prog_set_opt(&prog, opt, optarg))
exit(1);
}
}
#ifndef WIN32
int flags = fcntl(STDIN_FILENO, F_GETFL);
flags |= O_NONBLOCK;
if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
{
perror("fcntl");
exit(1);
}
#else
{
u_long on = 1;
ioctlsocket(STDIN_FILENO, FIONBIO, &on);
}
#endif
if (0 != prog_prep(&prog))
{
LSQ_ERROR("could not prep");
exit(EXIT_FAILURE);
}
if (0 != prog_connect(&prog, NULL, 0))
{
LSQ_ERROR("could not connect");
exit(EXIT_FAILURE);
}
LSQ_DEBUG("entering event loop");
s = prog_run(&prog);
prog_cleanup(&prog);
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
}

160
bin/duck_server.c Normal file
View File

@ -0,0 +1,160 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* A duck quacks! The server for the siduck protocol:
* https://tools.ietf.org/html/draft-pardue-quic-siduck-00
*/
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <time.h>
#ifndef WIN32
#include <unistd.h>
#include <netinet/in.h>
#else
#include "vc_compat.h"
#include "getopt.h"
#endif
#include "lsquic.h"
#include "../src/liblsquic/lsquic_hash.h"
#include "test_common.h"
#include "test_cert.h"
#include "prog.h"
#include "../src/liblsquic/lsquic_logger.h"
static lsquic_conn_ctx_t *
duck_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
{
LSQ_NOTICE("New siduck connection established!");
return NULL;
}
static void
duck_server_on_conn_closed (lsquic_conn_t *conn)
{
LSQ_NOTICE("siduck connection closed");
}
/* Expected request and response of the siduck protocol */
#define REQUEST "quack"
#define RESPONSE "quack-ack"
static ssize_t
duck_on_dg_write (lsquic_conn_t *conn, void *buf, size_t sz)
{
int s;
/* We only write one response */
s = lsquic_conn_want_datagram_write(conn, 0);
assert(s == 1); /* Old value was "yes" */
if (sz >= sizeof(RESPONSE) - 1)
{
LSQ_INFO("wrote `%s' in response", RESPONSE);
memcpy(buf, RESPONSE, sizeof(RESPONSE) - 1);
lsquic_conn_close(conn); /* Close connection right away */
return sizeof(RESPONSE) - 1;
}
else
return -1;
}
static void
duck_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
{
int s;
if (bufsz == sizeof(REQUEST) - 1
&& 0 == memcmp(buf, REQUEST, sizeof(REQUEST) - 1))
{
LSQ_DEBUG("received the expected `%s' request", REQUEST);
s = lsquic_conn_want_datagram_write(conn, 1);
assert(s == 0);
}
else
{
LSQ_NOTICE("unexpected request received, abort connection");
lsquic_conn_abort(conn);
}
}
const struct lsquic_stream_if duck_server_stream_if = {
.on_new_conn = duck_server_on_new_conn,
.on_conn_closed = duck_server_on_conn_closed,
.on_dg_write = duck_on_dg_write,
.on_datagram = duck_on_datagram,
};
static void
usage (const char *prog)
{
const char *const slash = strrchr(prog, '/');
if (slash)
prog = slash + 1;
printf(
"Usage: %s [opts]\n"
"\n"
"Options:\n"
, prog);
}
int
main (int argc, char **argv)
{
int opt, s;
struct prog prog;
struct sport_head sports;
TAILQ_INIT(&sports);
prog_init(&prog, LSENG_SERVER, &sports, &duck_server_stream_if, NULL);
prog.prog_settings.es_datagrams = 1;
prog.prog_settings.es_init_max_data = 0;
prog.prog_settings.es_init_max_streams_bidi = 0;
prog.prog_settings.es_init_max_streams_uni = 0;
prog.prog_settings.es_max_streams_in = 0;
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
{
switch (opt) {
case 'h':
usage(argv[0]);
prog_print_common_options(&prog, stdout);
exit(0);
default:
if (0 != prog_set_opt(&prog, opt, optarg))
exit(1);
}
}
if (0 != add_alpn("siduck-00"))
{
LSQ_ERROR("could not add ALPN");
exit(EXIT_FAILURE);
}
if (0 != prog_prep(&prog))
{
LSQ_ERROR("could not prep");
exit(EXIT_FAILURE);
}
LSQ_DEBUG("entering event loop");
s = prog_run(&prog);
prog_cleanup(&prog);
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@ -29,9 +29,11 @@
#include <openssl/md5.h>
#include "lsquic.h"
#include "../src/liblsquic/lsquic_hash.h"
#include "lsxpack_header.h"
#include "test_config.h"
#include "test_common.h"
#include "test_cert.h"
#include "prog.h"
#if HAVE_REGEX
@ -1764,6 +1766,7 @@ main (int argc, char **argv)
struct stat st;
struct server_ctx server_ctx;
struct prog prog;
const char *const *alpn;
#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX)
fprintf(stderr, "cannot run server without regex or open_memstream\n");
@ -1846,6 +1849,18 @@ main (int argc, char **argv)
exit(EXIT_FAILURE);
}
alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions);
while (*alpn)
{
if (0 == add_alpn(*alpn))
++alpn;
else
{
LSQ_ERROR("cannot add ALPN %s", *alpn);
exit(EXIT_FAILURE);
}
}
if (0 != prog_prep(&prog))
{
LSQ_ERROR("could not prep");

View File

@ -357,6 +357,12 @@ prog_set_opt (struct prog *prog, int opt, const char *arg)
}
}
prog->prog_keylog_dir = optarg;
if (prog->prog_settings.es_ql_bits)
{
LSQ_NOTICE("QL loss bits turned off because of -G. If you want "
"to turn it on, just override: -G dir -o ql_bits=2");
prog->prog_settings.es_ql_bits = 0;
}
return 0;
#else
LSQ_ERROR("key logging is not supported on Windows");

View File

@ -17,15 +17,36 @@
#include "test_cert.h"
static char s_alpn[0x100];
int
add_alpn (const char *alpn)
{
size_t alpn_len, all_len;
alpn_len = strlen(alpn);
if (alpn_len > 255)
return -1;
all_len = strlen(s_alpn);
if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn))
return -1;
s_alpn[all_len] = alpn_len;
memcpy(&s_alpn[all_len + 1], alpn, alpn_len);
s_alpn[all_len + 1 + alpn_len] = '\0';
return 0;
}
static int
select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
const unsigned char alpn[] = "\x5h3-27\x5h3-28\x5h3-29";
int r;
r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,
alpn, sizeof(alpn));
(unsigned char *) s_alpn, strlen(s_alpn));
if (r == OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_OK;
else

View File

@ -24,5 +24,7 @@ lookup_cert (void *cert_lu_ctx, const struct sockaddr * /*unused */,
void
delete_certs (struct lsquic_hash *);
int
add_alpn (const char *alpn);
#endif

View File

@ -58,6 +58,10 @@ developed by the IETF. Both types are included in a single enum:
IETF QUIC version ID 29
.. member:: LSQVER_ID30
IETF QUIC version ID 30
.. member:: N_LSQVER
Special value indicating the number of versions in the enum. It
@ -695,11 +699,25 @@ settings structure:
Congestion control algorithm to use.
- 0: Use default (:macro:`LSQUIC_DF_CC_ALGO)`
- 0: Use default (:macro:`LSQUIC_DF_CC_ALGO`)
- 1: Cubic
- 2: BBR
- 2: BBRv1
- 3: Adaptive congestion control.
IETF QUIC only.
Adaptive congestion control adapts to the environment. It figures
out whether to use Cubic or BBRv1 based on the RTT.
.. member:: unsigned es_cc_rtt_thresh
Congestion controller RTT threshold in microseconds.
Adaptive congestion control uses BBRv1 until RTT is determined. At
that point a permanent choice of congestion controller is made. If
RTT is smaller than or equal to
:member:`lsquic_engine_settings.es_cc_rtt_thresh`, congestion
controller is switched to Cubic; otherwise, BBRv1 is picked.
The default value is :macro:`LSQUIC_DF_CC_RTT_THRESH`
.. member:: int es_ql_bits
@ -802,6 +820,20 @@ settings structure:
Default value is :macro:`LSQUIC_DF_GREASE_QUIC_BIT`
.. member:: int es_datagrams
Enable datagrams extension. Allowed values are 0 and 1.
Default value is :macro:`LSQUIC_DF_DATAGRAMS`
.. member:: int es_optimistic_nat
If set to true, changes in peer port are assumed to be due to a
benign NAT rebinding and path characteristics -- MTU, RTT, and
CC state -- are not reset.
Default value is :macro:`LSQUIC_DF_OPTIMISTIC_NAT`
To initialize the settings structure to library defaults, use the following
convenience function:
@ -971,7 +1003,11 @@ out of date. Please check your :file:`lsquic.h` for actual values.*
.. macro:: LSQUIC_DF_CC_ALGO
Use Cubic by default.
Use Adaptive Congestion Controller by default.
.. macro:: LSQUIC_DF_CC_RTT_THRESH
Default value of the CC RTT threshold is 1500 microseconds
.. macro:: LSQUIC_DF_DELAYED_ACKS
@ -1010,6 +1046,18 @@ out of date. Please check your :file:`lsquic.h` for actual values.*
By default, greasing the QUIC bit is enabled (if peer sent
the "grease_quic_bit" transport parameter).
.. macro:: LSQUIC_DF_TIMESTAMPS
Timestamps are on by default.
.. macro:: LSQUIC_DF_DATAGRAMS
Datagrams are off by default.
.. macro:: LSQUIC_DF_OPTIMISTIC_NAT
Assume optimistic NAT by default.
Receiving Packets
-----------------
@ -1220,6 +1268,19 @@ the engine to communicate with the user code:
This callback is optional.
.. member:: ssize_t (*on_dg_write)(lsquic_conn_t *c, void *buf, size_t buf_sz)
Called when datagram is ready to be written. Write at most
``buf_sz`` bytes to ``buf`` and return number of bytes
written.
.. member:: void (*on_datagram)(lsquic_conn_t *c, const void *buf, size_t sz)
Called when datagram is read from a packet. This callback is
required when :member:`lsquic_engine_settings.es_datagrams` is true.
Take care to process it quickly, as this is called during
:func:`lsquic_engine_packet_in()`.
Creating Connections
--------------------
@ -2077,7 +2138,7 @@ List of Log Modules
The following log modules are defined:
- *alarmset*: Alarm processing.
- *bbr*: BBR congestion controller.
- *bbr*: BBRv1 congestion controller.
- *bw-sampler*: Bandwidth sampler (used by BBR).
- *cfcw*: Connection flow control window.
- *conn*: Connection.
@ -2115,3 +2176,36 @@ The following log modules are defined:
- *stream*: Stream operation.
- *tokgen*: Token generation and validation.
- *trapa*: Transport parameter processing.
.. _apiref-datagrams:
Datagrams
---------
lsquic supports the
`Unreliable Datagram Extension <https://tools.ietf.org/html/draft-pauly-quic-datagram-05>`_.
To enable datagrams, set :member:`lsquic_engine_settings.es_datagrams` to
true and specify
:member:`lsquic_stream_if.on_datagram`
and
:member:`lsquic_stream_if.on_dg_write` callbacks.
.. function:: int lsquic_conn_want_datagram_write (lsquic_conn_t *conn, int want)
Indicate desire (or lack thereof) to write a datagram.
:param conn: Connection on which to send a datagram.
:param want: Boolean value indicating whether the caller wants to write
a datagram.
:return: Previous value of ``want`` or ``-1`` if the datagrams cannot be
written.
.. function:: size_t lsquic_conn_get_min_datagram_size (lsquic_conn_t *conn)
Get minimum datagram size. By default, this value is zero.
.. function:: int lsquic_conn_set_min_datagram_size (lsquic_conn_t *conn, size_t sz)
Set minimum datagram size. This is the minumum value of the buffer
passed to the :member:`lsquic_stream_if.on_dg_write` callback.
Returns 0 on success and -1 on error.

View File

@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
author = u'LiteSpeed Technologies'
# The short X.Y version
version = u'2.19'
version = u'2.20'
# The full version, including alpha/beta/rc tags
release = u'2.19.10'
release = u'2.20.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,
and ID-29.
ID-29, and ID-30.
Support for newer versions will be added soon after they are released.
LSQUIC is licensed under the `MIT License`_; see LICENSE in the source
@ -37,6 +37,7 @@ LSQUIC supports nearly all QUIC and HTTP/3 features, including
- TLS Key updates
- Extensions:
- :ref:`apiref-datagrams`
- Loss bits extension (allowing network observer to locate source of packet loss)
- Timestamps extension (allowing for one-way delay calculation, improving performance of some congestion controllers)
- Delayed ACKs (this reduces number of ACK frames sent and processed, improving throughput)

View File

@ -24,8 +24,8 @@ extern "C" {
#endif
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 19
#define LSQUIC_PATCH_VERSION 10
#define LSQUIC_MINOR_VERSION 20
#define LSQUIC_PATCH_VERSION 0
/**
* Engine flags:
@ -91,6 +91,11 @@ enum lsquic_version
*/
LSQVER_ID29,
/**
* IETF QUIC Draft-30
*/
LSQVER_ID30,
/**
* Special version to trigger version negotiation.
* [draft-ietf-quic-transport-11], Section 3.
@ -101,7 +106,8 @@ enum lsquic_version
};
/**
* We currently support versions 43, 46, 50, Draft-27, Draft-28, and Draft-29.
* We currently support versions 43, 46, 50, Draft-27, Draft-28, Draft-29,
* and Draft-30.
* @see lsquic_version
*/
#define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1)
@ -114,15 +120,15 @@ enum lsquic_version
#define LSQUIC_EXPERIMENTAL_VERSIONS ( \
(1 << LSQVER_VERNEG) | LSQUIC_EXPERIMENTAL_Q098)
#define LSQUIC_DEPRECATED_VERSIONS 0
#define LSQUIC_DEPRECATED_VERSIONS (1 << LSQVER_ID28)
#define LSQUIC_GQUIC_HEADER_VERSIONS (1 << LSQVER_043)
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \
| (1 << LSQVER_ID29) | (1 << LSQVER_VERNEG))
| (1 << LSQVER_ID29) | (1 << LSQVER_ID30) | (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \
| (1 << LSQVER_ID29) | (1 << LSQVER_VERNEG))
| (1 << LSQVER_ID29) | (1 << LSQVER_ID30) | (1 << LSQVER_VERNEG))
enum lsquic_hsk_status
{
@ -179,6 +185,13 @@ struct lsquic_stream_if {
void (*on_read) (lsquic_stream_t *s, lsquic_stream_ctx_t *h);
void (*on_write) (lsquic_stream_t *s, lsquic_stream_ctx_t *h);
void (*on_close) (lsquic_stream_t *s, lsquic_stream_ctx_t *h);
/* Called when datagram is ready to be written */
ssize_t (*on_dg_write)(lsquic_conn_t *c, void *, size_t);
/* Called when datagram is read from a packet. This callback is required
* when es_datagrams is true. Take care to process it quickly, as this
* is called during lsquic_engine_packet_in().
*/
void (*on_datagram)(lsquic_conn_t *, const void *buf, size_t);
/* This callback in only called in client mode */
/**
* When handshake is completed, this optional callback is called.
@ -338,8 +351,17 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
/** Turn on timestamp extension by default */
#define LSQUIC_DF_TIMESTAMPS 1
/* 1: Cubic; 2: BBR */
#define LSQUIC_DF_CC_ALGO 1
/* Use Adaptive CC by default */
#define LSQUIC_DF_CC_ALGO 3
/* Default value of the CC RTT threshold is 1.5 ms */
#define LSQUIC_DF_CC_RTT_THRESH 1500
/** Turn off datagram extension by default */
#define LSQUIC_DF_DATAGRAMS 0
/** Assume optimistic NAT by default. */
#define LSQUIC_DF_OPTIMISTIC_NAT 1
/** By default, incoming packet size is not limited. */
#define LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX 0
@ -565,6 +587,8 @@ struct lsquic_engine_settings {
* @ref lsquic_stream_wantread() or @ref lsquic_stream_wantwrite()
* or shuts down the stream.
*
* This also applies to the on_dg_write() callback.
*
* The default value is @ref LSQUIC_DF_RW_ONCE.
*/
int es_rw_once;
@ -605,10 +629,23 @@ struct lsquic_engine_settings {
*
* 0: Use default (@ref LSQUIC_DF_CC_ALGO)
* 1: Cubic
* 2: BBR
* 2: BBRv1
* 3: Adaptive (Cubic or BBRv1)
*/
unsigned es_cc_algo;
/**
* Congestion controller RTT threshold in microseconds.
*
* Adaptive congestion control uses BBRv1 until RTT is determined. At
* that point a permanent choice of congestion controller is made. If
* RTT is smaller than or equal to es_cc_rtt_thresh, congestion
* controller is switched to Cubic; otherwise, BBRv1 is picked.
*
* The default value is @ref LSQUIC_DF_CC_RTT_THRESH.
*/
unsigned es_cc_rtt_thresh;
/**
* No progress timeout.
*
@ -878,6 +915,22 @@ struct lsquic_engine_settings {
* Default value is @ref LSQUIC_DF_MTU_PROBE_TIMER.
*/
unsigned es_mtu_probe_timer;
/**
* Enable datagram extension. Allowed values are 0 and 1.
*
* Default value is @ref LSQUIC_DF_DATAGRAMS
*/
int es_datagrams;
/**
* If set to true, changes in peer port are assumed to be due to a
* benign NAT rebinding and path characteristics -- MTU, RTT, and
* CC state -- are not reset.
*
* Default value is @ref LSQUIC_DF_OPTIMISTIC_NAT.
*/
int es_optimistic_nat;
};
/* Initialize `settings' to default values */
@ -1587,6 +1640,20 @@ int
lsquic_conn_get_sockaddr(lsquic_conn_t *c,
const struct sockaddr **local, const struct sockaddr **peer);
/* Returns previous value */
int
lsquic_conn_want_datagram_write (lsquic_conn_t *, int is_want);
/* Get minimum datagram size. By default, this value is zero. */
size_t
lsquic_conn_get_min_datagram_size (lsquic_conn_t *);
/* Set minimum datagram size. This is the minumum value of the buffer passed
* to the on_dg_write() callback.
*/
int
lsquic_conn_set_min_datagram_size (lsquic_conn_t *, size_t sz);
struct lsquic_logger_if {
int (*log_buf)(void *logger_ctx, const char *buf, size_t len);
};

View File

@ -1,6 +1,7 @@
# Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE.
SET(lsquic_STAT_SRCS
ls-qpack/lsqpack.c
lsquic_adaptive_cc.c
lsquic_alarmset.c
lsquic_arr.c
lsquic_attq.c

View File

@ -0,0 +1,212 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/* lsquic_adaptive_cc.c -- adaptive congestion controller */
#include <inttypes.h>
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#ifdef WIN32
#include <vc_compat.h>
#endif
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_hash.h"
#include "lsquic_util.h"
#include "lsquic_cong_ctl.h"
#include "lsquic_sfcw.h"
#include "lsquic_conn_flow.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_stream.h"
#include "lsquic_rtt.h"
#include "lsquic_conn_public.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_cubic.h"
#include "lsquic_adaptive_cc.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ADAPTIVE_CC
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(acc->acc_cubic.cu_conn)
#include "lsquic_logger.h"
#define CALL_BOTH(method, ...) do { \
lsquic_cong_bbr_if.method(&acc->acc_bbr, __VA_ARGS__); \
lsquic_cong_cubic_if.method(&acc->acc_cubic, __VA_ARGS__); \
} while (0)
#define CALL_BOTH_MAYBE(method, ...) do { \
if (lsquic_cong_bbr_if.method) \
lsquic_cong_bbr_if.method(&acc->acc_bbr, __VA_ARGS__); \
if (lsquic_cong_cubic_if.method) \
lsquic_cong_cubic_if.method(&acc->acc_cubic, __VA_ARGS__); \
} while (0)
#define CALL_BOTH0(method) do { \
lsquic_cong_bbr_if.method(&acc->acc_bbr); \
lsquic_cong_cubic_if.method(&acc->acc_cubic); \
} while (0)
static void
adaptive_cc_init (void *cong_ctl, const struct lsquic_conn_public *conn_pub,
enum quic_ft_bit retx_frames)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH(cci_init, conn_pub, retx_frames);
LSQ_DEBUG("initialized");
}
static void
adaptive_cc_reinit (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_reinit);
}
static void
adaptive_cc_ack (void *cong_ctl, struct lsquic_packet_out *packet_out,
unsigned packet_sz, lsquic_time_t now, int app_limited)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH(cci_ack, packet_out, packet_sz, now, app_limited);
}
static void
adaptive_cc_loss (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_loss);
}
static void
adaptive_cc_begin_ack (void *cong_ctl, lsquic_time_t ack_time,
uint64_t in_flight)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_begin_ack, ack_time, in_flight);
}
static void
adaptive_cc_end_ack (void *cong_ctl, uint64_t in_flight)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_end_ack, in_flight);
}
static void
adaptive_cc_sent (void *cong_ctl, struct lsquic_packet_out *packet_out,
uint64_t in_flight, int app_limited)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_sent, packet_out, in_flight, app_limited);
}
static void
adaptive_cc_lost (void *cong_ctl, struct lsquic_packet_out *packet_out,
unsigned packet_sz)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_lost, packet_out, packet_sz);
}
static void
adaptive_cc_timeout (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_timeout);
}
static void
adaptive_cc_was_quiet (void *cong_ctl, lsquic_time_t now, uint64_t in_flight)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH(cci_was_quiet, now, in_flight);
}
static uint64_t
adaptive_cc_get_cwnd (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
uint64_t rv[2];
rv[0] = lsquic_cong_cubic_if.cci_get_cwnd(&acc->acc_cubic);
rv[1] = lsquic_cong_bbr_if.cci_get_cwnd(&acc->acc_bbr);
if (acc->acc_flags & ACC_CUBIC)
return rv[0];
else
return rv[1];
}
static uint64_t
adaptive_cc_pacing_rate (void *cong_ctl, int in_recovery)
{
struct adaptive_cc *const acc = cong_ctl;
uint64_t rv[2];
rv[0] = lsquic_cong_cubic_if.cci_pacing_rate(&acc->acc_cubic, in_recovery);
rv[1] = lsquic_cong_bbr_if.cci_pacing_rate(&acc->acc_bbr, in_recovery);
if (acc->acc_flags & ACC_CUBIC)
return rv[0];
else
return rv[1];
}
static void
adaptive_cc_cleanup (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_cleanup);
LSQ_DEBUG("cleanup");
}
const struct cong_ctl_if lsquic_cong_adaptive_if =
{
.cci_ack = adaptive_cc_ack,
.cci_begin_ack = adaptive_cc_begin_ack,
.cci_end_ack = adaptive_cc_end_ack,
.cci_cleanup = adaptive_cc_cleanup,
.cci_get_cwnd = adaptive_cc_get_cwnd,
.cci_init = adaptive_cc_init,
.cci_pacing_rate = adaptive_cc_pacing_rate,
.cci_loss = adaptive_cc_loss,
.cci_lost = adaptive_cc_lost,
.cci_reinit = adaptive_cc_reinit,
.cci_timeout = adaptive_cc_timeout,
.cci_sent = adaptive_cc_sent,
.cci_was_quiet = adaptive_cc_was_quiet,
};

View File

@ -0,0 +1,24 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_adaptive_cc.h -- Adaptive congestion controller
*
* The controller begins using BBRv1, but keeps Cubic state as well.
* When RTT is known, we pick either Cubic (small RTT) or BBRv1 (large
* RTT).
*/
#ifndef LSQUIC_ADAPTIVE_CC_H
#define LSQUIC_ADAPTIVE_CC_H 1
struct adaptive_cc
{
struct lsquic_cubic acc_cubic;
struct lsquic_bbr acc_bbr;
enum {
ACC_CUBIC, /* If set, use Cubic; otherwise, use BBR */
} acc_flags;
};
extern const struct cong_ctl_if lsquic_cong_adaptive_if;
#endif

View File

@ -58,7 +58,10 @@ lsquic_bw_sampler_cleanup (struct bw_sampler *sampler)
if (sampler->bws_conn)
LSQ_DEBUG("cleanup");
if (sampler->bws_malo)
{
lsquic_malo_destroy(sampler->bws_malo);
sampler->bws_malo = NULL;
}
}

View File

@ -267,3 +267,33 @@ lsquic_conn_log_cid (const struct lsquic_conn *lconn)
return lconn->cn_if->ci_get_log_cid(lconn);
return CN_SCID(lconn);
}
int
lsquic_conn_want_datagram_write (struct lsquic_conn *lconn, int is_want)
{
if (lconn->cn_if && lconn->cn_if->ci_want_datagram_write)
return lconn->cn_if->ci_want_datagram_write(lconn, is_want);
else
return -1;
}
int
lsquic_conn_set_min_datagram_size (struct lsquic_conn *lconn, size_t sz)
{
if (lconn->cn_if && lconn->cn_if->ci_set_min_datagram_size)
return lconn->cn_if->ci_set_min_datagram_size(lconn, sz);
else
return -1;
}
size_t
lsquic_conn_get_min_datagram_size (struct lsquic_conn *lconn)
{
if (lconn->cn_if && lconn->cn_if->ci_get_min_datagram_size)
return lconn->cn_if->ci_get_min_datagram_size(lconn);
else
return 0;
}

View File

@ -91,6 +91,12 @@ struct ack_state
uint32_t arr[6];
};
struct to_coal
{
const struct lsquic_packet_out *prev_packet;
size_t prev_sz_sum;
};
struct conn_iface
{
enum tick_st
@ -108,7 +114,7 @@ struct conn_iface
* for by the congestion controller.
*/
struct lsquic_packet_out *
(*ci_next_packet_to_send) (struct lsquic_conn *, size_t);
(*ci_next_packet_to_send) (struct lsquic_conn *, const struct to_coal *);
void
(*ci_packet_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
@ -270,6 +276,18 @@ struct conn_iface
void
(*ci_ack_rollback) (struct lsquic_conn *, struct ack_state *);
/* Optional method. */
int
(*ci_want_datagram_write) (struct lsquic_conn *, int);
/* Optional method */
int
(*ci_set_min_datagram_size) (struct lsquic_conn *, size_t);
/* Optional method */
size_t
(*ci_get_min_datagram_size) (struct lsquic_conn *);
};
#define LSCONN_CCE_BITS 3

View File

@ -342,6 +342,7 @@ extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
ver == LSQVER_ID27 ? &lsquic_enc_session_common_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_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \
&lsquic_enc_session_common_gquic_1 )

View File

@ -74,7 +74,8 @@ static const struct alpn_map {
{ LSQVER_ID27, (unsigned char *) "\x05h3-27", },
{ LSQVER_ID28, (unsigned char *) "\x05h3-28", },
{ LSQVER_ID29, (unsigned char *) "\x05h3-29", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-29", },
{ LSQVER_ID30, (unsigned char *) "\x05h3-30", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-30", },
};
struct enc_sess_iquic;
@ -571,7 +572,6 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
#endif
}
#if LSQUIC_TEST_QUANTUM_READINESS
else
{
const char *s = getenv("LSQUIC_TEST_QUANTUM_READINESS");
if (s && atoi(s))
@ -627,6 +627,16 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
params.tp_numerics[TPI_TIMESTAMPS] = TS_GENERATE_THEM;
params.tp_set |= 1 << TPI_TIMESTAMPS;
}
if (settings->es_datagrams)
{
if (params.tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
= params.tp_max_udp_payload_size;
else
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
= TP_DEF_MAX_UDP_PAYLOAD_SIZE;
params.tp_set |= 1 << TPI_MAX_DATAGRAM_FRAME_SIZE;
}
len = (version == LSQVER_ID27 ? lsquic_tp_encode_27 : lsquic_tp_encode)(
&params, enc_sess->esi_flags & ESI_SERVER, buf, bufsz);
@ -1220,7 +1230,11 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
SSL_CTX *ssl_ctx = NULL;
union {
char errbuf[ERR_ERROR_STRING_BUF_LEN];
unsigned char trans_params[sizeof(struct transport_params)];
unsigned char trans_params[sizeof(struct transport_params)
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
} u;
if (enc_sess->esi_enpub->enp_alpn)
@ -1396,7 +1410,7 @@ init_client (struct enc_sess_iquic *const enc_sess)
#define hexbuf errbuf /* This is a dual-purpose buffer */
unsigned char trans_params[0x80
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + QUANTUM_READY_SZ
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
@ -2018,6 +2032,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
packet_out->po_sent_sz = dst_sz;
packet_out->po_flags &= ~PO_IPv6;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT);
packet_out->po_dcid_len = packet_out->po_path->np_dcid.len;
lsquic_packet_out_set_enc_level(packet_out, enc_level);
lsquic_packet_out_set_kp(packet_out, enc_sess->esi_key_phase);

View File

@ -58,6 +58,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_set.h"
#include "lsquic_conn_flow.h"
#include "lsquic_sfcw.h"
@ -359,6 +360,9 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_grease_quic_bit = LSQUIC_DF_GREASE_QUIC_BIT;
settings->es_mtu_probe_timer = LSQUIC_DF_MTU_PROBE_TIMER;
settings->es_dplpmtud = LSQUIC_DF_DPLPMTUD;
settings->es_cc_algo = LSQUIC_DF_CC_ALGO;
settings->es_cc_rtt_thresh = LSQUIC_DF_CC_RTT_THRESH;
settings->es_optimistic_nat = LSQUIC_DF_OPTIMISTIC_NAT;
}
@ -418,7 +422,7 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
return -1;
}
if (settings->es_cc_algo > 2)
if (settings->es_cc_algo > 3)
{
if (err_buf)
snprintf(err_buf, err_buf_sz, "Invalid congestion control "
@ -2472,8 +2476,11 @@ send_packets_out (struct lsquic_engine *engine,
#endif
&& iov < batch->iov + sizeof(batch->iov) / sizeof(batch->iov[0]))
{
const size_t size = iov_size(packet_iov, iov);
packet_out = conn->cn_if->ci_next_packet_to_send(conn, size);
const struct to_coal to_coal = {
.prev_packet = packet_out,
.prev_sz_sum = iov_size(packet_iov, iov),
};
packet_out = conn->cn_if->ci_next_packet_to_send(conn, &to_coal);
if (packet_out)
goto next_coa;
}
@ -2829,7 +2836,7 @@ lsquic_engine_packet_in (lsquic_engine_t *engine,
break;
}
/* [draft-ietf-quic-transport-27] Section 12.2:
/* [draft-ietf-quic-transport-30] Section 12.2:
* " Receivers SHOULD ignore any subsequent packets with a different
* " Destination Connection ID than the first packet in the datagram.
*/

View File

@ -42,6 +42,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_set.h"
#include "lsquic_malo.h"
#include "lsquic_chsk_stream.h"
@ -3670,10 +3671,11 @@ full_conn_ci_packet_in (lsquic_conn_t *lconn, lsquic_packet_in_t *packet_in)
static lsquic_packet_out_t *
full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *unused)
{
struct full_conn *conn = (struct full_conn *) lconn;
return lsquic_send_ctl_next_packet_to_send(&conn->fc_send_ctl, 0);
return lsquic_send_ctl_next_packet_to_send(&conn->fc_send_ctl, NULL);
}

View File

@ -42,6 +42,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_alarmset.h"
#include "lsquic_ver_neg.h"
@ -132,9 +133,10 @@ enum ifull_conn_flags
IFC_IGNORE_HSK = 1 << 25,
IFC_PROC_CRYPTO = 1 << 26,
IFC_MIGRA = 1 << 27,
IFC_SPIN = 1 << 28, /* Spin bits are enabled */
IFC_UNUSED28 = 1 << 28, /* Unused */
IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */
IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */
IFC_DATAGRAMS = 1u<< 31, /* Datagrams are enabled */
};
@ -146,6 +148,7 @@ enum more_flags
MF_IGNORE_MISSING = 1 << 3,
MF_CONN_CLOSE_PACK = 1 << 4, /* CONNECTION_CLOSE has been packetized */
MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */
MF_WANT_DATAGRAM_WRITE = 1 << 6,
};
@ -305,6 +308,7 @@ struct conn_path
struct network_path cop_path;
uint64_t cop_path_chals[8]; /* Arbitrary number */
uint64_t cop_inc_chal; /* Incoming challenge */
lsquic_packno_t cop_max_packno;
enum {
/* Initialized covers cop_path.np_pack_size and cop_path.np_dcid */
COP_INITIALIZED = 1 << 0,
@ -316,9 +320,12 @@ struct conn_path
* original path.
*/
COP_GOT_NONPROB = 1 << 2,
/* Spin bit is enabled on this path. */
COP_SPIN_BIT = 1 << 3,
} cop_flags;
unsigned char cop_n_chals;
unsigned char cop_cce_idx;
unsigned char cop_spin_bit;
struct dplpmtud_state cop_dplpmtud;
};
@ -336,6 +343,8 @@ struct ietf_full_conn
struct lsquic_conn ifc_conn;
struct conn_cid_elem ifc_cces[MAX_SCID];
struct lsquic_rechist ifc_rechist[N_PNS];
/* App PNS only, used to calculate was_missing: */
lsquic_packno_t ifc_max_ackable_packno_in;
struct lsquic_send_ctl ifc_send_ctl;
struct lsquic_stream *ifc_stream_hcsi; /* HTTP Control Stream Incoming */
struct lsquic_stream *ifc_stream_hcso; /* HTTP Control Stream Outgoing */
@ -359,7 +368,6 @@ struct ietf_full_conn
struct conn_err ifc_error;
unsigned ifc_n_delayed_streams;
unsigned ifc_n_cons_unretx;
int ifc_spin_bit;
const struct lsquic_stream_if
*ifc_stream_if;
void *ifc_stream_ctx;
@ -448,6 +456,8 @@ struct ietf_full_conn
lsquic_time_t ifc_idle_to;
lsquic_time_t ifc_ping_period;
uint64_t ifc_last_max_data_off_sent;
unsigned short ifc_min_dg_sz,
ifc_max_dg_sz;
struct inc_ack_stats ifc_ias;
struct ack_info ifc_ack;
};
@ -459,6 +469,9 @@ struct ietf_full_conn
#define DCES_END(conn_) ((conn_)->ifc_dces + (sizeof((conn_)->ifc_dces) \
/ sizeof((conn_)->ifc_dces[0])))
#define NPATH2CPATH(npath_) ((struct conn_path *) \
((char *) (npath_) - offsetof(struct conn_path, cop_path)))
static const struct ver_neg server_ver_neg;
static const struct conn_iface *ietf_full_conn_iface_ptr;
@ -1098,20 +1111,16 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn,
* " connection IDs.
*/
static void
maybe_enable_spin (struct ietf_full_conn *conn)
maybe_enable_spin (struct ietf_full_conn *conn, struct conn_path *cpath)
{
uint8_t nyb;
if (!conn->ifc_settings->es_spin)
if (conn->ifc_settings->es_spin
&& lsquic_crand_get_nybble(conn->ifc_enpub->enp_crand))
{
conn->ifc_flags &= ~IFC_SPIN;
LSQ_DEBUG("spin bit disabled via settings");
}
else if (lsquic_crand_get_nybble(conn->ifc_enpub->enp_crand))
{
conn->ifc_flags |= IFC_SPIN;
conn->ifc_spin_bit = 0;
LSQ_DEBUG("spin bit enabled");
cpath->cop_flags |= COP_SPIN_BIT;
cpath->cop_spin_bit = 0;
LSQ_DEBUG("spin bit enabled on path %hhu", cpath->cop_path.np_path_id);
}
else
{
@ -1120,11 +1129,13 @@ maybe_enable_spin (struct ietf_full_conn *conn)
* " independently for each connection ID.
* (ibid.)
*/
conn->ifc_flags &= ~IFC_SPIN;
cpath->cop_flags &= ~COP_SPIN_BIT;
nyb = lsquic_crand_get_nybble(conn->ifc_enpub->enp_crand);
conn->ifc_spin_bit = nyb & 1;
LSQ_DEBUG("spin bit randomly disabled; random spin bit value is %d",
conn->ifc_spin_bit);
cpath->cop_spin_bit = nyb & 1;
LSQ_DEBUG("spin bit disabled %s on path %hhu; random spin bit "
"value is %hhu",
!conn->ifc_settings->es_spin ? "via settings" : "randomly",
cpath->cop_path.np_path_id, cpath->cop_spin_bit);
}
}
@ -1186,6 +1197,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
conn->ifc_max_ack_packno[PNS_INIT] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_APP] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ackable_packno_in = 0;
conn->ifc_paths[0].cop_path.np_path_id = 0;
conn->ifc_paths[1].cop_path.np_path_id = 1;
conn->ifc_paths[2].cop_path.np_path_id = 2;
@ -1194,7 +1206,6 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
conn->ifc_max_req_id = VINT_MAX_VALUE + 1;
conn->ifc_ping_unretx_thresh = 20;
conn->ifc_max_retx_since_last_ack = MAX_RETR_PACKETS_SINCE_LAST_ACK;
maybe_enable_spin(conn);
if (conn->ifc_settings->es_noprogress_timeout)
conn->ifc_mflags |= MF_NOPROG_TIMEOUT;
return 0;
@ -1403,6 +1414,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
conn->ifc_paths[0].cop_path = imc->imc_path;
conn->ifc_paths[0].cop_flags = COP_VALIDATED|COP_INITIALIZED;
conn->ifc_used_paths = 1 << 0;
maybe_enable_spin(conn, &conn->ifc_paths[0]);
if (imc->imc_flags & IMC_ADDR_VALIDATED)
lsquic_send_ctl_path_validated(&conn->ifc_send_ctl);
else
@ -2720,6 +2732,28 @@ ietf_full_conn_ci_write_ack (struct lsquic_conn *lconn,
}
static int
ietf_full_conn_ci_want_datagram_write (struct lsquic_conn *lconn, int is_want)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
int old;
if (conn->ifc_flags & IFC_DATAGRAMS)
{
old = !!(conn->ifc_mflags & MF_WANT_DATAGRAM_WRITE);
if (is_want)
conn->ifc_mflags |= MF_WANT_DATAGRAM_WRITE;
else
conn->ifc_mflags &= ~MF_WANT_DATAGRAM_WRITE;
LSQ_DEBUG("turn %s \"want datagram write\" flag",
is_want ? "on" : "off");
return old;
}
else
return -1;
}
static void
ietf_full_conn_ci_client_call_on_new (struct lsquic_conn *lconn)
{
@ -2840,6 +2874,8 @@ ietf_full_conn_ci_retire_cid (struct lsquic_conn *lconn)
* Switch DCID.
*/
*CUR_DCID(conn) = (*dces[0])->de_cid;
if (CUR_CPATH(conn)->cop_flags & COP_SPIN_BIT)
CUR_CPATH(conn)->cop_spin_bit = 0;
LSQ_INFOC("switched DCID to %"CID_FMT, CID_BITS(CUR_DCID(conn)));
/*
* Mark old DCID for retirement.
@ -3348,6 +3384,15 @@ handshake_ok (struct lsquic_conn *lconn)
LSQ_DEBUG("timestamps enabled: will send TIMESTAMP frames");
conn->ifc_flags |= IFC_TIMESTAMPS;
}
if (conn->ifc_settings->es_datagrams
&& (params->tp_set & (1 << TPI_MAX_DATAGRAM_FRAME_SIZE)))
{
LSQ_DEBUG("datagrams enabled");
conn->ifc_flags |= IFC_DATAGRAMS;
conn->ifc_max_dg_sz =
params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE] > USHRT_MAX
? USHRT_MAX : params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE];
}
conn->ifc_max_peer_ack_usec = params->tp_max_ack_delay * 1000;
@ -3766,6 +3811,11 @@ ietf_full_conn_ci_is_tickable (struct lsquic_conn *lconn)
LSQ_DEBUG("tickable: send DATA_BLOCKED frame");
goto check_can_send;
}
if (conn->ifc_mflags & MF_WANT_DATAGRAM_WRITE)
{
LSQ_DEBUG("tickable: want to write DATAGRAM frame");
goto check_can_send;
}
if (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE ?
lsquic_send_ctl_has_buffered(&conn->ifc_send_ctl) :
lsquic_send_ctl_has_buffered_high(&conn->ifc_send_ctl))
@ -4375,26 +4425,32 @@ generate_path_resp_3 (struct ietf_full_conn *conn, lsquic_time_t now)
static struct lsquic_packet_out *
ietf_full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
ietf_full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
struct lsquic_packet_out *packet_out;
const struct conn_path *cpath;
packet_out = lsquic_send_ctl_next_packet_to_send(&conn->ifc_send_ctl, size);
packet_out = lsquic_send_ctl_next_packet_to_send(&conn->ifc_send_ctl,
to_coal);
if (packet_out)
lsquic_packet_out_set_spin_bit(packet_out, conn->ifc_spin_bit);
{
cpath = NPATH2CPATH(packet_out->po_path);
lsquic_packet_out_set_spin_bit(packet_out, cpath->cop_spin_bit);
}
return packet_out;
}
static struct lsquic_packet_out *
ietf_full_conn_ci_next_packet_to_send_pre_hsk (struct lsquic_conn *lconn,
size_t size)
const struct to_coal *to_coal)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
struct lsquic_packet_out *packet_out;
packet_out = ietf_full_conn_ci_next_packet_to_send(lconn, size);
packet_out = ietf_full_conn_ci_next_packet_to_send(lconn, to_coal);
if (packet_out)
++conn->ifc_u.cli.ifcli_packets_out;
return packet_out;
@ -4618,16 +4674,55 @@ maybe_retire_dcid (struct ietf_full_conn *conn, const lsquic_cid_t *dcid)
}
/* Return true if the two paths differ only in peer port */
static int
only_peer_port_changed (const struct network_path *old,
struct network_path *new)
{
const struct sockaddr *old_sa, *new_sa;
if (!lsquic_sockaddr_eq(NP_LOCAL_SA(old), NP_LOCAL_SA(new)))
return 0;
old_sa = NP_PEER_SA(old);
new_sa = NP_PEER_SA(new);
if (old_sa->sa_family == AF_INET)
return old_sa->sa_family == new_sa->sa_family
&& ((struct sockaddr_in *) old_sa)->sin_addr.s_addr
== ((struct sockaddr_in *) new_sa)->sin_addr.s_addr
&& ((struct sockaddr_in *) old_sa)->sin_port
!= /* NE! */((struct sockaddr_in *) new_sa)->sin_port;
else
return old_sa->sa_family == new_sa->sa_family
&& ((struct sockaddr_in6 *) old_sa)->sin6_port != /* NE! */
((struct sockaddr_in6 *) new_sa)->sin6_port
&& 0 == memcmp(&((struct sockaddr_in6 *) old_sa)->sin6_addr,
&((struct sockaddr_in6 *) new_sa)->sin6_addr,
sizeof(((struct sockaddr_in6 *) new_sa)->sin6_addr));
}
static void
switch_path_to (struct ietf_full_conn *conn, unsigned char path_id)
{
const unsigned char old_path_id = conn->ifc_cur_path_id;
const int keep_path_properties = conn->ifc_settings->es_optimistic_nat
&& only_peer_port_changed(CUR_NPATH(conn),
&conn->ifc_paths[path_id].cop_path);
assert(conn->ifc_cur_path_id != path_id);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "switched paths");
if (keep_path_properties)
{
conn->ifc_paths[path_id].cop_path.np_pack_size
= CUR_NPATH(conn)->np_pack_size;
LSQ_DEBUG("keep path properties: set MTU to %hu",
conn->ifc_paths[path_id].cop_path.np_pack_size);
}
lsquic_send_ctl_repath(&conn->ifc_send_ctl,
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path);
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path,
keep_path_properties);
maybe_retire_dcid(conn, &CUR_NPATH(conn)->np_dcid);
conn->ifc_cur_path_id = path_id;
conn->ifc_pub.path = CUR_NPATH(conn);
@ -5600,7 +5695,11 @@ insert_new_dcid (struct ietf_full_conn *conn, uint64_t seqno,
memcpy((*dce)->de_srst, token, sizeof((*dce)->de_srst));
(*dce)->de_flags |= DE_SRST;
if (update_cur_dcid)
{
*CUR_DCID(conn) = *cid;
if (CUR_CPATH(conn)->cop_flags & COP_SPIN_BIT)
CUR_CPATH(conn)->cop_spin_bit = 0;
}
}
else
LSQ_WARN("cannot allocate dce to insert DCID seqno %"PRIu64, seqno);
@ -5742,7 +5841,7 @@ process_retire_connection_id_frame (struct ietf_full_conn *conn,
{
if (LSQUIC_CIDS_EQ(&cce->cce_cid, &packet_in->pi_dcid))
{
ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR, "cannot retire CID "
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION, "cannot retire CID "
"seqno=%"PRIu64", for it is used as DCID in the packet", seqno);
return 0;
}
@ -6012,6 +6111,35 @@ process_timestamp_frame (struct ietf_full_conn *conn,
}
static unsigned
process_datagram_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
const void *data;
size_t data_sz;
int parsed_len;
if (!(conn->ifc_flags & IFC_DATAGRAMS))
{
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
"Received unexpected DATAGRAM frame (not negotiated)");
return 0;
}
parsed_len = conn->ifc_conn.cn_pf->pf_parse_datagram_frame(p, len,
&data, &data_sz);
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "%zd-byte DATAGRAM", data_sz);
LSQ_DEBUG("%zd-byte DATAGRAM", data_sz);
conn->ifc_enpub->enp_stream_if->on_datagram(&conn->ifc_conn, data, data_sz);
return parsed_len;
}
typedef unsigned (*process_frame_f)(
struct ietf_full_conn *, struct lsquic_packet_in *,
const unsigned char *p, size_t);
@ -6041,6 +6169,7 @@ static process_frame_f const process_frames[N_QUIC_FRAMES] =
[QUIC_FRAME_HANDSHAKE_DONE] = process_handshake_done_frame,
[QUIC_FRAME_ACK_FREQUENCY] = process_ack_frequency_frame,
[QUIC_FRAME_TIMESTAMP] = process_timestamp_frame,
[QUIC_FRAME_DATAGRAM] = process_datagram_frame,
};
@ -6254,9 +6383,22 @@ force_queueing_ack_app (struct ietf_full_conn *conn)
}
enum was_missing {
/* Note that particular enum values matter for speed */
WM_NONE = 0,
WM_MAX_GAP = 1, /* Newly arrived ackable packet introduced a gap in incoming
* packet number sequence.
*/
WM_SMALLER = 2, /* Newly arrived ackable packet is smaller than previously
* seen maximum number.
*/
};
static void
try_queueing_ack_app (struct ietf_full_conn *conn,
int was_missing, int ecn, lsquic_time_t now)
enum was_missing was_missing, int ecn, lsquic_time_t now)
{
lsquic_time_t srtt, ack_timeout;
@ -6268,8 +6410,10 @@ try_queueing_ack_app (struct ietf_full_conn *conn,
*/
|| (ecn == ECN_CE
&& lsquic_send_ctl_ecn_turned_on(&conn->ifc_send_ctl))
|| (was_missing == WM_MAX_GAP)
|| ((conn->ifc_flags & IFC_ACK_HAD_MISS)
&& was_missing && conn->ifc_n_slack_akbl[PNS_APP] > 0)
&& was_missing == WM_SMALLER
&& conn->ifc_n_slack_akbl[PNS_APP] > 0)
|| many_in_and_will_write(conn))
{
lsquic_alarmset_unset(&conn->ifc_alset, AL_ACK_APP);
@ -6279,7 +6423,7 @@ try_queueing_ack_app (struct ietf_full_conn *conn,
"was_missing: %d",
lsquic_pns2str[PNS_APP], conn->ifc_n_slack_akbl[PNS_APP],
conn->ifc_n_slack_all,
!!(conn->ifc_flags & IFC_ACK_HAD_MISS), was_missing);
!!(conn->ifc_flags & IFC_ACK_HAD_MISS), (int) was_missing);
}
else if (conn->ifc_n_slack_akbl[PNS_APP] > 0)
{
@ -6310,17 +6454,6 @@ try_queueing_ack_init_or_hsk (struct ietf_full_conn *conn,
}
static void
try_queueing_ack (struct ietf_full_conn *conn, enum packnum_space pns,
int was_missing, int ecn, lsquic_time_t now)
{
if (PNS_APP == pns)
try_queueing_ack_app(conn, was_missing, ecn, now);
else
try_queueing_ack_init_or_hsk(conn, pns);
}
static int
maybe_queue_opp_ack (struct ietf_full_conn *conn)
{
@ -6437,6 +6570,8 @@ process_retry_packet (struct ietf_full_conn *conn,
return -1;
*CUR_DCID(conn) = scid;
if (CUR_CPATH(conn)->cop_flags & COP_SPIN_BIT)
CUR_CPATH(conn)->cop_spin_bit = 0;
lsquic_alarmset_unset(&conn->ifc_alset, AL_RETX_INIT);
lsquic_alarmset_unset(&conn->ifc_alset, AL_RETX_HSK);
lsquic_alarmset_unset(&conn->ifc_alset, AL_RETX_APP);
@ -6509,9 +6644,6 @@ on_dcid_change (struct ietf_full_conn *conn, const lsquic_cid_t *dcid_in)
LSQ_DEBUGC("%s: set SCID to %"CID_FMT, __func__, CID_BITS(CN_SCID(lconn)));
LOG_SCIDS(conn);
/* Reset spin bit, see [draft-ietf-quic-transport-20] Section 17.3.1 */
maybe_enable_spin(conn);
return 0;
}
@ -6568,15 +6700,33 @@ record_dcid (struct ietf_full_conn *conn,
}
static int
holes_after (struct lsquic_rechist *rechist, lsquic_packno_t packno)
{
const struct lsquic_packno_range *first_range;
first_range = lsquic_rechist_peek(rechist);
/* If it's not in the very first range, there is obviously a gap
* between it and the maximum packet number. If the packet number
* in question preceeds the cutoff, we assume that there are no
* holes (as we simply have no information).
*/
return first_range
&& packno < first_range->low
&& packno > lsquic_rechist_cutoff(rechist);
}
static int
process_regular_packet (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in)
{
struct conn_path *cpath;
enum packnum_space pns;
enum received_st st;
enum dec_packin dec_packin;
enum quic_ft_bit frame_types;
int was_missing, packno_increased;
enum was_missing was_missing;
unsigned n_rechist_packets;
unsigned char saved_path_id;
if (HETY_RETRY == packet_in->pi_header_type)
@ -6679,8 +6829,7 @@ process_regular_packet (struct ietf_full_conn *conn,
EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in);
packno_increased = packet_in->pi_packno
> lsquic_rechist_largest_packno(&conn->ifc_rechist[pns]);
n_rechist_packets = lsquic_rechist_n_packets(&conn->ifc_rechist[pns]);
st = lsquic_rechist_received(&conn->ifc_rechist[pns], packet_in->pi_packno,
packet_in->pi_received);
switch (st) {
@ -6704,31 +6853,81 @@ process_regular_packet (struct ietf_full_conn *conn,
if (lsquic_packet_in_non_probing(packet_in)
&& packet_in->pi_packno > conn->ifc_max_non_probing)
conn->ifc_max_non_probing = packet_in->pi_packno;
if (0 == (conn->ifc_flags & (IFC_ACK_QUED_INIT << pns)))
/* From [draft-ietf-quic-transport-30] Section 13.2.1:
*
" In order to assist loss detection at the sender, an endpoint SHOULD
" generate and send an ACK frame without delay when it receives an ack-
" eliciting packet either:
"
" * when the received packet has a packet number less than another
" ack-eliciting packet that has been received, or
"
" * when the packet has a packet number larger than the highest-
" numbered ack-eliciting packet that has been received and there are
" missing packets between that packet and this packet.
*
*/
if (packet_in->pi_frame_types & IQUIC_FRAME_ACKABLE_MASK)
{
frame_types = packet_in->pi_frame_types;
if (frame_types & IQUIC_FRAME_ACKABLE_MASK)
if (PNS_APP == pns /* was_missing is only used in PNS_APP */)
{
was_missing = packet_in->pi_packno !=
lsquic_rechist_largest_packno(&conn->ifc_rechist[pns]);
++conn->ifc_n_slack_akbl[pns];
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 */
&& conn->ifc_max_ackable_packno_in + 1
< packet_in->pi_packno
&& holes_after(&conn->ifc_rechist[PNS_APP],
conn->ifc_max_ackable_packno_in);
conn->ifc_max_ackable_packno_in = packet_in->pi_packno;
}
else
was_missing = (enum was_missing) /* WM_SMALLER is 2 */
/* The check is necessary (rather setting was_missing to
* WM_SMALLER) because we cannot guarantee that peer does
* not have bugs.
*/
((packet_in->pi_packno
< conn->ifc_max_ackable_packno_in) << 1);
}
else
was_missing = 0;
conn->ifc_n_slack_all += PNS_APP == pns;
try_queueing_ack(conn, pns, was_missing,
was_missing = WM_NONE;
++conn->ifc_n_slack_akbl[pns];
}
else
was_missing = WM_NONE;
conn->ifc_n_slack_all += PNS_APP == pns;
if (0 == (conn->ifc_flags & (IFC_ACK_QUED_INIT << pns)))
{
if (PNS_APP == pns)
try_queueing_ack_app(conn, was_missing,
lsquic_packet_in_ecn(packet_in), packet_in->pi_received);
else
try_queueing_ack_init_or_hsk(conn, pns);
}
conn->ifc_incoming_ecn <<= 1;
conn->ifc_incoming_ecn |=
lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT;
++conn->ifc_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
if (packno_increased && PNS_APP == pns && (conn->ifc_flags & IFC_SPIN))
if (PNS_APP == pns
&& (cpath = &conn->ifc_paths[packet_in->pi_path_id],
cpath->cop_flags & COP_SPIN_BIT)
/* [draft-ietf-quic-transport-30] Section 17.3.1 talks about
* how spin bit value is set.
*/
&& (packet_in->pi_packno > cpath->cop_max_packno
/* Zero means "unset", in which case any incoming packet
* number will do. On receipt of second packet numbered
* zero, the rechist module will dup it and this code path
* won't hit.
*/
|| cpath->cop_max_packno == 0))
{
cpath->cop_max_packno = packet_in->pi_packno;
if (conn->ifc_flags & IFC_SERVER)
conn->ifc_spin_bit = lsquic_packet_in_spin_bit(packet_in);
cpath->cop_spin_bit = lsquic_packet_in_spin_bit(packet_in);
else
conn->ifc_spin_bit = !lsquic_packet_in_spin_bit(packet_in);
cpath->cop_spin_bit = !lsquic_packet_in_spin_bit(packet_in);
}
conn->ifc_pub.bytes_in += packet_in->pi_data_sz;
if ((conn->ifc_mflags & MF_VALIDATE_PATH) &&
@ -7290,6 +7489,88 @@ ietf_full_conn_ci_retx_timeout (struct lsquic_conn *lconn)
}
static size_t
ietf_full_conn_ci_get_min_datagram_size (struct lsquic_conn *lconn)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
return (size_t) conn->ifc_min_dg_sz;
}
static int
ietf_full_conn_ci_set_min_datagram_size (struct lsquic_conn *lconn,
size_t new_size)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
const struct transport_params *const params =
lconn->cn_esf.i->esfi_get_peer_transport_params(lconn->cn_enc_session);
if (!(conn->ifc_flags & IFC_DATAGRAMS))
{
LSQ_WARN("datagrams are not enabled: cannot set minimum size");
return -1;
}
if (new_size > USHRT_MAX)
{
LSQ_DEBUG("min datagram size cannot be larger than %hu", USHRT_MAX);
return -1;
}
if (new_size > params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE])
{
LSQ_DEBUG("maximum datagram frame size is %"PRIu64", cannot change it "
"to %zd", params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE],
new_size);
return -1;
}
conn->ifc_min_dg_sz = new_size;
LSQ_DEBUG("set minimum datagram size to %zd bytes", new_size);
return 0;
}
/* Return true if datagram was written, false otherwise */
static int
write_datagram (struct ietf_full_conn *conn)
{
struct lsquic_packet_out *packet_out;
size_t need;
int w;
need = conn->ifc_conn.cn_pf->pf_datagram_frame_size(conn->ifc_min_dg_sz);
packet_out = get_writeable_packet(conn, need);
if (!packet_out)
return 0;
w = conn->ifc_conn.cn_pf->pf_gen_datagram_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), conn->ifc_min_dg_sz,
conn->ifc_max_dg_sz,
conn->ifc_enpub->enp_stream_if->on_dg_write, &conn->ifc_conn);
if (w < 0)
{
LSQ_DEBUG("could not generate DATAGRAM frame");
return 0;
}
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_DATAGRAM, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding DATAGRAME frame to packet failed: %d", errno);
return 0;
}
packet_out->po_frame_types |= QUIC_FTBIT_DATAGRAM;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
/* XXX The DATAGRAM frame should really be a regen. Do it when we
* no longer require these frame types to be at the beginning of the
* packet.
*/
return 1;
}
static enum tick_st
ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
{
@ -7454,6 +7735,10 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
if (!write_is_possible(conn))
goto end_write;
while ((conn->ifc_mflags & MF_WANT_DATAGRAM_WRITE) && write_datagram(conn))
if (!write_is_possible(conn))
goto end_write;
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams))
{
process_streams_write_events(conn, 1);
@ -7850,8 +8135,11 @@ ietf_full_conn_ci_record_addrs (struct lsquic_conn *lconn, void *peer_ctx,
{
record_to_path(conn, first_unused, peer_ctx, local_sa, peer_sa);
if (0 == conn->ifc_used_paths && !(conn->ifc_flags & IFC_SERVER))
{
/* First path is considered valid immediately */
first_unused->cop_flags |= COP_VALIDATED;
maybe_enable_spin(conn, first_unused);
}
LSQ_DEBUG("record new path ID %d",
(int) (first_unused - conn->ifc_paths));
conn->ifc_used_paths |= 1 << (first_unused - conn->ifc_paths);
@ -7905,6 +8193,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_get_ctx = ietf_full_conn_ci_get_ctx, \
.ci_get_engine = ietf_full_conn_ci_get_engine, \
.ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \
.ci_get_min_datagram_size= ietf_full_conn_ci_get_min_datagram_size, \
.ci_get_path = ietf_full_conn_ci_get_path, \
.ci_going_away = ietf_full_conn_ci_going_away, \
.ci_hsk_done = ietf_full_conn_ci_hsk_done, \
@ -7922,10 +8211,12 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_report_live = ietf_full_conn_ci_report_live, \
.ci_retx_timeout = ietf_full_conn_ci_retx_timeout, \
.ci_set_ctx = ietf_full_conn_ci_set_ctx, \
.ci_set_min_datagram_size= ietf_full_conn_ci_set_min_datagram_size, \
.ci_status = ietf_full_conn_ci_status, \
.ci_stateless_reset = ietf_full_conn_ci_stateless_reset, \
.ci_tick = ietf_full_conn_ci_tick, \
.ci_tls_alert = ietf_full_conn_ci_tls_alert, \
.ci_want_datagram_write = ietf_full_conn_ci_want_datagram_write, \
.ci_write_ack = ietf_full_conn_ci_write_ack
static const struct conn_iface ietf_full_conn_iface = {
@ -8111,6 +8402,14 @@ on_setting (void *ctx, uint64_t setting_id, uint64_t value)
LSQ_DEBUG("received unknown SETTING 0x%"PRIX64"=0x%"PRIX64
"; ignore it", setting_id, value);
break;
case 2: /* HTTP/2 SETTINGS_ENABLE_PUSH */
case 3: /* HTTP/2 SETTINGS_MAX_CONCURRENT_STREAMS */
case 4: /* HTTP/2 SETTINGS_INITIAL_WINDOW_SIZE */
case 5: /* HTTP/2 SETTINGS_MAX_FRAME_SIZE */
/* [draft-ietf-quic-http-30] Section 7.2.4.1 */
ABORT_QUIETLY(1, HEC_SETTINGS_ERROR, "unexpected HTTP/2 setting "
"%"PRIu64, setting_id);
break;
}
}
@ -8333,12 +8632,14 @@ hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
callbacks = &hcsi_callbacks_server_28;
break;
case (0 << 8) | LSQVER_ID29:
case (0 << 8) | LSQVER_ID30:
callbacks = &hcsi_callbacks_client_29;
break;
default:
assert(0);
/* fallthru */
case (1 << 8) | LSQVER_ID29:
case (1 << 8) | LSQVER_ID30:
callbacks = &hcsi_callbacks_server_29;
break;
}

View File

@ -3768,6 +3768,7 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
packet_out->po_sent_sz = enc_sz;
packet_out->po_flags &= ~PO_IPv6;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT);
packet_out->po_dcid_len = GQUIC_CID_LEN;
return ENCPA_OK;
}

View File

@ -72,6 +72,7 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
[LSQLM_BBR] = LSQ_LOG_WARN,
[LSQLM_CUBIC] = LSQ_LOG_WARN,
[LSQLM_ADAPTIVE_CC] = LSQ_LOG_WARN,
[LSQLM_HEADERS] = LSQ_LOG_WARN,
[LSQLM_FRAME_READER]= LSQ_LOG_WARN,
[LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
@ -115,6 +116,7 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_HSK_ADAPTER] = "hsk-adapter",
[LSQLM_BBR] = "bbr",
[LSQLM_CUBIC] = "cubic",
[LSQLM_ADAPTIVE_CC] = "adaptive-cc",
[LSQLM_HEADERS] = "headers",
[LSQLM_FRAME_READER]= "frame-reader",
[LSQLM_FRAME_WRITER]= "frame-writer",

View File

@ -63,6 +63,7 @@ enum lsquic_logger_module {
LSQLM_HSK_ADAPTER,
LSQLM_BBR,
LSQLM_CUBIC,
LSQLM_ADAPTIVE_CC,
LSQLM_HEADERS,
LSQLM_FRAME_WRITER,
LSQLM_FRAME_READER,

View File

@ -182,6 +182,7 @@ lsquic_malo_create (size_t obj_size)
{
TAILQ_INIT(&malo->elems);
malo->obj_size = obj_size;
malo->next_iter_elem = NULL;
return malo;
}
else

View File

@ -1924,12 +1924,13 @@ mini_conn_ci_Q050_packet_in (struct lsquic_conn *lconn,
static struct lsquic_packet_out *
mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal_UNUSED)
{
struct mini_conn *mc = (struct mini_conn *) lconn;
lsquic_packet_out_t *packet_out;
assert(0 == size);
assert(NULL == to_coal_UNUSED);
TAILQ_FOREACH(packet_out, &mc->mc_packets_out, po_next)
{
if (packet_out->po_flags & PO_SENT)

View File

@ -448,6 +448,17 @@ is_first_packet_ok (const struct lsquic_packet_in *packet_in,
}
static void
imico_peer_addr_validated (struct ietf_mini_conn *conn, const char *how)
{
if (!(conn->imc_flags & IMC_ADDR_VALIDATED))
{
conn->imc_flags |= IMC_ADDR_VALIDATED;
LSQ_DEBUG("peer address validated (%s)", how);
}
}
struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
const struct lsquic_packet_in *packet_in,
@ -527,7 +538,7 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
TAILQ_INIT(&conn->imc_app_packets);
TAILQ_INIT(&conn->imc_crypto_frames);
if (odcid)
conn->imc_flags |= IMC_ADDR_VALIDATED;
imico_peer_addr_validated(conn, "odcid");
LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
conn, conn->imc_path.np_pack_size);
@ -652,7 +663,8 @@ imico_can_send (const struct ietf_mini_conn *conn, size_t size)
static struct lsquic_packet_out *
ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal)
{
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
struct lsquic_packet_out *packet_out;
@ -663,7 +675,11 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
if (packet_out->po_flags & PO_SENT)
continue;
packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
if (size == 0 || packet_size + size <= conn->imc_path.np_pack_size)
if (!(to_coal
&& (packet_size + to_coal->prev_sz_sum
> conn->imc_path.np_pack_size
|| !lsquic_packet_out_equal_dcids(to_coal->prev_packet, packet_out))
))
{
if (!imico_can_send(conn, packet_size))
{
@ -674,7 +690,7 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
}
packet_out->po_flags |= PO_SENT;
conn->imc_bytes_out += packet_size;
if (size == 0)
if (!to_coal)
LSQ_DEBUG("packet_to_send: %"PRIu64, packet_out->po_packno);
else
LSQ_DEBUG("packet_to_send: %"PRIu64" (coalesced)",
@ -847,7 +863,7 @@ imico_process_crypto_frame (IMICO_PROC_FRAME_ARGS)
{
if (0 != conn->imc_conn.cn_esf.i->esfi_init_server(
conn->imc_conn.cn_enc_session))
return -1;
return 0;
conn->imc_flags |= IMC_ENC_SESS_INITED;
}
@ -1210,6 +1226,32 @@ imico_maybe_delay_processing (struct ietf_mini_conn *conn,
}
/* [draft-ietf-quic-transport-30] Section 8.1:
" Additionally, a server MAY consider the client address validated if
" the client uses a connection ID chosen by the server and the
" connection ID contains at least 64 bits of entropy.
*
* We use RAND_bytes() to generate SCIDs, so it's all entropy.
*/
static void
imico_maybe_validate_by_dcid (struct ietf_mini_conn *conn,
const lsquic_cid_t *dcid)
{
unsigned i;
if (dcid->len >= 8)
/* Generic code with unnecessary loop as future-proofing */
for (i = 0; i < conn->imc_conn.cn_n_cces; ++i)
if ((conn->imc_conn.cn_cces_mask & (i << 1))
&& (conn->imc_conn.cn_cces[i].cce_flags & CCE_SEQNO)
&& LSQUIC_CIDS_EQ(&conn->imc_conn.cn_cces[i].cce_cid, dcid))
{
imico_peer_addr_validated(conn, "dcid/scid + entropy");
return;
}
}
/* Only a single packet is supported */
static void
ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
@ -1236,6 +1278,9 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
return;
}
if (!(conn->imc_flags & IMC_ADDR_VALIDATED))
imico_maybe_validate_by_dcid(conn, &packet_in->pi_dcid);
pns = lsquic_hety2pns[ packet_in->pi_header_type ];
if (pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT))
{
@ -1261,7 +1306,7 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
return;
}
else if (pns == PNS_HSK)
conn->imc_flags |= IMC_ADDR_VALIDATED;
imico_peer_addr_validated(conn, "handshake PNS");
if (((conn->imc_flags >> IMCBIT_PNS_BIT_SHIFT) & 3) < pns)
{

View File

@ -36,6 +36,7 @@ enum quic_frame_type
QUIC_FRAME_HANDSHAKE_DONE, /* I */
QUIC_FRAME_ACK_FREQUENCY, /* I */
QUIC_FRAME_TIMESTAMP, /* I */
QUIC_FRAME_DATAGRAM, /* I */
N_QUIC_FRAMES
};
@ -66,6 +67,7 @@ enum quic_ft_bit {
QUIC_FTBIT_HANDSHAKE_DONE = 1 << QUIC_FRAME_HANDSHAKE_DONE,
QUIC_FTBIT_ACK_FREQUENCY = 1 << QUIC_FRAME_ACK_FREQUENCY,
QUIC_FTBIT_TIMESTAMP = 1 << QUIC_FRAME_TIMESTAMP,
QUIC_FTBIT_DATAGRAM = 1 << QUIC_FRAME_DATAGRAM,
};
static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
@ -95,6 +97,7 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
[QUIC_FRAME_HANDSHAKE_DONE] = "QUIC_FRAME_HANDSHAKE_DONE",
[QUIC_FRAME_ACK_FREQUENCY] = "QUIC_FRAME_ACK_FREQUENCY",
[QUIC_FRAME_TIMESTAMP] = "QUIC_FRAME_TIMESTAMP",
[QUIC_FRAME_DATAGRAM] = "QUIC_FRAME_DATAGRAM",
};
#define QUIC_FRAME_PRELEN (sizeof("QUIC_FRAME_"))
@ -132,6 +135,7 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
QUIC_FRAME_SLEN(QUIC_FRAME_HANDSHAKE_DONE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_ACK_FREQUENCY) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_TIMESTAMP) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_DATAGRAM) + 1 + \
0

View File

@ -475,3 +475,55 @@ lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *packet_out,
return -1;
}
static unsigned
offset_to_dcid (const struct lsquic_packet_out *packet_out)
{
if (packet_out->po_header_type == HETY_NOT_SET)
return 1;
else
{
assert(!(packet_out->po_lflags & POL_GQUIC));
return 6;
}
}
/* Return true if DCIDs of the two packets are equal, false otherwise. */
int
lsquic_packet_out_equal_dcids (const struct lsquic_packet_out *a,
const struct lsquic_packet_out *b)
{
const int a_encrypted = !!(a->po_flags & PO_ENCRYPTED);
const int b_encrypted = !!(b->po_flags & PO_ENCRYPTED);
const unsigned char *dcids[2];
size_t sizes[2];
switch ((a_encrypted << 1) | b_encrypted)
{
case (0 << 1) | 0:
return a->po_path == b->po_path;
case (0 << 1) | 1:
dcids[0] = a->po_path->np_dcid.idbuf;
sizes[0] = a->po_path->np_dcid.len;
dcids[1] = b->po_enc_data + offset_to_dcid(b);
sizes[1] = b->po_dcid_len;
break;
case (1 << 1) | 0:
dcids[0] = a->po_enc_data + offset_to_dcid(a);
sizes[0] = a->po_dcid_len;
dcids[1] = b->po_path->np_dcid.idbuf;
sizes[1] = b->po_path->np_dcid.len;
break;
default:
dcids[0] = a->po_enc_data + offset_to_dcid(a);
sizes[0] = a->po_dcid_len;
dcids[1] = b->po_enc_data + offset_to_dcid(b);
sizes[1] = b->po_dcid_len;
break;
}
return sizes[0] == sizes[1]
&& 0 == memcmp(dcids[0], dcids[1], sizes[0]);
}

View File

@ -148,6 +148,7 @@ typedef struct lsquic_packet_out
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
unsigned short po_token_len;
enum header_type po_header_type:8;
unsigned char po_dcid_len; /* If PO_ENCRYPTED is set */
enum {
POL_GQUIC = 1 << 0, /* Used for logging */
#define POLEV_SHIFT 1
@ -345,4 +346,7 @@ int
lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *,
const struct parse_funcs *, const struct lsquic_stream *);
int
lsquic_packet_out_equal_dcids (const struct lsquic_packet_out *,
const struct lsquic_packet_out *);
#endif

View File

@ -323,6 +323,15 @@ struct parse_funcs
(*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 *);
int
(*pf_parse_datagram_frame) (const unsigned char *buf, size_t, const void **,
size_t *);
int
(*pf_gen_datagram_frame) (unsigned char *, size_t bufsz, size_t min_sz,
size_t max_sz, ssize_t (*)(struct lsquic_conn *, void *, size_t),
struct lsquic_conn *);
unsigned
(*pf_datagram_frame_size) (size_t);
};

View File

@ -220,7 +220,7 @@ 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_ID29] = {
[LSQVER_ID30] = {
[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
@ -231,6 +231,7 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| 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,
@ -246,6 +247,37 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| QUIC_FTBIT_HANDSHAKE_DONE | QUIC_FTBIT_ACK_FREQUENCY
| QUIC_FTBIT_RETIRE_CONNECTION_ID | QUIC_FTBIT_NEW_TOKEN
| QUIC_FTBIT_TIMESTAMP
| QUIC_FTBIT_DATAGRAM
,
},
[LSQVER_ID29] = {
[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_ID28] = {
@ -287,7 +319,9 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| QUIC_FTBIT_STREAMS_BLOCKED
| QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING
| QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE
| QUIC_FTBIT_RETIRE_CONNECTION_ID,
| QUIC_FTBIT_RETIRE_CONNECTION_ID
| QUIC_FTBIT_DATAGRAM
,
[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
@ -302,6 +336,7 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| QUIC_FTBIT_HANDSHAKE_DONE | QUIC_FTBIT_ACK_FREQUENCY
| QUIC_FTBIT_RETIRE_CONNECTION_ID | QUIC_FTBIT_NEW_TOKEN
| QUIC_FTBIT_TIMESTAMP
| QUIC_FTBIT_DATAGRAM
,
},
};

View File

@ -49,6 +49,8 @@
#define FRAME_TYPE_ACK_FREQUENCY 0xAF
#define FRAME_TYPE_TIMESTAMP 0x2F5
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int
ietf_v1_gen_one_varint (unsigned char *, size_t, unsigned char, uint64_t);
@ -2147,6 +2149,82 @@ ietf_v1_parse_timestamp_frame (const unsigned char *buf, size_t buf_len,
}
static int
ietf_v1_parse_datagram_frame (const unsigned char *buf, size_t buf_len,
const void **data, size_t *data_len)
{
uint64_t len;
int s;
/* Length and frame type have been checked already */
assert(buf_len > 0);
assert(buf[0] == 0x30 || buf[0] == 0x31);
if (buf[0] & 1)
{
s = vint_read(buf + 1, buf + buf_len, &len);
if (s > 0 && 1 + s + len >= buf_len)
{
*data = buf + 1 + s;
*data_len = len;
return 1 + buf_len + len;
}
else
return -1;
}
else
{
*data = buf + 1;
*data_len = buf_len - 1;
return buf_len;
}
}
static unsigned
ietf_v1_datagram_frame_size (size_t sz)
{
return 1u + vint_size(sz) + sz;
}
static int
ietf_v1_gen_datagram_frame (unsigned char *buf, size_t bufsz, size_t min_sz,
size_t max_sz,
ssize_t (*user_callback)(struct lsquic_conn *, void *, size_t),
struct lsquic_conn *lconn)
{
unsigned bits, len_sz;
ssize_t nw;
/* We always generate length. A more efficient implementation would
* complicate the API.
*/
if (min_sz)
bits = vint_val2bits(min_sz);
else
bits = vint_val2bits(bufsz);
len_sz = 1u << bits;
if (1 + len_sz + min_sz > bufsz)
{
errno = ENOBUFS;
return -1;
}
nw = user_callback(lconn, buf + 1 + len_sz, min_sz ? min_sz
: MIN(bufsz - 1 - len_sz, max_sz));
if (nw >= 0)
{
buf[0] = 0x31;
vint_write(&buf[1], (uint64_t) nw, bits, len_sz);
return 1 + len_sz + nw;
}
else
return -1;
}
const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
{
.pf_gen_reg_pkt_header = ietf_v1_gen_reg_pkt_header,
@ -2217,4 +2295,7 @@ const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
.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,
.pf_parse_datagram_frame = ietf_v1_parse_datagram_frame,
.pf_gen_datagram_frame = ietf_v1_gen_datagram_frame,
.pf_datagram_frame_size = ietf_v1_datagram_frame_size,
};

View File

@ -289,8 +289,8 @@ const enum quic_frame_type lsquic_iquic_byte2type[0x40] =
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_INVALID,
[0x31] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_DATAGRAM,
[0x31] = QUIC_FRAME_DATAGRAM,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,

View File

@ -515,10 +515,11 @@ lsquic_prq_have_pending (const struct pr_queue *prq)
static struct lsquic_packet_out *
evanescent_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
evanescent_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal_UNUSED)
{
struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn;
assert(size == 0);
assert(!to_coal_UNUSED);
return &evconn->evc_packet_out;
}

View File

@ -188,3 +188,16 @@ lsquic_rechist_mem_used (const struct lsquic_rechist *rechist)
- sizeof(rechist->rh_pints)
+ lsquic_packints_mem_used(&rechist->rh_pints);
}
const struct lsquic_packno_range *
lsquic_rechist_peek (const struct lsquic_rechist *rechist)
{
const struct packet_interval *pint;
pint = TAILQ_FIRST(&rechist->rh_pints.pk_intervals);
if (pint)
return &pint->range;
else
return NULL;
}

View File

@ -77,4 +77,9 @@ lsquic_rechist_largest_recv (const lsquic_rechist_t *);
size_t
lsquic_rechist_mem_used (const struct lsquic_rechist *);
const struct lsquic_packno_range *
lsquic_rechist_peek (const struct lsquic_rechist *);
#define lsquic_rechist_n_packets(rechist_) (+(rechist_)->rh_n_packets)
#endif

View File

@ -30,6 +30,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_util.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
@ -63,7 +64,7 @@
#define MIN_RTO_DELAY 1000000 /* Microseconds */
#define N_NACKS_BEFORE_RETX 3
#define CGP(ctl) ((struct cong_ctl *) &(ctl)->sc_cong_u)
#define CGP(ctl) ((struct cong_ctl *) (ctl)->sc_cong_ctl)
#define packet_out_total_sz(p) \
lsquic_packet_out_total_sz(ctl->sc_conn_pub->lconn, p)
@ -323,7 +324,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
struct lsquic_engine_public *enpub, const struct ver_neg *ver_neg,
struct lsquic_conn_public *conn_pub, enum send_ctl_flags flags)
{
unsigned i, algo;
unsigned i;
memset(ctl, 0, sizeof(*ctl));
TAILQ_INIT(&ctl->sc_scheduled_packets);
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_INIT]);
@ -351,14 +352,22 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
lsquic_alarmset_init_alarm(alset, AL_RETX_HSK, retx_alarm_rings, ctl);
lsquic_alarmset_init_alarm(alset, AL_RETX_APP, retx_alarm_rings, ctl);
lsquic_senhist_init(&ctl->sc_senhist, ctl->sc_flags & SC_IETF);
if (0 == enpub->enp_settings.es_cc_algo)
algo = LSQUIC_DF_CC_ALGO;
else
algo = enpub->enp_settings.es_cc_algo;
if (algo == 2)
ctl->sc_ci = &lsquic_cong_bbr_if;
else
switch (enpub->enp_settings.es_cc_algo)
{
case 1:
ctl->sc_ci = &lsquic_cong_cubic_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_cubic;
break;
case 2:
ctl->sc_ci = &lsquic_cong_bbr_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_bbr;
break;
case 3:
default:
ctl->sc_ci = &lsquic_cong_adaptive_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc;
break;
}
ctl->sc_ci->cci_init(CGP(ctl), conn_pub, ctl->sc_retx_frames);
if (ctl->sc_flags & SC_PACE)
lsquic_pacer_init(&ctl->sc_pacer, conn_pub->lconn,
@ -682,6 +691,33 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
}
static void
send_ctl_select_cc (struct lsquic_send_ctl *ctl)
{
lsquic_time_t srtt;
srtt = lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats);
if (srtt <= ctl->sc_enpub->enp_settings.es_cc_rtt_thresh)
{
LSQ_INFO("srtt is %"PRIu64" usec, which is smaller than or equal to "
"the threshold of %u usec: select Cubic congestion controller",
srtt, ctl->sc_enpub->enp_settings.es_cc_rtt_thresh);
ctl->sc_ci = &lsquic_cong_cubic_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_cubic;
ctl->sc_flags |= SC_CLEANUP_BBR;
}
else
{
LSQ_INFO("srtt is %"PRIu64" usec, which is greater than the threshold "
"of %u usec: select BBRv1 congestion controller", srtt,
ctl->sc_enpub->enp_settings.es_cc_rtt_thresh);
ctl->sc_ci = &lsquic_cong_bbr_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_bbr;
}
}
static void
take_rtt_sample (lsquic_send_ctl_t *ctl,
lsquic_time_t now, lsquic_time_t lack_delta)
@ -696,6 +732,8 @@ take_rtt_sample (lsquic_send_ctl_t *ctl,
LSQ_DEBUG("packno %"PRIu64"; rtt: %"PRIu64"; delta: %"PRIu64"; "
"new srtt: %"PRIu64, packno, measured_rtt, lack_delta,
lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats));
if (ctl->sc_ci == &lsquic_cong_adaptive_if)
send_ctl_select_cc(ctl);
}
}
@ -1423,6 +1461,11 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
if (ctl->sc_flags & SC_PACE)
lsquic_pacer_cleanup(&ctl->sc_pacer);
ctl->sc_ci->cci_cleanup(CGP(ctl));
if (ctl->sc_flags & SC_CLEANUP_BBR)
{
assert(ctl->sc_ci == &lsquic_cong_cubic_if);
lsquic_cong_bbr_if.cci_cleanup(&ctl->sc_adaptive_cc.acc_bbr);
}
#if LSQUIC_SEND_STATS
LSQ_NOTICE("stats: n_total_sent: %u; n_resent: %u; n_delayed: %u",
ctl->sc_stats.n_total_sent, ctl->sc_stats.n_resent,
@ -1801,9 +1844,11 @@ lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *ctl)
lsquic_packet_out_t *
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
const struct to_coal *to_coal)
{
lsquic_packet_out_t *packet_out;
size_t size;
int dec_limit;
get_packet:
@ -1843,14 +1888,24 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
}
}
if (UNLIKELY(size))
if (UNLIKELY(to_coal != NULL))
{
if (packet_out_total_sz(packet_out) + size > SC_PACK_SIZE(ctl))
/* From [draft-ietf-quic-transport-30], Section-12.2:
" Senders MUST NOT coalesce QUIC packets with different connection
" IDs into a single UDP datagram.
*/
if (packet_out_total_sz(packet_out) + to_coal->prev_sz_sum
> SC_PACK_SIZE(ctl)
|| !lsquic_packet_out_equal_dcids(to_coal->prev_packet, packet_out))
return NULL;
LSQ_DEBUG("packet %"PRIu64" (%zu bytes) will be tacked on to "
"previous packet(s) (%zu bytes) (coalescing)",
packet_out->po_packno, packet_out_total_sz(packet_out), size);
packet_out->po_packno, packet_out_total_sz(packet_out),
to_coal->prev_sz_sum);
size = to_coal->prev_sz_sum;
}
else
size = 0;
send_ctl_sched_remove(ctl, packet_out);
if (dec_limit)
@ -3336,8 +3391,9 @@ send_ctl_resize_q (struct lsquic_send_ctl *ctl, struct lsquic_packets_tailq *q,
void
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
struct network_path *new)
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl,
const struct network_path *old, const struct network_path *new,
int keep_path_properties)
{
struct lsquic_packet_out *packet_out;
unsigned count;
@ -3367,11 +3423,15 @@ lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
LSQ_DEBUG("repathed %u packet%.*s", count, count != 1, "s");
lsquic_send_ctl_resize(ctl);
memset(&ctl->sc_conn_pub->rtt_stats, 0,
sizeof(ctl->sc_conn_pub->rtt_stats));
ctl->sc_ci->cci_reinit(CGP(ctl));
if (keep_path_properties)
LSQ_DEBUG("keeping path properties: MTU, RTT, and CC state");
else
{
lsquic_send_ctl_resize(ctl);
memset(&ctl->sc_conn_pub->rtt_stats, 0,
sizeof(ctl->sc_conn_pub->rtt_stats));
ctl->sc_ci->cci_reinit(CGP(ctl));
}
}

View File

@ -20,6 +20,7 @@ struct lsquic_conn_public;
struct network_path;
struct ver_neg;
enum pns;
struct to_coal;
enum buf_packet_type { BPT_HIGHEST_PRIO, BPT_OTHER_PRIO, };
@ -48,6 +49,7 @@ enum send_ctl_flags {
SC_SANITY_CHECK = 1 << 15,
SC_CIDLEN = 1 << 16, /* sc_cidlen is set */
SC_POISON = 1 << 17, /* poisoned packet exists */
SC_CLEANUP_BBR = 1 << 18,
};
typedef struct lsquic_send_ctl {
@ -67,11 +69,9 @@ typedef struct lsquic_send_ctl {
int (*sc_can_send)(struct lsquic_send_ctl *);
unsigned sc_bytes_unacked_retx;
unsigned sc_bytes_scheduled;
union {
struct lsquic_cubic cubic;
struct lsquic_bbr bbr;
} sc_cong_u;
struct adaptive_cc sc_adaptive_cc;
const struct cong_ctl_if *sc_ci;
void *sc_cong_ctl;
struct lsquic_engine_public *sc_enpub;
unsigned sc_bytes_unacked_all;
unsigned sc_n_in_flight_all;
@ -166,7 +166,8 @@ void
lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *, struct lsquic_packet_out *);
struct lsquic_packet_out *
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *, size_t);
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *,
const struct to_coal *);
int
lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *);
@ -362,8 +363,9 @@ void
lsquic_send_ctl_empty_pns (struct lsquic_send_ctl *, enum packnum_space);
void
lsquic_send_ctl_repath (struct lsquic_send_ctl *, struct network_path *old,
struct network_path *new);
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl,
const struct network_path *old, const struct network_path *new,
int keep_path_properties);
void
lsquic_send_ctl_resize (struct lsquic_send_ctl *);

View File

@ -61,6 +61,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_headers.h"
#include "lsquic_ev_log.h"
@ -504,9 +505,16 @@ lsquic_stream_new_crypto (enum enc_level enc_level,
stream->sm_bflags |= SMBF_CRYPTO|SMBF_IETF;
stream->sm_enc_level = enc_level;
/* TODO: why have limit in crypto stream? Set it to UINT64_MAX? */
/* We allow buffering of up to 16 KB of CRYPTO data (I guess we could
* make this configurable?). The window is opened (without sending
* MAX_STREAM_DATA) as CRYPTO data is consumed. If too much comes in
* at a time, we abort with TEC_CRYPTO_BUFFER_EXCEEDED.
*/
lsquic_sfcw_init(&stream->fc, 16 * 1024, NULL, conn_pub, stream_id);
stream->max_send_off = 16 * 1024;
/* Don't limit ourselves from sending CRYPTO data. We assume that
* the underlying crypto library behaves in a sane manner.
*/
stream->max_send_off = UINT64_MAX;
LSQ_DEBUG("created crypto stream");
SM_HISTORY_APPEND(stream, SHE_CREATED);
stream->sm_frame_header_sz = stream_crypto_frame_header_sz;
@ -970,8 +978,14 @@ lsquic_stream_update_sfcw (lsquic_stream_t *stream, uint64_t max_off)
if (stream->sm_bflags & SMBF_IETF)
{
lconn = stream->conn_pub->lconn;
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FLOW_CONTROL_ERROR,
"flow control violation on stream %"PRIu64, stream->id);
if (lsquic_stream_is_crypto(stream))
lconn->cn_if->ci_abort_error(lconn, 0,
TEC_CRYPTO_BUFFER_EXCEEDED,
"crypto buffer exceeded on in crypto level %"PRIu64,
crypto_level(stream));
else
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FLOW_CONTROL_ERROR,
"flow control violation on stream %"PRIu64, stream->id);
}
return -1;
}
@ -1369,7 +1383,12 @@ static void
stream_consumed_bytes (struct lsquic_stream *stream)
{
lsquic_sfcw_set_read_off(&stream->fc, stream->read_offset);
if (lsquic_sfcw_fc_offsets_changed(&stream->fc))
if (lsquic_sfcw_fc_offsets_changed(&stream->fc)
/* We advance crypto streams' offsets (to control amount of
* buffering we allow), but do not send MAX_STREAM_DATA frames.
*/
&& !((stream->sm_bflags & (SMBF_IETF|SMBF_CRYPTO))
== (SMBF_IETF|SMBF_CRYPTO)))
{
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
@ -4534,6 +4553,12 @@ update_type_hist_and_check (const struct lsquic_stream *stream,
case HQFT_MAX_PUSH_ID:
/* [draft-ietf-quic-http-24], Section 7 */
return -1;
case 2: /* HTTP/2 PRIORITY */
case 6: /* HTTP/2 PING */
case 8: /* HTTP/2 WINDOW_UPDATE */
case 9: /* HTTP/2 CONTINUATION */
/* [draft-ietf-quic-http-30], Section 7.2.8 */
return -1;
default:
/* Ignore unknown frames */
return 0;

View File

@ -536,7 +536,7 @@ size_t
lsquic_stream_flush_threshold (const struct lsquic_stream *, unsigned);
#endif
#define crypto_level(stream) (~0ULL - (stream)->id)
#define crypto_level(stream) (UINT64_MAX - (stream)->id)
void
lsquic_stream_set_stream_if (struct lsquic_stream *,

View File

@ -49,7 +49,7 @@ struct tokgen_shm_state
{
uint8_t tgss_version;
uint8_t tgss_magic_top[sizeof(TOKGEN_SHM_MAGIC_TOP) - 1];
uint8_t tgss_crypter_key[N_TOKEN_TYPES][CRYPTER_KEY_SIZE];
uint8_t tgss_padding[2 * CRYPTER_KEY_SIZE];
uint8_t tgss_srst_prk_size;
uint8_t tgss_srst_prk[SRST_MAX_PRK_SIZE];
uint8_t tgss_magic_bottom[sizeof(TOKGEN_SHM_MAGIC_BOTTOM) - 1];
@ -72,8 +72,6 @@ struct crypter
struct token_generator
{
/* We encrypt different token types using different keys. */
struct crypter tg_crypters[N_TOKEN_TYPES];
/* Stateless reset token is generated using HKDF with CID as the
* `info' parameter to HKDF-Expand.

View File

@ -7,8 +7,6 @@ struct sockaddr;
struct lsquic_packet_in;
struct lsquic_cid;
enum token_type { TOKEN_RETRY, TOKEN_RESUME, N_TOKEN_TYPES, };
struct token_generator;
struct token_generator *

View File

@ -54,6 +54,7 @@ tpi_val_2_enum (uint64_t tpi_val)
case 14: return TPI_ACTIVE_CONNECTION_ID_LIMIT;
case 15: return TPI_INITIAL_SOURCE_CID;
case 16: return TPI_RETRY_SOURCE_CID;
case 0x20: return TPI_MAX_DATAGRAM_FRAME_SIZE;
#if LSQUIC_TEST_QUANTUM_READINESS
case 0xC37: return TPI_QUANTUM_READINESS;
#endif
@ -85,6 +86,7 @@ static const unsigned enum_2_tpi_val[LAST_TPI + 1] =
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = 0xE,
[TPI_INITIAL_SOURCE_CID] = 0xF,
[TPI_RETRY_SOURCE_CID] = 0x10,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = 0x20,
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = 0xC37,
#endif
@ -114,6 +116,7 @@ const char * const lsquic_tpi2str[LAST_TPI + 1] =
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = "active_connection_id_limit",
[TPI_INITIAL_SOURCE_CID] = "initial_source_connection_id",
[TPI_RETRY_SOURCE_CID] = "retry_source_connection_id",
[TPI_MAX_DATAGRAM_FRAME_SIZE] = "max_datagram_frame_size",
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = "quantum_readiness",
#endif
@ -148,8 +151,8 @@ static const uint64_t max_vals[MAX_NUMERIC_TPI + 1] =
*/
[TPI_MAX_UDP_PAYLOAD_SIZE] = VINT_MAX_VALUE,
[TPI_ACK_DELAY_EXPONENT] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_UNI] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_BIDI] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_UNI] = 1ull << 60,
[TPI_INIT_MAX_STREAMS_BIDI] = 1ull << 60,
[TPI_INIT_MAX_DATA] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = VINT_MAX_VALUE,
@ -160,6 +163,7 @@ static const uint64_t max_vals[MAX_NUMERIC_TPI + 1] =
[TPI_LOSS_BITS] = 1,
[TPI_MIN_ACK_DELAY] = (1u << 24) - 1u,
[TPI_TIMESTAMPS] = TS_WANT_THEM|TS_GENERATE_THEM,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = VINT_MAX_VALUE,
};
@ -206,6 +210,23 @@ lsquic_tp_has_pref_ipv6 (const struct transport_params *params)
}
#if LSQUIC_TEST_QUANTUM_READINESS
#include <stdlib.h>
size_t
lsquic_tp_get_quantum_sz (void)
{
const char *str;
str = getenv("LSQUIC_QUANTUM_SZ");
if (str)
return atoi(str);
else
/* https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test */
return 1200;
}
#endif
static size_t
update_cid_bits (unsigned bits[][3], enum transport_param_id tpi,
const lsquic_cid_t *cid)
@ -238,6 +259,9 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
@ -275,14 +299,14 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
}
}
#if LSQUIC_TEST_QUANTUM_READINESS
else if (set & (1 << TPI_QUANTUM_READINESS))
if (set & (1 << TPI_QUANTUM_READINESS))
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(QUANTUM_READY_SZ);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(quantum_sz);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ QUANTUM_READY_SZ;
+ quantum_sz;
}
#endif
@ -375,6 +399,7 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
@ -420,11 +445,11 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
vint_write(p, QUANTUM_READY_SZ,
bits[tpi][1], 1 << bits[tpi][1]);
LSQ_DEBUG("encoded %zd bytes of quantum readiness", quantum_sz);
vint_write(p, quantum_sz, bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
memset(p, 'Q', quantum_sz);
p += quantum_sz;
break;
#endif
}
@ -501,6 +526,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
switch (len)
{
case 1:
@ -734,6 +760,9 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
@ -788,10 +817,10 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(QUANTUM_READY_SZ);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(quantum_sz);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ QUANTUM_READY_SZ;
+ quantum_sz;
}
#endif
@ -884,6 +913,7 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
@ -931,11 +961,11 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
vint_write(p, QUANTUM_READY_SZ,
bits[tpi][1], 1 << bits[tpi][1]);
LSQ_DEBUG("encoded %zd bytes of quantum readiness", quantum_sz);
vint_write(p, quantum_sz, bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
memset(p, 'Q', quantum_sz);
p += quantum_sz;
break;
#endif
}
@ -1012,6 +1042,7 @@ lsquic_tp_decode_27 (const unsigned char *const buf, size_t bufsz,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
switch (len)
{
case 1:

View File

@ -34,6 +34,7 @@ enum transport_param_id
*/
TPI_MIN_ACK_DELAY,
TPI_TIMESTAMPS,
TPI_MAX_DATAGRAM_FRAME_SIZE,
TPI_LOSS_BITS, MAX_NUMERIC_TPI = TPI_LOSS_BITS,
/*
@ -53,8 +54,6 @@ enum transport_param_id
#define LAST_TP_CID TPI_RETRY_SOURCE_CID
TPI_RETRY_SOURCE_CID,
#if LSQUIC_TEST_QUANTUM_READINESS
/* https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test */
#define QUANTUM_READY_SZ 1200
TPI_QUANTUM_READINESS,
#endif
TPI_STATELESS_RESET_TOKEN, LAST_TPI = TPI_STATELESS_RESET_TOKEN
@ -180,4 +179,9 @@ extern const char * const lsquic_tpi2str[LAST_TPI + 1];
#define TS_WANT_THEM 1
#define TS_GENERATE_THEM 2
#if LSQUIC_TEST_QUANTUM_READINESS
size_t
lsquic_tp_get_quantum_sz (void);
#endif
#endif

View File

@ -21,6 +21,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
[LSQVER_ID27] = { 0xFF, 0, 0, 27, },
[LSQVER_ID28] = { 0xFF, 0, 0, 28, },
[LSQVER_ID29] = { 0xFF, 0, 0, 29, },
[LSQVER_ID30] = { 0xFF, 0, 0, 30, },
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
};
@ -60,6 +61,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_ID27] = "FF00001B",
[LSQVER_ID28] = "FF00001C",
[LSQVER_ID29] = "FF00001D",
[LSQVER_ID30] = "FF00001E",
[LSQVER_VERNEG] = "FAFAFAFA",
};

View File

@ -45,6 +45,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_ver_neg.h"
#include "lsquic_packet_out.h"
@ -363,7 +364,8 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
tobjs->eng_pub.enp_hsi_if = &tobjs->hsi_if;
lsquic_send_ctl_init(&tobjs->send_ctl, &tobjs->alset, &tobjs->eng_pub,
&tobjs->ver_neg, &tobjs->conn_pub, 0);
tobjs->send_ctl.sc_cong_u.cubic.cu_cwnd = ~0ull;
tobjs->send_ctl.sc_adaptive_cc.acc_cubic.cu_cwnd = ~0ull;
tobjs->send_ctl.sc_cong_ctl = &tobjs->send_ctl.sc_adaptive_cc.acc_cubic;
tobjs->stream_if = &stream_if;
tobjs->stream_if_ctx = &test_ctx;
tobjs->ctor_flags = stream_ctor_flags;
@ -1626,7 +1628,6 @@ main (int argc, char **argv)
else
{
main_test_pwritev();
return 0;
main_test_hq_framing();
for (n_packets = 1; n_packets <= 2; ++n_packets)
for (extra_sz = 0; extra_sz <= 2; ++extra_sz)

View File

@ -46,6 +46,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_ver_neg.h"
#include "lsquic_packet_out.h"

View File

@ -40,6 +40,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_ver_neg.h"
#include "lsquic_packet_out.h"
@ -3005,7 +3006,7 @@ test_packetization (int schedule_stream_packets_immediately, int dispatch_once,
}
else
{
assert(0x4000 == nw);
assert(sizeof(buf) == nw);
assert(0 == memcmp(buf, buf_out, nw));
}