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:
parent
c3c69ba3bb
commit
b1a7c3f944
24
CHANGELOG
24
CHANGELOG
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
--------------
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
104
docs/apiref.rst
104
docs/apiref.rst
|
@ -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.
|
||||
|
|
|
@ -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 ---------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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)(
|
||||
¶ms, 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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue