mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	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
					
				
					 53 changed files with 1745 additions and 161 deletions
				
			
		
							
								
								
									
										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
 | 
			
		||||
-------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										189
									
								
								bin/duck_client.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								bin/duck_client.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,189 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * duck_client.c -- The siduck client.  See
 | 
			
		||||
 *      https://tools.ietf.org/html/draft-pardue-quic-siduck-00
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#define Read read
 | 
			
		||||
#else
 | 
			
		||||
#include "vc_compat.h"
 | 
			
		||||
#include "getopt.h"
 | 
			
		||||
#include <io.h>
 | 
			
		||||
#define Read _read
 | 
			
		||||
#define STDIN_FILENO 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <event2/event.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
/* Expected request and response of the siduck protocol */
 | 
			
		||||
#define REQUEST "quack"
 | 
			
		||||
#define RESPONSE "quack-ack"
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
duck_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    LSQ_NOTICE("created a new connection");
 | 
			
		||||
    return stream_if_ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
duck_client_on_hsk_done (lsquic_conn_t *conn, enum lsquic_hsk_status s)
 | 
			
		||||
{
 | 
			
		||||
    if (s == LSQ_HSK_OK || s == LSQ_HSK_RESUMED_OK)
 | 
			
		||||
    {
 | 
			
		||||
        if (lsquic_conn_want_datagram_write(conn, 1) < 0)
 | 
			
		||||
            LSQ_ERROR("want_datagram_write failed");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
duck_client_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_conn_ctx_t *ctx = lsquic_conn_get_ctx(conn);
 | 
			
		||||
    LSQ_NOTICE("Connection closed, stop client");
 | 
			
		||||
    prog_stop((struct prog *) ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static ssize_t
 | 
			
		||||
duck_client_on_dg_write (lsquic_conn_t *conn, void *buf, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
    int s;
 | 
			
		||||
 | 
			
		||||
    /* We only write one request */
 | 
			
		||||
    s = lsquic_conn_want_datagram_write(conn, 0);
 | 
			
		||||
    assert(s == 1); /* Old value was "yes, we want to write a datagram" */
 | 
			
		||||
 | 
			
		||||
    if (sz >= sizeof(REQUEST) - 1)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("wrote `%s' in request", REQUEST);
 | 
			
		||||
        memcpy(buf, REQUEST, sizeof(REQUEST) - 1);
 | 
			
		||||
        return sizeof(REQUEST) - 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
duck_client_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
 | 
			
		||||
{
 | 
			
		||||
    if (bufsz == sizeof(RESPONSE) - 1
 | 
			
		||||
            && 0 == memcmp(buf, RESPONSE, sizeof(RESPONSE) - 1))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("received the expected `%s' response", RESPONSE);
 | 
			
		||||
        lsquic_conn_close(conn);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("unexpected request received, abort connection");
 | 
			
		||||
        lsquic_conn_abort(conn);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if duck_client_stream_if = {
 | 
			
		||||
    .on_new_conn            = duck_client_on_new_conn,
 | 
			
		||||
    .on_hsk_done            = duck_client_on_hsk_done,
 | 
			
		||||
    .on_conn_closed         = duck_client_on_conn_closed,
 | 
			
		||||
    .on_dg_write            = duck_client_on_dg_write,
 | 
			
		||||
    .on_datagram            = duck_client_on_datagram,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    LSQ_NOTICE(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
            , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
 | 
			
		||||
    TAILQ_INIT(&sports);
 | 
			
		||||
    prog_init(&prog, 0, &sports, &duck_client_stream_if, &prog);
 | 
			
		||||
    prog.prog_settings.es_datagrams = 1;
 | 
			
		||||
    prog.prog_settings.es_init_max_data = 0;
 | 
			
		||||
    prog.prog_settings.es_init_max_streams_bidi = 0;
 | 
			
		||||
    prog.prog_settings.es_init_max_streams_uni = 0;
 | 
			
		||||
    prog.prog_settings.es_max_streams_in = 0;
 | 
			
		||||
    prog.prog_api.ea_alpn = "siduck-00";
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
    int flags = fcntl(STDIN_FILENO, F_GETFL);
 | 
			
		||||
    flags |= O_NONBLOCK;
 | 
			
		||||
    if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
 | 
			
		||||
    {
 | 
			
		||||
        perror("fcntl");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    {
 | 
			
		||||
        u_long on = 1;
 | 
			
		||||
        ioctlsocket(STDIN_FILENO, FIONBIO, &on);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
    if (0 != prog_connect(&prog, NULL, 0))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not connect");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										160
									
								
								bin/duck_server.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								bin/duck_server.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * A duck quacks!  The server for the siduck protocol:
 | 
			
		||||
 *      https://tools.ietf.org/html/draft-pardue-quic-siduck-00
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#else
 | 
			
		||||
#include "vc_compat.h"
 | 
			
		||||
#include "getopt.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "lsquic.h"
 | 
			
		||||
#include "../src/liblsquic/lsquic_hash.h"
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
#include "test_cert.h"
 | 
			
		||||
#include "prog.h"
 | 
			
		||||
 | 
			
		||||
#include "../src/liblsquic/lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
duck_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    LSQ_NOTICE("New siduck connection established!");
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
duck_server_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
    LSQ_NOTICE("siduck connection closed");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Expected request and response of the siduck protocol */
 | 
			
		||||
#define REQUEST "quack"
 | 
			
		||||
#define RESPONSE "quack-ack"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static ssize_t
 | 
			
		||||
duck_on_dg_write (lsquic_conn_t *conn, void *buf, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
    int s;
 | 
			
		||||
 | 
			
		||||
    /* We only write one response */
 | 
			
		||||
    s = lsquic_conn_want_datagram_write(conn, 0);
 | 
			
		||||
    assert(s == 1);     /* Old value was "yes" */
 | 
			
		||||
 | 
			
		||||
    if (sz >= sizeof(RESPONSE) - 1)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("wrote `%s' in response", RESPONSE);
 | 
			
		||||
        memcpy(buf, RESPONSE, sizeof(RESPONSE) - 1);
 | 
			
		||||
        lsquic_conn_close(conn);    /* Close connection right away */
 | 
			
		||||
        return sizeof(RESPONSE) - 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
duck_on_datagram (lsquic_conn_t *conn, const void *buf, size_t bufsz)
 | 
			
		||||
{
 | 
			
		||||
    int s;
 | 
			
		||||
 | 
			
		||||
    if (bufsz == sizeof(REQUEST) - 1
 | 
			
		||||
            && 0 == memcmp(buf, REQUEST, sizeof(REQUEST) - 1))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("received the expected `%s' request", REQUEST);
 | 
			
		||||
        s = lsquic_conn_want_datagram_write(conn, 1);
 | 
			
		||||
        assert(s == 0);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("unexpected request received, abort connection");
 | 
			
		||||
        lsquic_conn_abort(conn);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if duck_server_stream_if = {
 | 
			
		||||
    .on_new_conn            = duck_server_on_new_conn,
 | 
			
		||||
    .on_conn_closed         = duck_server_on_conn_closed,
 | 
			
		||||
    .on_dg_write            = duck_on_dg_write,
 | 
			
		||||
    .on_datagram            = duck_on_datagram,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    printf(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
                , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
 | 
			
		||||
    TAILQ_INIT(&sports);
 | 
			
		||||
    prog_init(&prog, LSENG_SERVER, &sports, &duck_server_stream_if, NULL);
 | 
			
		||||
    prog.prog_settings.es_datagrams = 1;
 | 
			
		||||
    prog.prog_settings.es_init_max_data = 0;
 | 
			
		||||
    prog.prog_settings.es_init_max_streams_bidi = 0;
 | 
			
		||||
    prog.prog_settings.es_init_max_streams_uni = 0;
 | 
			
		||||
    prog.prog_settings.es_max_streams_in = 0;
 | 
			
		||||
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != add_alpn("siduck-00"))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not add ALPN");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										212
									
								
								src/liblsquic/lsquic_adaptive_cc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/liblsquic/lsquic_adaptive_cc.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,212 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/* lsquic_adaptive_cc.c -- adaptive congestion controller */
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/queue.h>
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
#include <vc_compat.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "lsquic_int_types.h"
 | 
			
		||||
#include "lsquic_types.h"
 | 
			
		||||
#include "lsquic_hash.h"
 | 
			
		||||
#include "lsquic_util.h"
 | 
			
		||||
#include "lsquic_cong_ctl.h"
 | 
			
		||||
#include "lsquic_sfcw.h"
 | 
			
		||||
#include "lsquic_conn_flow.h"
 | 
			
		||||
#include "lsquic_varint.h"
 | 
			
		||||
#include "lsquic_hq.h"
 | 
			
		||||
#include "lsquic_stream.h"
 | 
			
		||||
#include "lsquic_rtt.h"
 | 
			
		||||
#include "lsquic_conn_public.h"
 | 
			
		||||
#include "lsquic_packet_common.h"
 | 
			
		||||
#include "lsquic_packet_out.h"
 | 
			
		||||
#include "lsquic_bw_sampler.h"
 | 
			
		||||
#include "lsquic_minmax.h"
 | 
			
		||||
#include "lsquic_bbr.h"
 | 
			
		||||
#include "lsquic_cubic.h"
 | 
			
		||||
#include "lsquic_adaptive_cc.h"
 | 
			
		||||
 | 
			
		||||
#define LSQUIC_LOGGER_MODULE LSQLM_ADAPTIVE_CC
 | 
			
		||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(acc->acc_cubic.cu_conn)
 | 
			
		||||
#include "lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define CALL_BOTH(method, ...) do {                             \
 | 
			
		||||
    lsquic_cong_bbr_if.method(&acc->acc_bbr, __VA_ARGS__);      \
 | 
			
		||||
    lsquic_cong_cubic_if.method(&acc->acc_cubic, __VA_ARGS__);  \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define CALL_BOTH_MAYBE(method, ...) do {                           \
 | 
			
		||||
    if (lsquic_cong_bbr_if.method)                                  \
 | 
			
		||||
        lsquic_cong_bbr_if.method(&acc->acc_bbr, __VA_ARGS__);      \
 | 
			
		||||
    if (lsquic_cong_cubic_if.method)                                \
 | 
			
		||||
        lsquic_cong_cubic_if.method(&acc->acc_cubic, __VA_ARGS__);  \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define CALL_BOTH0(method) do {                                 \
 | 
			
		||||
    lsquic_cong_bbr_if.method(&acc->acc_bbr);                   \
 | 
			
		||||
    lsquic_cong_cubic_if.method(&acc->acc_cubic);               \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_init (void *cong_ctl, const struct lsquic_conn_public *conn_pub,
 | 
			
		||||
                                                enum quic_ft_bit retx_frames)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH(cci_init, conn_pub, retx_frames);
 | 
			
		||||
    LSQ_DEBUG("initialized");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_reinit (void *cong_ctl)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH0(cci_reinit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_ack (void *cong_ctl, struct lsquic_packet_out *packet_out,
 | 
			
		||||
                    unsigned packet_sz, lsquic_time_t now, int app_limited)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH(cci_ack, packet_out, packet_sz, now, app_limited);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_loss (void *cong_ctl)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH0(cci_loss);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_begin_ack (void *cong_ctl, lsquic_time_t ack_time,
 | 
			
		||||
                                                        uint64_t in_flight)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH_MAYBE(cci_begin_ack, ack_time, in_flight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_end_ack (void *cong_ctl, uint64_t in_flight)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH_MAYBE(cci_end_ack, in_flight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_sent (void *cong_ctl, struct lsquic_packet_out *packet_out,
 | 
			
		||||
                                        uint64_t in_flight, int app_limited)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH_MAYBE(cci_sent, packet_out, in_flight, app_limited);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_lost (void *cong_ctl, struct lsquic_packet_out *packet_out,
 | 
			
		||||
                                                        unsigned packet_sz)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH_MAYBE(cci_lost, packet_out, packet_sz);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_timeout (void *cong_ctl)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH0(cci_timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_was_quiet (void *cong_ctl, lsquic_time_t now, uint64_t in_flight)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH(cci_was_quiet, now, in_flight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static uint64_t
 | 
			
		||||
adaptive_cc_get_cwnd (void *cong_ctl)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
    uint64_t rv[2];
 | 
			
		||||
 | 
			
		||||
    rv[0] = lsquic_cong_cubic_if.cci_get_cwnd(&acc->acc_cubic);
 | 
			
		||||
    rv[1] = lsquic_cong_bbr_if.cci_get_cwnd(&acc->acc_bbr);
 | 
			
		||||
 | 
			
		||||
    if (acc->acc_flags & ACC_CUBIC)
 | 
			
		||||
        return rv[0];
 | 
			
		||||
    else
 | 
			
		||||
        return rv[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static uint64_t
 | 
			
		||||
adaptive_cc_pacing_rate (void *cong_ctl, int in_recovery)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
    uint64_t rv[2];
 | 
			
		||||
 | 
			
		||||
    rv[0] = lsquic_cong_cubic_if.cci_pacing_rate(&acc->acc_cubic, in_recovery);
 | 
			
		||||
    rv[1] = lsquic_cong_bbr_if.cci_pacing_rate(&acc->acc_bbr, in_recovery);
 | 
			
		||||
 | 
			
		||||
    if (acc->acc_flags & ACC_CUBIC)
 | 
			
		||||
        return rv[0];
 | 
			
		||||
    else
 | 
			
		||||
        return rv[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adaptive_cc_cleanup (void *cong_ctl)
 | 
			
		||||
{
 | 
			
		||||
    struct adaptive_cc *const acc = cong_ctl;
 | 
			
		||||
 | 
			
		||||
    CALL_BOTH0(cci_cleanup);
 | 
			
		||||
    LSQ_DEBUG("cleanup");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct cong_ctl_if lsquic_cong_adaptive_if =
 | 
			
		||||
{
 | 
			
		||||
    .cci_ack           = adaptive_cc_ack,
 | 
			
		||||
    .cci_begin_ack     = adaptive_cc_begin_ack,
 | 
			
		||||
    .cci_end_ack       = adaptive_cc_end_ack,
 | 
			
		||||
    .cci_cleanup       = adaptive_cc_cleanup,
 | 
			
		||||
    .cci_get_cwnd      = adaptive_cc_get_cwnd,
 | 
			
		||||
    .cci_init          = adaptive_cc_init,
 | 
			
		||||
    .cci_pacing_rate   = adaptive_cc_pacing_rate,
 | 
			
		||||
    .cci_loss          = adaptive_cc_loss,
 | 
			
		||||
    .cci_lost          = adaptive_cc_lost,
 | 
			
		||||
    .cci_reinit        = adaptive_cc_reinit,
 | 
			
		||||
    .cci_timeout       = adaptive_cc_timeout,
 | 
			
		||||
    .cci_sent          = adaptive_cc_sent,
 | 
			
		||||
    .cci_was_quiet     = adaptive_cc_was_quiet,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										24
									
								
								src/liblsquic/lsquic_adaptive_cc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/liblsquic/lsquic_adaptive_cc.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
 | 
			
		||||
/*
 | 
			
		||||
 * lsquic_adaptive_cc.h -- Adaptive congestion controller
 | 
			
		||||
 *
 | 
			
		||||
 * The controller begins using BBRv1, but keeps Cubic state as well.
 | 
			
		||||
 * When RTT is known, we pick either Cubic (small RTT) or BBRv1 (large
 | 
			
		||||
 * RTT).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef LSQUIC_ADAPTIVE_CC_H
 | 
			
		||||
#define LSQUIC_ADAPTIVE_CC_H 1
 | 
			
		||||
 | 
			
		||||
struct adaptive_cc
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_cubic     acc_cubic;
 | 
			
		||||
    struct lsquic_bbr       acc_bbr;
 | 
			
		||||
    enum {
 | 
			
		||||
        ACC_CUBIC,  /* If set, use Cubic; otherwise, use BBR */
 | 
			
		||||
    }                       acc_flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct cong_ctl_if lsquic_cong_adaptive_if;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue