Release 2.2.0: server included, ID-22 supported (#76)

This commit is contained in:
LiteSpeed Tech 2019-09-11 11:27:58 -04:00 committed by GitHub
parent 8cba36d873
commit 5392f7a3b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
221 changed files with 113761 additions and 4563 deletions

View File

@ -9,10 +9,13 @@ task:
# This is so that both GQUIC and IETF branches build. Just picking
# a known good revision:
- git checkout 32e59d2d3264e4e104b355ef73663b8b79ac4093
- patch -p1 -i ../patches/boringssl-meds.patch
- cmake .
- make
- cd -
script:
- git submodule init
- git submodule update
- cmake -DBORINGSSL_DIR=$PWD/boringssl .
- make
- make test

4
.gitmodules vendored Normal file
View File

@ -0,0 +1,4 @@
[submodule "src/liblsquic/ls-qpack"]
path = src/liblsquic/ls-qpack
url = https://github.com/litespeedtech/ls-qpack
branch = v0.9.13

View File

@ -24,9 +24,12 @@ before_script:
# This is so that both GQUIC and IETF branches build. Just picking
# a known good revision:
- git checkout 32e59d2d3264e4e104b355ef73663b8b79ac4093
- patch -p1 -i ../patches/boringssl-meds.patch
- cmake .
- make
- cd -
- git submodule init
- git submodule update
- cmake -DBORINGSSL_DIR=$PWD/boringssl .
script:
# Now build lsquic-client

View File

@ -99,6 +99,9 @@ struct lsquic_engine_settings and struct lsquic_stream_if, as well as:
- Interface for sending outgoing packets, ea_packets_out
- Interface for allocating memory for outgoing packet buffers
(optional).
- Interface for share memory hash, ea_shi
- Optional interface for reporting connections whose handshake
did not complete (ea_bad_handshake)
ea_packets_out is a pointer to a function of type lsquic_packets_out_f.
The engine calls this function when it is appropriate to send out packets

View File

@ -1,3 +1,8 @@
2019-09-11
- 2.2.0
- [FEATURE] Server code is included in the library
- [FEATURE] IETF QUIC and HTTP/3 Support (ID-22)
2019-05-13
- 1.21.2
- [OPTIMIZATION] HPACK: use history to improve compression performance

View File

@ -18,7 +18,6 @@ IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
SET(CMAKE_BUILD_TYPE Debug)
ENDIF()
MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
@ -26,22 +25,30 @@ IF (NOT MSVC)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wall -Wextra -Wno-unused-parameter")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fno-omit-frame-pointer")
INCLUDE(CheckCCompilerFlag)
CHECK_C_COMPILER_FLAG(-Wno-implicit-fallthrough HAS_NO_IMPLICIT_FALLTHROUGH)
IF (HAS_NO_IMPLICIT_FALLTHROUGH)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wno-implicit-fallthrough")
ENDIF()
IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.3)
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Wno-missing-field-initializers")
ENDIF()
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Werror")
IF(CMAKE_C_COMPILER MATCHES "clang" AND
NOT "$ENV{TRAVIS}" MATCHES "^true$")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fsanitize=address")
ENDIF()
# Uncomment to enable fault injection testing via libfiu:
#SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DFIU_ENABLE=1")
# Uncomment to enable cleartext protocol mode (no crypto):
#SET (MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_ENABLE_HANDSHAKE_DISABLE=1")
#SET(LIBS ${LIBS} fiu)
ELSE()
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O3 -g0")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DNDEBUG")
# Comment out the following line to compile out debug messages:
#SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_INFO")
ENDIF()
@ -70,7 +77,6 @@ IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
#SET(LIBS ${LIBS} fiu)
ELSE()
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Ox")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DNDEBUG")
# Comment out the following line to compile out debug messages:
#SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_INFO")
ENDIF()
@ -189,19 +195,24 @@ ELSE()
MESSAGE(STATUS "libevent not found")
ENDIF()
add_executable(http_server test/http_server.c test/prog.c test/test_common.c test/test_cert.c)
SET(LIBS lsquic ${EVENT_LIB} ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${ZLIB_LIB} ${LIBS})
IF (NOT MSVC)
add_executable(http_client
test/http_client.c
test/prog.c
test/test_common.c
test/test_cert.c
)
LIST(APPEND LIBS pthread m)
#MSVC
ELSE()
add_executable(http_client
test/http_client.c
test/prog.c
@ -214,13 +225,13 @@ LIST(APPEND LIBS ws2_32)
ENDIF()
TARGET_LINK_LIBRARIES(http_client ${LIBS})
TARGET_LINK_LIBRARIES(http_server ${LIBS})
add_subdirectory(src)
add_subdirectory(test)
IF(NOT (CMAKE_C_FLAGS MATCHES "-DNDEBUG"))
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Our test framework relies on assertions, only compile if assertions are
# enabled.
#

View File

@ -2,7 +2,18 @@
LSQUIC Examples
===============
test/http_client.c demonstrates how to use HTTP features of QUIC.
LSQUIC comes with several examples of how the library is used.
The client and server programs described below are built on a common
framework and share many options.
HTTP client and server
----------------------
See test/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.
Usage Examples
--------------
@ -65,10 +76,41 @@ To perform load testing, it is good to mix sending and receiving data:
If you don't want to create a hundred 256-megabyte out-get.* files, use -K
flag to discard output.
Testing Large Packet Sizes
---------------------------------
IETF QUIC supports all valid UDP packet sizes. This section outlines the
environment setup and testing parameters necessary to test this feature.
Compilation
- Make sure to compile the library in DEBUG mode so that the NDEBUG
define is off.
- Build both the http_client and http_server test programs.
Running Instructions
- On the server side, define the environment variable
LSQUIC_CN_PACK_SIZE and set it to the intended packet size.
Valid sizes are up to 65507 (IPv4).
- Use the -W flag for http_client and http_server for the ability
to send packets of large size.
- On the client side, use the -z flag to specify the maximum size
of packet that the client will accept.
Example Usage
./http_client -p /file-1M -H www.litespeedtech.com -s 192.168.0.85:5443
-o version=FF000014 -z 65507 -W
./http_server -c www.litespeedtech.com,certschain,privkey
-s 0.0.0.0:5443 -W
Additional Notes
Since this feature does not have MTU discovery enabled at the time of
writing, make sure to use client and server machines that share a path
with the intended MTU.
Control QUIC Settings via -o Flag
---------------------------------
Most of the settings in struct lsquic_engine_settings can be controlled
Most of the settings in struct squic_engine_settings can be controlled
via -o flag. With exception of es_versions, which is a bit mask, other
es_* options can be mapped to corresponding -o value via s/^es_//:
@ -77,6 +119,16 @@ es_* options can be mapped to corresponding -o value via s/^es_//:
And so on.
For example, to test version negotiation:
./http_server -c www.litespeedtech.com,certschain,privkey \
-o version=Q035 -L debug 2>server.out &
./http_client -H www.litespeedtech.com -p Makefile -L debug 2>client.out
Above, the client will start with the default, which is the highest supported
QUIC version, which the server should negotiate down. You should see it from
the log files.
The code to set options via -o flag lives in set_engine_option(). It is good
to update this function at the same time as member fields are added to struct
lsquic_engine_settings.
@ -84,6 +136,46 @@ lsquic_engine_settings.
Control LSQUIC Behavior via Environment Variables
-------------------------------------------------
LSQUIC_PACKET_OUT_LIMIT
If set, the value of this environment variable is the maximum number
of packets that can be sent out in one shot. The limit is in the test
program framework's ea_packets_out() callback.
It is not applicable when sendmmsg(2) is used.
Note 1: sendmmsg can be enabled using -g option, if available for your
platform.
Note 2: see -m option for a related packet-out limitation.
LSQUIC_DISABLE_HANDSHAKE
If set (to anything, not any particular value), the QUIC handshake is
disabled and packets are not encrypted. This can be useful to:
a) profile functions in LSQUIC without accounting for crypto stuff,
which tends to dwarf everything else;
b) see bytes in the clear on the wire; and
c) compare throughput performance to TCP without crypto.
This functionality is compiled in if the somewhat-awkwardly named
LSQUIC_ENABLE_HANDSHAKE_DISABLE is set to 1. By default, it is enabled
in debug builds and disabled in optimized builds.
LSQUIC_LOSE_PACKETS_RE
If set, this regular expression specifies the numbers of packets which
the sender will lose on purpose. For example:
export LSQUIC_LOSE_PACKETS_RE='^(3|5|10)$'
The act of losing a packet is performed by changing its payload to
zero-filled buffer of the same size, which is almost as good as
not sending anything. The latter is difficult to implement without
negatively affecting the regular code flow.
Only available in debug builds.
LSQUIC_PACER_INTERTICK
Number of microsecods to use as constant intertick time in lieu of the
@ -104,6 +196,21 @@ LSQUIC_RANDOM_SEND_FAILURE
Only available when compiled with -DLSQUIC_RANDOM_SEND_FAILURE=1
LSQUIC_LOG_SECRETS
If set to true value, crypto secrets will be logged. Applies to
IETF QUIC only.
LSQUIC_COALESCE
If set to false, packets are not coalesced. Defaults to true.
LSQUIC_USE_POOLS
If set to false, all memory pooling code is replaced with calls to
malloc() and free(). This facilitates debugging memory issues.
The default is true.
Control Network-Related Stuff
-----------------------------
@ -116,9 +223,16 @@ Control Network-Related Stuff
sndbuf=12345 # Sets SO_SNDBUF
rcvbuf=12345 # Sets SO_RCVBUF
-g Use sendmmsg() to send packets. This is only compiled in
if available.
More Compilation Options
------------------------
-DLSQUIC_ENABLE_HANDSHAKE_DISABLE=1
Support disabling of handshake. See above.
-DLSQUIC_CONN_STATS=1
Track some statistics about connections -- packets in, sent, delayed,
@ -140,6 +254,11 @@ More Compilation Options
Turn off statistics collection performed by the send controller: number
of packets sent, resent, and delayed.
-DLOG_PACKET_CHECKSUM=1
When turned on, CRC32 checksum of each sent and received packet is
logged as an event.
-DLSQUIC_LOWEST_LOG_LEVEL=LSQ_LOG_WARN
If you want to go even faster: compile out some log levels entirely.

View File

@ -1,25 +1,22 @@
[![Build Status](https://travis-ci.org/litespeedtech/lsquic-client.svg?branch=master)](https://travis-ci.org/litespeedtech/lsquic-client)
[![Build Status](https://api.cirrus-ci.com/github/litespeedtech/lsquic-client.svg)](https://cirrus-ci.com/github/litespeedtech/lsquic-client)
[![Build status](https://ci.appveyor.com/api/projects/status/kei9649t9leoqicr?svg=true)](https://ci.appveyor.com/project/litespeedtech/lsquic-client)
[![Build Status](https://travis-ci.org/litespeedtech/lsquic.svg?branch=master)](https://travis-ci.org/litespeedtech/lsquic)
[![Build Status](https://api.cirrus-ci.com/github/litespeedtech/lsquic.svg)](https://cirrus-ci.com/github/litespeedtech/lsquic)
[![Build status](https://ci.appveyor.com/api/projects/status/kei9649t9leoqicr?svg=true)](https://ci.appveyor.com/project/litespeedtech/lsquic)
LiteSpeed QUIC (LSQUIC) Client Library README
LiteSpeed QUIC (LSQUIC) Library README
=============================================
Description
-----------
LiteSpeed QUIC (LSQUIC) Client Library is an open-source implementation
of QUIC functionality for clients. It is released in the hope to speed
LiteSpeed QUIC (LSQUIC) Library is an open-source implementation of QUIC
functionality for servers and clients. It is released in the hope to speed
the adoption of QUIC. Most of the code in this distribution is used in
our own products: LiteSpeed Web Server and ADC. 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!
our own products: LiteSpeed Web Server, LiteSpeed ADC, and OpenLiteSpeed.
We think it is free of major problems. Nevertheless, do not hesitate to
report bugs back to us. Even better, send us fixes and improvements!
Currently supported QUIC versions are Q035, Q039, Q043, and Q044. Support
for newer versions will be added soon after they are released. The
version(s) specified by IETF QUIC WG are being developed on
[one or more branches](https://github.com/litespeedtech/lsquic-client/branches).
When deemed stable, the IETF QUIC support will be added to the master branch.
Currently supported QUIC versions are Q039, Q043, Q046, and ID-22. Support
for newer versions will be added soon after they are released.
Documentation
-------------
@ -49,13 +46,9 @@ git clone https://boringssl.googlesource.com/boringssl
cd boringssl
```
2. Check out stable branch:
You may need to install pre-requisites like zlib and libevent.
```
git checkout chromium-stable
```
3. Compile the library
2. Compile the library
```
cmake . && make
@ -72,17 +65,20 @@ If you want to turn on optimizations, do
cmake -DCMAKE_BUILD_TYPE=Release . && make
```
Building LSQUIC Client Library
------------------------------
Building LSQUIC Library
-----------------------
LSQUIC's `http_client` and the tests link BoringSSL libraries statically.
Following previous section, you can build LSQUIC as follows:
LSQUIC's `http_client`, `http_server`, and the tests link BoringSSL
libraries statically. Following previous section, you can build LSQUIC
as follows:
1. Get the source code
```
git clone https://github.com/litespeedtech/lsquic-client.git
cd lsquic-client
git clone https://github.com/litespeedtech/lsquic.git
cd lsquic
git submodule init
git submodule update
```
2. Compile the library
@ -104,31 +100,31 @@ Building with Docker
---------
The library and http_client example can be built with Docker.
```
docker build -t lsquic-client .
docker build -t lsquic .
```
Then you can use the http_client example from the command line.
```
docker run -it --rm lsquic-client http_client -H www.google.com -s 74.125.22.106:443 -p /
docker run -it --rm lsquic http_client -H www.google.com -s 74.125.22.106:443 -p /
```
Platforms
---------
The client library has been tested on the following platforms:
The library has been tested on the following platforms:
- Linux
- i386
- x86_64
- ARM (Raspberry Pi 3)
- FreeBSD
- i386
- Windows
- x86_64
- MacOS
- x86_64
- Windows (this needs updating for the server part, now broken)
- x86_64
Have fun,
LiteSpeed QUIC Team.
Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc
Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc

View File

@ -23,9 +23,9 @@ struct sockaddr;
extern "C" {
#endif
#define LSQUIC_MAJOR_VERSION 1
#define LSQUIC_MINOR_VERSION 21
#define LSQUIC_PATCH_VERSION 2
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 2
#define LSQUIC_PATCH_VERSION 0
/**
* Engine flags:
@ -49,7 +49,9 @@ enum lsquic_version
{
/** Q035. This is the first version to be supported by LSQUIC. */
LSQVER_035,
/* Support for this version has been removed. The comment remains to
* document the changes.
*/
/*
* Q037. This version is like Q035, except the way packet hashes are
@ -101,7 +103,9 @@ enum lsquic_version
* Q044. IETF-like packet headers are used. Frames are the same as
* in Q043. Server never includes CIDs in short packets.
*/
LSQVER_044,
/* Support for this version has been removed. The comment remains to
* document the changes.
*/
/**
* Q046. Use IETF Draft-17 compatible packet headers.
@ -120,27 +124,41 @@ enum lsquic_version
#define LSQUIC_EXPERIMENTAL_Q098 0
#endif
/**
* IETF QUIC Draft-22
*/
LSQVER_ID22,
/**
* Special version to trigger version negotiation.
* [draft-ietf-quic-transport-11], Section 3.
*/
LSQVER_VERNEG,
N_LSQVER
};
/**
* We currently support versions 35, 39, 43, 44, and 46.
* We currently support versions 39, 43, 46, and IETF Draft-22
* @see lsquic_version
*/
#define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1)
#define LSQUIC_EXPERIMENTAL_VERSIONS (0 \
| LSQUIC_EXPERIMENTAL_Q098)
#define LSQUIC_DEPRECATED_VERSIONS 0
#define LSQUIC_GQUIC_HEADER_VERSIONS ( \
(1 << LSQVER_035) | (1 << LSQVER_039) | (1 << LSQVER_043))
/**
* List of versions in which the server never includes CID in short packets.
*/
#define LSQUIC_FORCED_TCID0_VERSIONS ((1 << LSQVER_044) | (1 << LSQVER_046))
#define LSQUIC_FORCED_TCID0_VERSIONS (1 << LSQVER_046)
#define LSQUIC_EXPERIMENTAL_VERSIONS ( \
(1 << LSQVER_VERNEG) | LSQUIC_EXPERIMENTAL_Q098)
#define LSQUIC_DEPRECATED_VERSIONS 0
#define LSQUIC_GQUIC_HEADER_VERSIONS ((1 << LSQVER_039) | (1 << LSQVER_043))
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID22) | (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID22) | (1 << LSQVER_VERNEG))
enum lsquic_hsk_status
{
@ -156,6 +174,11 @@ enum lsquic_hsk_status
* The handshake succeeded with 0-RTT.
*/
LSQ_HSK_0RTT_OK,
/**
* The handshake failed because of 0-RTT (early data rejected). Retry
* the connection without 0-RTT.
*/
LSQ_HSK_0RTT_FAIL,
};
/**
@ -192,6 +215,7 @@ 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);
/* This callback in only called in client mode */
/**
* When handshake is completed, this callback is called. `ok' is set
* to true if handshake was successful; otherwise, `ok' is set to
@ -200,8 +224,31 @@ struct lsquic_stream_if {
* This callback is optional.
*/
void (*on_hsk_done)(lsquic_conn_t *c, enum lsquic_hsk_status s);
/**
* When server sends a token in NEW_TOKEN frame, this callback is called.
* The callback is optional.
*/
void (*on_new_token)(lsquic_conn_t *c, const unsigned char *token,
size_t token_size);
/**
* This optional callback lets client record information needed to
* perform a zero-RTT handshake next time around.
*/
void (*on_zero_rtt_info)(lsquic_conn_t *c, const unsigned char *, size_t);
};
struct ssl_ctx_st;
struct ssl_st;
/**
* QUIC engine in server role needs access to certificates. This is
* accomplished by providing a callback and a context to the engine
* constructor.
*/
typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni);
/**
* Minimum flow control window is set to 16 KB for both client and server.
* This means we can send up to this amount of data before handshake gets
@ -226,12 +273,36 @@ struct lsquic_stream_if {
#define LSQUIC_DF_SFCW_CLIENT (6 * 1024 * 1024)
#define LSQUIC_DF_MAX_STREAMS_IN 100
/* IQUIC uses different names for these: */
#define LSQUIC_DF_INIT_MAX_DATA_SERVER LSQUIC_DF_CFCW_SERVER
#define LSQUIC_DF_INIT_MAX_DATA_CLIENT LSQUIC_DF_CFCW_CLIENT
#define LSQUIC_DF_INIT_MAX_STREAM_DATA_BIDI_REMOTE_SERVER LSQUIC_DF_SFCW_SERVER
#define LSQUIC_DF_INIT_MAX_STREAM_DATA_BIDI_LOCAL_SERVER 0
#define LSQUIC_DF_INIT_MAX_STREAM_DATA_BIDI_REMOTE_CLIENT 0
#define LSQUIC_DF_INIT_MAX_STREAM_DATA_BIDI_LOCAL_CLIENT LSQUIC_DF_SFCW_CLIENT
#define LSQUIC_DF_INIT_MAX_STREAMS_BIDI LSQUIC_DF_MAX_STREAMS_IN
#define LSQUIC_DF_INIT_MAX_STREAMS_UNI_CLIENT 100
#define LSQUIC_DF_INIT_MAX_STREAMS_UNI_SERVER 3
/* XXX What's a good value here? */
#define LSQUIC_DF_INIT_MAX_STREAM_DATA_UNI_CLIENT (32 * 1024)
#define LSQUIC_DF_INIT_MAX_STREAM_DATA_UNI_SERVER (12 * 1024)
/**
* Default idle connection time in seconds.
*/
#define LSQUIC_DF_IDLE_TIMEOUT 30
/**
* Default ping period in seconds.
*/
#define LSQUIC_DF_PING_PERIOD 15
/**
* Default handshake timeout in microseconds.
*/
#define LSQUIC_DF_HANDSHAKE_TO (10 * 1000 * 1000)
#define LSQUIC_DF_IDLE_CONN_TO (30 * 1000 * 1000)
#define LSQUIC_DF_IDLE_CONN_TO (LSQUIC_DF_IDLE_TIMEOUT * 1000 * 1000)
#define LSQUIC_DF_SILENT_CLOSE 1
/** Default value of maximum header list size. If set to non-zero value,
@ -249,11 +320,18 @@ struct lsquic_stream_if {
#define LSQUIC_DF_SUPPORT_SREJ_CLIENT 0 /* TODO: client support */
/** Do not use NSTP by default */
#define LSQUIC_DF_SUPPORT_NSTP 0
/** TODO: IETF QUIC clients do not support push */
#define LSQUIC_DF_SUPPORT_PUSH 1
#define LSQUIC_DF_SUPPORT_TCID0 0
#define LSQUIC_DF_SUPPORT_TCID0 1
/** By default, LSQUIC ignores Public Reset packets. */
#define LSQUIC_DF_HONOR_PRST 0
/**
* By default, LSQUIC will not send Public Reset packets in response to
* packets that specify unknown connections.
*/
#define LSQUIC_DF_SEND_PRST 0
/** By default, infinite loop checks are turned on */
#define LSQUIC_DF_PROGRESS_CHECK 1000
@ -269,6 +347,32 @@ struct lsquic_stream_if {
/** Default clock granularity is 1000 microseconds */
#define LSQUIC_DF_CLOCK_GRANULARITY 1000
/** The default value is 8 for simplicity */
#define LSQUIC_DF_SCID_LEN 8
/** The default value is 60 CIDs per minute */
#define LSQUIC_DF_SCID_ISS_RATE 60
#define LSQUIC_DF_QPACK_DEC_MAX_BLOCKED 100
#define LSQUIC_DF_QPACK_DEC_MAX_SIZE 4096
#define LSQUIC_DF_QPACK_ENC_MAX_BLOCKED 100
#define LSQUIC_DF_QPACK_ENC_MAX_SIZE 4096
/** ECN is enabled by default */
#define LSQUIC_DF_ECN 1
/**
* The default number of the priority placeholders is higher than the
* recommended value of 16 to give the clients even more freedom.
*/
#define LSQUIC_DF_H3_PLACEHOLDERS 50
/** Allow migration by default */
#define LSQUIC_DF_ALLOW_MIGRATION 1
/* 1: Cubic; 2: BBR */
#define LSQUIC_DF_CC_ALGO 1
struct lsquic_engine_settings {
/**
* This is a bit mask wherein each bit corresponds to a value in
@ -276,6 +380,8 @@ struct lsquic_engine_settings {
* version and goes down. Server supports either of the versions
* specified here.
*
* This setting applies to both Google and IETF QUIC.
*
* @see lsquic_version
*/
unsigned es_versions;
@ -325,10 +431,12 @@ struct lsquic_engine_settings {
* For client, this can be set to an arbitrary value (zero turns the
* timeout off).
*
* For server, this value is limited to about 16 seconds. Do not set
* it to zero.
*/
unsigned long es_handshake_to;
/** ICSL in microseconds */
/** ICSL in microseconds; GQUIC only */
unsigned long es_idle_conn_to;
/** SCLS (silent close) */
@ -344,10 +452,20 @@ struct lsquic_engine_settings {
/** UAID -- User-Agent ID. Defaults to @ref LSQUIC_DF_UA. */
const char *es_ua;
/**
* More parameters for server
*/
uint64_t es_sttl; /* SCFG TTL in seconds */
uint32_t es_pdmd; /* One fixed value X509 */
uint32_t es_aead; /* One fixed value AESG */
uint32_t es_kexs; /* One fixed value C255 */
/* Maximum number of incoming connections in inchoate state. This is
* only applicable in server mode.
*/
unsigned es_max_inchoate;
/**
* Support SREJ: for client side, this means supporting server's SREJ
* responses (this does not work yet) and for server side, this means
@ -364,6 +482,8 @@ struct lsquic_engine_settings {
* b) All incoming pushed streams get reset immediately.
* (For maximum effect, set es_max_streams_in to 0.)
*
* For server:
* lsquic_conn_push_stream() will return -1.
*/
int es_support_push;
@ -376,7 +496,7 @@ struct lsquic_engine_settings {
* (source-addr, dest-addr) tuple, thereby making it necessary to create
* a socket for each connection.
*
* This option has no effect in Q044 or Q046, as the server never includes
* This option has no effect in Q046, as the server never includes
* CIDs in the short packets.
*
* The default is @ref LSQUIC_DF_SUPPORT_TCID0.
@ -402,6 +522,13 @@ struct lsquic_engine_settings {
*/
int es_honor_prst;
/**
* If set to true value, the library will send Public Reset packets
* in response to incoming packets with unknown Connection IDs.
* The default is @ref LSQUIC_DF_SEND_PRST.
*/
int es_send_prst;
/**
* A non-zero value enables internal checks that identify suspected
* infinite loops in user @ref on_read and @ref on_write callbacks
@ -461,6 +588,169 @@ struct lsquic_engine_settings {
* is in microseconds; default is @ref LSQUIC_DF_CLOCK_GRANULARITY.
*/
unsigned es_clock_granularity;
/* The following settings are specific to IETF QUIC. */
/* vvvvvvvvvvv */
/**
* Initial max data.
*
* This is a transport parameter.
*
* Depending on the engine mode, the default value is either
* @ref LSQUIC_DF_INIT_MAX_DATA_CLIENT or
* @ref LSQUIC_DF_INIT_MAX_DATA_SERVER.
*/
unsigned es_init_max_data;
/**
* Initial max stream data.
*
* This is a transport parameter.
*
* Depending on the engine mode, the default value is either
* @ref LSQUIC_DF_INIT_MAX_STREAM_DATA_CLIENT or
* @ref LSQUIC_DF_INIT_MAX_STREAM_DATA_BIDI_REMOTE_SERVER.
*/
unsigned es_init_max_stream_data_bidi_remote;
unsigned es_init_max_stream_data_bidi_local;
/**
* Initial max stream data for unidirectional streams initiated
* by remote endpoint.
*
* This is a transport parameter.
*
* Depending on the engine mode, the default value is either
* @ref LSQUIC_DF_INIT_MAX_STREAM_DATA_UNI_CLIENT or
* @ref LSQUIC_DF_INIT_MAX_STREAM_DATA_UNI_SERVER.
*/
unsigned es_init_max_stream_data_uni;
/**
* Maximum initial number of bidirectional stream.
*
* This is a transport parameter.
*
* Default value is @ref LSQUIC_DF_INIT_MAX_STREAMS_BIDI.
*/
unsigned es_init_max_streams_bidi;
/**
* Maximum initial number of unidirectional stream.
*
* This is a transport parameter.
*
* Default value is @ref LSQUIC_DF_INIT_MAX_STREAMS_UNI_CLIENT or
* @ref LSQUIC_DF_INIT_MAX_STREAM_DATA_UNI_SERVER.
*/
unsigned es_init_max_streams_uni;
/**
* Idle connection timeout.
*
* This is a transport parameter.
*
* (Note: es_idle_conn_to is not reused because it is in microseconds,
* which, I now realize, was not a good choice. Since it will be
* obsoleted some time after the switchover to IETF QUIC, we do not
* have to keep on using strange units.)
*
* Default value is @ref LSQUIC_DF_IDLE_TIMEOUT.
*
* Maximum value is 600 seconds.
*/
unsigned es_idle_timeout;
/**
* Ping period. If set to non-zero value, the connection will generate and
* send PING frames in the absence of other activity.
*
* By default, the server does not send PINGs and the period is set to zero.
* The client's defaut value is @ref LSQUIC_DF_PING_PERIOD.
*/
unsigned es_ping_period;
/**
* Source Connection ID length. Only applicable to the IETF QUIC
* versions. Valid values are 4 through 18, inclusive.
*
* Default value is @ref LSQUIC_DF_SCID_LEN.
*/
unsigned es_scid_len;
/**
* Source Connection ID issuance rate. Only applicable to the IETF QUIC
* versions. This field is measured in CIDs per minute. Using value 0
* indicates that there is no rate limit for CID issuance.
*
* Default value is @ref LSQUIC_DF_SCID_ISS_RATE.
*/
unsigned es_scid_iss_rate;
/**
* Maximum size of the QPACK dynamic table that the QPACK decoder will
* use.
*
* The default is @ref LSQUIC_DF_QPACK_DEC_MAX_SIZE.
*/
unsigned es_qpack_dec_max_size;
/**
* Maximum number of blocked streams that the QPACK decoder is willing
* to tolerate.
*
* The default is @ref LSQUIC_DF_QPACK_DEC_MAX_BLOCKED.
*/
unsigned es_qpack_dec_max_blocked;
/**
* Maximum size of the dynamic table that the encoder is willing to use.
* The actual size of the dynamic table will not exceed the minimum of
* this value and the value advertized by peer.
*
* The default is @ref LSQUIC_DF_QPACK_ENC_MAX_SIZE.
*/
unsigned es_qpack_enc_max_size;
/**
* Maximum number of blocked streams that the QPACK encoder is willing
* to risk. The actual number of blocked streams will not exceed the
* minimum of this value and the value advertized by peer.
*
* The default is @ref LSQUIC_DF_QPACK_ENC_MAX_BLOCKED.
*/
unsigned es_qpack_enc_max_blocked;
/**
* Enable ECN support.
*
* The default is @ref LSQUIC_DF_ECN
*/
int es_ecn;
/**
* Number of HTTP/3 priorify placeholders.
*
* The default is @ref LSQUIC_DF_H3_PLACEHOLDERS
*/
unsigned es_h3_placeholders;
/**
* Allow peer to migrate connection.
*
* The default is @ref LSQUIC_DF_ALLOW_MIGRATION
*/
int es_allow_migration;
/**
* Congestion control algorithm to use.
*
* 0: Use default (@ref LSQUIC_DF_CC_ALGO)
* 1: Cubic
* 2: BBR
*/
unsigned es_cc_algo;
};
/* Initialize `settings' to default values */
@ -491,11 +781,12 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
struct lsquic_out_spec
{
const unsigned char *buf;
size_t sz;
struct iovec *iov;
size_t iovlen;
const struct sockaddr *local_sa;
const struct sockaddr *dest_sa;
void *peer_ctx;
int ecn; /* Valid values are 0 - 3. See RFC 3168 */
};
/**
@ -511,6 +802,41 @@ typedef int (*lsquic_packets_out_f)(
unsigned n_packets_out
);
/**
* The shared hash interface is used to share data between multiple LSQUIC
* instances.
*/
struct lsquic_shared_hash_if
{
/**
* If you want your item to never expire, set `expiry' to zero.
* Returns 0 on success, -1 on failure.
*
* If inserted successfully, `free()' will be called on `data' and 'key'
* pointer when the element is deleted, whether due to expiration
* or explicit deletion.
*/
int (*shi_insert)(void *shi_ctx, void *key, unsigned key_sz,
void *data, unsigned data_sz, time_t expiry);
/**
* Returns 0 on success, -1 on failure.
*/
int (*shi_delete)(void *shi_ctx, const void *key, unsigned key_sz);
/**
* `data' is pointed to the result and `data_sz' is set to the
* object size. The implementation may choose to copy the object
* into buffer pointed to by `data', so you should have it ready.
*
* @retval 1 found.
* @retval 0 not found.
* @retval -1 error (perhaps not enough room in `data' if copy was
* attempted).
*/
int (*shi_lookup)(void *shi_ctx, const void *key, unsigned key_sz,
void **data, unsigned *data_sz);
};
/**
* The packet out memory interface is used by LSQUIC to get buffers to
* which outgoing packets will be written before they are passed to
@ -539,6 +865,9 @@ struct lsquic_packout_mem_if
char is_ipv6);
};
typedef void (*lsquic_cids_update_f)(void *ctx, void **peer_ctx,
const lsquic_cid_t *cids, unsigned n_cids);
struct stack_st_X509;
/**
@ -554,7 +883,8 @@ enum lsquic_header_status
LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR,
/** Unnecessary request pseudo-header present in the response */
LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR,
LSQUIC_HDR_ERR_BAD_REQ_HEADER = LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR,
/** Prohibited header in request */
LSQUIC_HDR_ERR_BAD_REQ_HEADER,
/** Not all response pseudo-headers are present */
LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR,
/** Unnecessary response pseudo-header present in the response. */
@ -590,9 +920,12 @@ struct lsquic_hset_if
* `hdr_set' is the header set object returned by
* @ref hsi_create_header_set().
*
* `name_idx' is set to the index in the HPACK static table whose entry's
* name element matches `name'. If there is no such match, `name_idx' is
* set to zero.
* `name_idx' is set to the index in either the HPACK or QPACK static table
* whose entry's name element matches `name'. The values are as follows:
* - if there is no such match, `name_idx' is set to zero;
* - if HPACK is used, the value is between 1 and 61; and
* - if QPACK is used, the value is 62+ (subtract 62 to get the QPACK
* static table index).
*
* If `name' is NULL, this means that no more header are going to be
* added to the set.
@ -608,6 +941,26 @@ struct lsquic_hset_if
void (*hsi_discard_header_set)(void *hdr_set);
};
/**
* SSL keylog interface.
*/
struct lsquic_keylog_if
{
/** Return keylog handle or NULL if no key logging is desired */
void * (*kli_open) (void *keylog_ctx, lsquic_conn_t *);
/**
* Log line. The first argument is the pointer returned by
* @ref kli_open.
*/
void (*kli_log_line) (void *handle, const char *line);
/**
* Close handle.
*/
void (*kli_close) (void *handle);
};
/* TODO: describe this important data structure */
typedef struct lsquic_engine_api
{
@ -616,11 +969,27 @@ typedef struct lsquic_engine_api
void *ea_stream_if_ctx;
lsquic_packets_out_f ea_packets_out;
void *ea_packets_out_ctx;
lsquic_lookup_cert_f ea_lookup_cert;
void *ea_cert_lu_ctx;
struct ssl_ctx_st * (*ea_get_ssl_ctx)(void *peer_ctx);
/**
* Shared hash interface is optional. If set to zero, performance of
* multiple LSQUIC instances will be degraded.
*/
const struct lsquic_shared_hash_if *ea_shi;
void *ea_shi_ctx;
/**
* Memory interface is optional.
*/
const struct lsquic_packout_mem_if *ea_pmi;
void *ea_pmi_ctx;
/**
* Optional interface to report new and old source connection IDs.
*/
lsquic_cids_update_f ea_new_scids;
lsquic_cids_update_f ea_live_scids;
lsquic_cids_update_f ea_old_scids;
void *ea_cids_update_ctx;
/**
* Function to verify server certificate. The chain contains at least
* one element. The first element in the chain is the server
@ -650,6 +1019,12 @@ typedef struct lsquic_engine_api
*/
void /* FILE, really */ *ea_stats_fh;
#endif
/**
* Optional SSL key logging interface.
*/
const struct lsquic_keylog_if *ea_keylog_if;
void *ea_keylog_ctx;
} lsquic_engine_api_t;
/**
@ -672,7 +1047,9 @@ lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa,
const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_packet_size,
const unsigned char *zero_rtt, size_t zero_rtt_len);
const unsigned char *zero_rtt, size_t zero_rtt_len,
/** Resumption token: optional */
const unsigned char *token, size_t token_sz);
/**
* Pass incoming packet to the QUIC engine. This function can be called
@ -681,6 +1058,10 @@ lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa,
*
* @retval 0 Packet was processed by a real connection.
*
* @retval 1 Packet was handled successfully, but not by a connection.
* This may happen with version negotiation and public reset
* packets as well as some packets that may be ignored.
*
* @retval -1 Some error occurred. Possible reasons are invalid packet
* size or failure to allocate memory.
*/
@ -688,7 +1069,7 @@ int
lsquic_engine_packet_in (lsquic_engine_t *,
const unsigned char *packet_in_data, size_t packet_in_size,
const struct sockaddr *sa_local, const struct sockaddr *sa_peer,
void *peer_ctx);
void *peer_ctx, int ecn);
/**
* Process tickable connections. This function must be called often enough so
@ -722,7 +1103,8 @@ lsquic_engine_destroy (lsquic_engine_t *);
unsigned
lsquic_conn_n_avail_streams (const lsquic_conn_t *);
void lsquic_conn_make_stream(lsquic_conn_t *);
void
lsquic_conn_make_stream (lsquic_conn_t *);
/** Return number of delayed streams currently pending */
unsigned
@ -735,21 +1117,48 @@ lsquic_conn_cancel_pending_streams (lsquic_conn_t *, unsigned n);
/**
* Mark connection as going away: send GOAWAY frame and do not accept
* any more incoming streams, nor generate streams of our own.
*
* In the server mode, of course, we can call this function just fine in both
* Google and IETF QUIC.
*
* In client mode, calling this function in for an IETF QUIC connection does
* not do anything, as the client MUST NOT send GOAWAY frames.
* See [draft-ietf-quic-http-17] Section 4.2.7.
*/
void
lsquic_conn_going_away(lsquic_conn_t *conn);
lsquic_conn_going_away (lsquic_conn_t *);
/**
* This forces connection close. on_conn_closed and on_close callbacks
* will be called.
*/
void lsquic_conn_close(lsquic_conn_t *conn);
void
lsquic_conn_close (lsquic_conn_t *);
int lsquic_stream_wantread(lsquic_stream_t *s, int is_want);
ssize_t lsquic_stream_read(lsquic_stream_t *s, void *buf, size_t len);
ssize_t lsquic_stream_readv(lsquic_stream_t *s, const struct iovec *,
int iovcnt);
/**
* This function allows user-supplied callback to read the stream contents.
* It is meant to be used for zero-copy stream processing.
*/
ssize_t
lsquic_stream_readf (lsquic_stream_t *s,
/**
* The callback takes four parameters:
* - Pointer to user-supplied context;
* - Pointer to the data;
* - Data size (can be zero); and
* - Indicator whether the FIN follows the data.
*
* The callback returns number of bytes processed. If this number is zero
* or is smaller than `len', reading from stream stops.
*/
size_t (*readf)(void *ctx, const unsigned char *buf, size_t len, int fin),
void *ctx);
int lsquic_stream_wantwrite(lsquic_stream_t *s, int is_want);
/**
@ -838,7 +1247,33 @@ int lsquic_stream_send_headers(lsquic_stream_t *s,
void *
lsquic_stream_get_hset (lsquic_stream_t *);
int lsquic_conn_is_push_enabled(lsquic_conn_t *c);
/**
* A server may push a stream. This call creates a new stream in reference
* to stream `s'. It will behave as if the client made a request: it will
* trigger on_new_stream() event and it can be used as a regular client-
* initiated stream.
*
* If `hdr_set' is not set, it is generated by using `ea_hsi_if' callbacks.
* In either case, the header set object belongs to the connection. The
* user is not to free this object until (@ref hsi_discard_header_set) is
* called.
*
* @retval 0 Stream pushed successfully.
* @retval 1 Stream push failed because it is disabled or because we hit
* stream limit or connection is going away.
* @retval -1 Stream push failed because of an internal error.
*/
int
lsquic_conn_push_stream (lsquic_conn_t *c, void *hdr_set, lsquic_stream_t *s,
const struct iovec* url, const struct iovec* authority,
const lsquic_http_headers_t *headers);
/**
* Only makes sense in server mode: the client cannot push a stream and this
* function always returns false in client mode.
*/
int
lsquic_conn_is_push_enabled (lsquic_conn_t *);
/** Possible values for how are 0, 1, and 2. See shutdown(2). */
int lsquic_stream_shutdown(lsquic_stream_t *s, int how);
@ -857,16 +1292,8 @@ int lsquic_stream_close(lsquic_stream_t *s);
struct stack_st_X509 *
lsquic_conn_get_server_cert_chain (lsquic_conn_t *);
/**
* Get server config zero_rtt from the encryption session.
* Returns the number of bytes written to the zero_rtt.
*/
ssize_t
lsquic_conn_get_zero_rtt(const lsquic_conn_t *,
unsigned char *zero_rtt, size_t zero_rtt_len);
/** Returns ID of the stream */
uint32_t
lsquic_stream_id_t
lsquic_stream_id (const lsquic_stream_t *s);
/**
@ -880,6 +1307,13 @@ lsquic_stream_get_ctx (const lsquic_stream_t *s);
int
lsquic_stream_is_pushed (const lsquic_stream_t *s);
/**
* Returns true if this stream was rejected, false otherwise. Use this as
* an aid to distinguish between errors.
*/
int
lsquic_stream_is_rejected (const lsquic_stream_t *s);
/**
* Refuse pushed stream. Call it from @ref on_new_stream.
*
@ -902,8 +1336,8 @@ lsquic_stream_refuse_push (lsquic_stream_t *s);
* @retval -1 This is not a pushed stream.
*/
int
lsquic_stream_push_info (const lsquic_stream_t *, uint32_t *ref_stream_id,
void **hdr_set);
lsquic_stream_push_info (const lsquic_stream_t *,
lsquic_stream_id_t *ref_stream_id, void **hdr_set);
/** Return current priority of the stream */
unsigned lsquic_stream_priority (const lsquic_stream_t *s);
@ -923,21 +1357,22 @@ int lsquic_stream_set_priority (lsquic_stream_t *s, unsigned priority);
lsquic_conn_t * lsquic_stream_conn(const lsquic_stream_t *s);
lsquic_stream_t *
lsquic_conn_get_stream_by_id (lsquic_conn_t *c, uint32_t stream_id);
lsquic_conn_get_stream_by_id (lsquic_conn_t *c, lsquic_stream_id_t stream_id);
/** Get connection ID */
lsquic_cid_t
const lsquic_cid_t *
lsquic_conn_id (const lsquic_conn_t *c);
/** Get pointer to the engine */
lsquic_engine_t *
lsquic_conn_get_engine (lsquic_conn_t *c);
int lsquic_conn_get_sockaddr(const lsquic_conn_t *c,
int
lsquic_conn_get_sockaddr(lsquic_conn_t *c,
const struct sockaddr **local, const struct sockaddr **peer);
struct lsquic_logger_if {
int (*vprintf)(void *logger_ctx, const char *fmt, va_list args);
int (*log_buf)(void *logger_ctx, const char *buf, size_t len);
};
/**
@ -1058,31 +1493,98 @@ lsquic_global_cleanup (void);
enum lsquic_version
lsquic_conn_quic_version (const lsquic_conn_t *c);
/* Return keysize or -1 on error */
int
lsquic_conn_crypto_keysize (const lsquic_conn_t *c);
/* Return algorithm keysize or -1 on error */
int
lsquic_conn_crypto_alg_keysize (const lsquic_conn_t *c);
enum lsquic_crypto_ver
{
LSQ_CRY_QUIC,
LSQ_CRY_TLSv13,
};
enum lsquic_crypto_ver
lsquic_conn_crypto_ver (const lsquic_conn_t *c);
/* Return cipher or NULL on error */
const char *
lsquic_conn_crypto_cipher (const lsquic_conn_t *c);
/** Translate string QUIC version to LSQUIC QUIC version representation */
enum lsquic_version
lsquic_str2ver (const char *str, size_t len);
/**
* This function closes all mini connections and marks all full connection
* as going away. In server mode, this also causes the engine to stop
* creating new connections.
*/
void
lsquic_engine_cooldown (lsquic_engine_t *);
struct ssl_st *
lsquic_hsk_getssl(lsquic_conn_t *conn);
/**
* Get user-supplied context associated with the connection.
*/
lsquic_conn_ctx_t *
lsquic_conn_get_ctx (const lsquic_conn_t *c);
lsquic_conn_get_ctx (const lsquic_conn_t *);
/**
* Set user-supplied context associated with the connection.
*/
void lsquic_conn_set_ctx (lsquic_conn_t *c, lsquic_conn_ctx_t *h);
void
lsquic_conn_set_ctx (lsquic_conn_t *, lsquic_conn_ctx_t *);
/**
* Get peer context associated with the connection.
*/
void *lsquic_conn_get_peer_ctx( const lsquic_conn_t *lconn);
void *
lsquic_conn_get_peer_ctx (lsquic_conn_t *, const struct sockaddr *local_sa);
/**
* Abort connection.
*/
void
lsquic_conn_abort (lsquic_conn_t *c);
lsquic_conn_abort (lsquic_conn_t *);
/**
* Helper function: convert list of versions as specified in the argument
* bitmask to string that can be included as argument to "v=" part of the
* Alt-Svc header.
*
* For example (1<<LSQVER_037)|(1<<LSQVER_038) => "37,38"
*
* This is only applicable to Google QUIC versions.
*/
const char *
lsquic_get_alt_svc_versions (unsigned versions);
/**
* Return a NULL-terminated list of HTTP/3 ALPNs, e.g "h3-17", "h3-18", "h3".
*/
const char *const *
lsquic_get_h3_alpns (unsigned versions);
/**
* Returns true if provided buffer could be a valid handshake-stage packet,
* false otherwise. Do not call this function if a connection has already
* been established: it will return incorrect result.
*/
int
lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, size_t);
/**
* Parse cid from packet stored in `buf' and store it to `cid'. Returns 0
* on success and -1 on failure.
*/
int
lsquic_cid_from_packet (const unsigned char *, size_t bufsz, lsquic_cid_t *cid);
/**
* Returns true if there are connections to be processed, false otherwise.

View File

@ -9,10 +9,30 @@
#include <stdint.h>
#define MAX_CID_LEN 20
#define GQUIC_CID_LEN 8
/**
* Connection ID
*/
typedef uint64_t lsquic_cid_t;
typedef struct lsquic_cid
{
uint_fast8_t len;
union {
uint8_t buf[MAX_CID_LEN];
uint64_t id;
} u_cid;
#define idbuf u_cid.buf
}
lsquic_cid_t;
#define LSQUIC_CIDS_EQ(a, b) ((a)->len == 8 ? \
(b)->len == 8 && (a)->u_cid.id == (b)->u_cid.id : \
(a)->len == (b)->len && 0 == memcmp((a)->idbuf, (b)->idbuf, (a)->len))
/** Stream ID */
typedef uint64_t lsquic_stream_id_t;
/** LSQUIC engine */
typedef struct lsquic_engine lsquic_engine_t;

View File

@ -0,0 +1,73 @@
commit 46f967bfe44a80bb4bc0e7e9d4b03de3f91d03fb
Author: Dmitri Tikhonov <dtikhonov@litespeedtech.com>
Date: Fri Feb 22 11:51:21 2019 -0500
Add support for QUIC's use of max_early_data_size
The server MUST set this value to 0xFFFFFFFF in NewSessionTicket,
while the client should be able to examine it in order to verify
this requirement.
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 59b9eac..2e6cefb 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1744,6 +1744,11 @@ OPENSSL_EXPORT int SSL_SESSION_set_ticket(SSL_SESSION *session,
OPENSSL_EXPORT uint32_t
SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
+// SSL_SESSION_get_max_early_data_size returns ticket max early data size of
+// |session| in bytes or zero if none was set.
+OPENSSL_EXPORT uint32_t
+SSL_SESSION_get_max_early_data_size(const SSL_SESSION *session);
+
// SSL_SESSION_get0_cipher returns the cipher negotiated by the connection which
// established |session|.
//
diff --git a/ssl/internal.h b/ssl/internal.h
index 1116bad..98dcfa3 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -2506,6 +2506,10 @@ struct SSL_CONFIG {
// kMaxEarlyDataSkipped in tls_record.c, which is measured in ciphertext.
static const size_t kMaxEarlyDataAccepted = 14336;
+// kQUICMaxEarlyData is the value to which the max_early_data_size field
+// in a NewSessionTicket must be set when sent by a QUIC server.
+static const uint32_t kQUICMaxEarlyData = 0xffffffffu;
+
UniquePtr<CERT> ssl_cert_dup(CERT *cert);
void ssl_cert_clear_certs(CERT *cert);
bool ssl_set_cert(CERT *cert, UniquePtr<CRYPTO_BUFFER> buffer);
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index 927dd1b..cd5b5bb 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -1025,6 +1025,10 @@ uint32_t SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session) {
return session->ticket_lifetime_hint;
}
+uint32_t SSL_SESSION_get_max_early_data_size(const SSL_SESSION *session) {
+ return session->ticket_max_early_data;
+}
+
const SSL_CIPHER *SSL_SESSION_get0_cipher(const SSL_SESSION *session) {
return session->cipher;
}
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index 562fecb..4edd0ef 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -179,7 +179,11 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
}
session->ticket_age_add_valid = true;
if (ssl->enable_early_data) {
- session->ticket_max_early_data = kMaxEarlyDataAccepted;
+ if (ssl->quic_method == nullptr) {
+ session->ticket_max_early_data = kMaxEarlyDataAccepted;
+ } else {
+ session->ticket_max_early_data = kQUICMaxEarlyData;
+ }
}
static_assert(kNumTickets < 256, "Too many tickets");

View File

@ -1,67 +1,109 @@
# Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE.
SET(lsquic_STAT_SRCS
ls-qpack/lsqpack.c
lsquic_alarmset.c
lsquic_conn.c
lsquic_full_conn.c
lsquic_arr.c
lsquic_attq.c
lsquic_bbr.c
lsquic_buf.c
lsquic_bw_sampler.c
lsquic_cfcw.c
lsquic_chsk_stream.c
lsquic_engine.c
lsquic_parse_gquic_common.c
lsquic_parse_iquic_common.c
lsquic_parse_common.c
lsquic_parse_gquic_le.c
lsquic_parse_gquic_be.c
lsquic_packet_in.c
lsquic_packet_out.c
lsquic_conn.c
lsquic_crt_compress.c
lsquic_crypto.c
lsquic_cubic.c
lsquic_di_error.c
lsquic_di_hash.c
lsquic_di_nocopy.c
lsquic_enc_sess_common.c
lsquic_enc_sess_ietf.c
lsquic_eng_hist.c
lsquic_engine.c
lsquic_ev_log.c
lsquic_frab_list.c
lsquic_frame_common.c
lsquic_frame_reader.c
lsquic_frame_writer.c
lsquic_full_conn.c
lsquic_full_conn_ietf.c
lsquic_global.c
lsquic_h3_prio.c
lsquic_handshake.c
lsquic_hash.c
lsquic_hcsi_reader.c
lsquic_hcso_writer.c
lsquic_headers_stream.c
lsquic_hkdf.c
lsquic_hq.c
lsquic_hspack_valid.c
lsquic_http1x_if.c
lsquic_logger.c
lsquic_malo.c
lsquic_min_heap.c
lsquic_mini_conn.c
lsquic_mini_conn_ietf.c
lsquic_minmax.c
lsquic_mm.c
lsquic_pacer.c
lsquic_packet_common.c
lsquic_packet_gquic.c
lsquic_packet_in.c
lsquic_packet_out.c
lsquic_packints.c
lsquic_parse_Q046.c
lsquic_parse_common.c
lsquic_parse_gquic_be.c
lsquic_parse_gquic_common.c
lsquic_parse_ietf_v1.c
lsquic_parse_iquic_common.c
lsquic_pr_queue.c
lsquic_purga.c
lsquic_qdec_hdl.c
lsquic_qenc_hdl.c
lsquic_qlog.c
lsquic_rechist.c
lsquic_rtt.c
lsquic_send_ctl.c
lsquic_senhist.c
lsquic_cfcw.c
lsquic_sfcw.c
lsquic_stream.c
lsquic_util.c
lsquic_cubic.c
lsquic_set.c
lsquic_headers_stream.c
lsquic_frame_reader.c
lsquic_frame_writer.c
lsquic_crt_compress.c
lsquic_conn_hash.c
lsquic_eng_hist.c
lsquic_sfcw.c
lsquic_shsk_stream.c
lsquic_spi.c
lsquic_di_nocopy.c
lsquic_di_hash.c
lsquic_di_error.c
lsquic_global.c
lsquic_packet_common.c
lsquic_qlog.c
lsquic_ev_log.c
lsquic_frame_common.c
lsquic_packints.c
lsquic_version.c
lsquic_pacer.c
lsquic_attq.c
lsquic_stock_shi.c
lsquic_str.c
lsquic_arr.c
lsquic_hash.c
lsquic_xxhash.c
lsquic_buf.c
lsquic_min_heap.c
../lshpack/lshpack.c
lsquic_parse_Q044.c
lsquic_parse_Q046.c
lsquic_http1x_if.c
lsquic_stream.c
lsquic_tokgen.c
lsquic_trans_params.c
lsquic_util.c
lsquic_varint.c
lsquic_version.c
)
set_source_files_properties(ls-qpack/lsqpack.c PROPERTIES COMPILE_FLAGS -Wno-uninitialized)
include_directories(ls-qpack)
IF(PROJECT_NAME STREQUAL "openlitespeed")
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/spdy)
ELSE()
SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS}
lsquic_xxhash.c
../lshpack/lshpack.c
)
ENDIF()
ADD_CUSTOM_COMMAND(
OUTPUT ${PROJECT_SOURCE_DIR}/src/liblsquic/lsquic_versions_to_string.c
COMMAND ${PROJECT_SOURCE_DIR}/src/liblsquic/gen-verstrs
ARGS ${PROJECT_SOURCE_DIR}/include/lsquic.h ${PROJECT_SOURCE_DIR}/src/liblsquic/lsquic_versions_to_string.c
DEPENDS ./gen-verstrs ${PROJECT_SOURCE_DIR}/include/lsquic.h
)
SET(lsquic_STAT_SRCS ${lsquic_STAT_SRCS} lsquic_versions_to_string.c)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DXXH_HEADER_NAME=\\\"lsquic_xxhash.h\\\"")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_ENC_LOGGER_HEADER=\\\"lsquic_qpack_enc_logger.h\\\"")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_DEC_LOGGER_HEADER=\\\"lsquic_qpack_dec_logger.h\\\"")
add_library(lsquic STATIC ${lsquic_STAT_SRCS} )
link_directories(${PROJECT_SOURCE_DIR}/ssl/ /usr/local/lib)

122
src/liblsquic/gen-verstrs Executable file
View File

@ -0,0 +1,122 @@
#!/usr/bin/env perl
#
# Generate C file that contains version strings
($header # This is lsquic.h that contains version enum which we parse
, $outfile # This is destination C file
) = @ARGV;
open HEADER, $header
or die "cannot open $header for reading: $!";
open OUT, ">$outfile"
or die "cannot open $outfile for writing: $!";
while (<HEADER>) {
if (/^enum lsquic_version$/ .. /^}/) {
if (/^\s*(LSQVER_0*(\d+)),\s*$/ && $1 ne 'LSQVER_098') {
push @enums, $1;
push @versions, $2;
}
if (/^\s*LSQVER_ID(\d+)\b/) {
push @draft_versions, $1;
}
}
}
close HEADER;
$timestamp = localtime;
print OUT <<C_CODE;
/*
* Auto-generated by $0 on $timestamp
*/
#include <assert.h>
#include "lsquic.h"
struct lsquic_engine;
static const char *const versions_to_string[ 1 << N_LSQVER ] = {
C_CODE
$max_mask = (1 << @versions) - 1;
for ($mask = 0; $mask <= $max_mask; ++$mask) {
my @indexes;
for ($i = 0; $i < @versions; ++$i) {
if ($mask & (1 << $i)) {
push @indexes, $i;
}
}
print OUT " [",
join('|', map "(1<<$_)", @enums[@indexes]) || 0,
"] = \"",
join(',', @versions[@indexes]),
"\",\n";
}
$enums = join '|', map "(1<<$_)", sort @enums;
print OUT <<"C_CODE";
};
const char *
lsquic_get_alt_svc_versions (unsigned versions)
{
/* Limit to versions in versions_to_string: */
versions &= ($enums);
return versions_to_string[ versions ];
}
C_CODE
$draft_version_count = @draft_versions;
$draft_version_count_and_null = $draft_version_count + 1;
print OUT <<"C_CODE";
static const struct {
unsigned versions;
const char *h3_alpns[$draft_version_count_and_null];
} vers_2_h3_alnps[] = {
C_CODE
for ($i = 0; $i < (1 << @draft_versions); ++$i)
{
my @vers;
for ($j = 0; $j < @draft_versions; ++$j)
{
if ($i & (1 << $j))
{
push @vers, $draft_versions[$j];
}
}
print OUT " {", join("|", 0, map "(1<<LSQVER_ID$_)", @vers), ", ",
"{ ", join(", ", map "\"h3-$_\"", @vers), @vers ? ", " : "", "NULL }},\n";
}
$draft_versions = join("|", map "(1<<LSQVER_ID$_)", @draft_versions);
print OUT <<"C_CODE";
};
const char *const *
lsquic_get_h3_alpns (unsigned versions)
{
unsigned i;
versions &= $draft_versions;
for (i = 0; i < sizeof(vers_2_h3_alnps) / sizeof(vers_2_h3_alnps[0]); ++i)
if (versions == vers_2_h3_alnps[i].versions)
return vers_2_h3_alnps[i].h3_alpns;
assert(0);
return vers_2_h3_alnps[0].h3_alpns;
}
C_CODE
close OUT;

@ -0,0 +1 @@
Subproject commit 9a7952e41acad045ea7a6d4935b976872d73c111

View File

@ -7,17 +7,18 @@
#include <string.h>
#include "lsquic_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_alarmset.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ALARMSET
#define LSQUIC_LOG_CONN_ID alset->as_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(alset->as_conn)
#include "lsquic_logger.h"
void
lsquic_alarmset_init (lsquic_alarmset_t *alset, lsquic_cid_t cid)
lsquic_alarmset_init (lsquic_alarmset_t *alset, const struct lsquic_conn *conn)
{
alset->as_cid = cid;
alset->as_conn = conn;
alset->as_armed_set = 0;
}
@ -31,6 +32,24 @@ lsquic_alarmset_init_alarm (lsquic_alarmset_t *alset, enum alarm_id al_id,
}
static const char *const lsquic_alid2str[] =
{
[AL_HANDSHAKE] = "HANDSHAKE",
[AL_RETX_INIT] = "RETX_INIT",
[AL_RETX_HSK] = "RETX_HSK",
[AL_RETX_APP] = "RETX_APP",
[AL_PING] = "PING",
[AL_IDLE] = "IDLE",
[AL_ACK_INIT] = "ACK_INIT",
[AL_ACK_HSK] = "ACK_HSK",
[AL_ACK_APP] = "ACK_APP",
[AL_RET_CIDS] = "RET_CIDS",
[AL_CID_THROT] = "CID_THROT",
[AL_PATH_CHAL_0] = "PATH_CHAL_0",
[AL_PATH_CHAL_1] = "PATH_CHAL_1",
};
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *alset, lsquic_time_t now)
{
@ -45,8 +64,8 @@ lsquic_alarmset_ring_expired (lsquic_alarmset_t *alset, lsquic_time_t now)
if (alset->as_expiry[al_id] < now)
{
alset->as_armed_set &= ~(1 << al_id);
LSQ_INFO("ring expired alarm %d", al_id);
alset->as_alarms[al_id].callback(
LSQ_INFO("ring expired %s alarm", lsquic_alid2str[al_id]);
alset->as_alarms[al_id].callback(al_id,
alset->as_alarms[al_id].cb_ctx,
alset->as_expiry[al_id], now);
}

View File

@ -8,7 +8,10 @@
#include "lsquic_int_types.h"
typedef void (*lsquic_alarm_cb_f)(void *cb_ctx,
enum alarm_id;
struct lsquic_conn;
typedef void (*lsquic_alarm_cb_f)(enum alarm_id, void *cb_ctx,
lsquic_time_t expiry, lsquic_time_t now);
typedef struct lsquic_alarm {
@ -19,33 +22,51 @@ typedef struct lsquic_alarm {
enum alarm_id {
AL_HANDSHAKE,
AL_RETX,
AL_ACK,
AL_RETX_INIT,
AL_RETX_HSK = AL_RETX_INIT + PNS_HSK,
AL_RETX_APP = AL_RETX_INIT + PNS_APP,
AL_PING,
AL_IDLE,
AL_ACK_INIT,
AL_ACK_HSK = AL_ACK_INIT + PNS_HSK,
AL_ACK_APP = AL_ACK_INIT + PNS_APP,
AL_RET_CIDS,
AL_CID_THROT,
AL_PATH_CHAL,
AL_PATH_CHAL_0 = AL_PATH_CHAL,
AL_PATH_CHAL_1,
MAX_LSQUIC_ALARMS
};
enum alarm_id_bit {
ALBIT_HANDSHAKE = 1 << AL_HANDSHAKE,
ALBIT_RETX = 1 << AL_RETX,
ALBIT_ACK = 1 << AL_ACK,
ALBIT_RETX_INIT = 1 << AL_RETX_INIT,
ALBIT_RETX_HSK = 1 << AL_RETX_HSK,
ALBIT_RETX_APP = 1 << AL_RETX_APP,
ALBIT_ACK_APP = 1 << AL_ACK_APP,
ALBIT_ACK_INIT = 1 << AL_ACK_INIT,
ALBIT_ACK_HSK = 1 << AL_ACK_HSK,
ALBIT_PING = 1 << AL_PING,
ALBIT_IDLE = 1 << AL_IDLE,
ALBIT_RET_CIDS = 1 << AL_RET_CIDS,
ALBIT_CID_THROT = 1 << AL_CID_THROT,
ALBIT_PATH_CHAL = 1 << AL_PATH_CHAL,
ALBIT_PATH_CHAL_0 = 1 << AL_PATH_CHAL_0,
ALBIT_PATH_CHAL_1 = 1 << AL_PATH_CHAL_1,
};
typedef struct lsquic_alarmset {
enum alarm_id_bit as_armed_set;
lsquic_time_t as_expiry[MAX_LSQUIC_ALARMS];
lsquic_cid_t as_cid; /* Used for logging */
const struct lsquic_conn *as_conn; /* Used for logging */
struct lsquic_alarm as_alarms[MAX_LSQUIC_ALARMS];
} lsquic_alarmset_t;
void
lsquic_alarmset_init (lsquic_alarmset_t *, lsquic_cid_t);
lsquic_alarmset_init (lsquic_alarmset_t *, const struct lsquic_conn *);
void
lsquic_alarmset_init_alarm (lsquic_alarmset_t *, enum alarm_id,
@ -63,6 +84,9 @@ lsquic_alarmset_init_alarm (lsquic_alarmset_t *, enum alarm_id,
#define lsquic_alarmset_is_set(alarmset, al_id) \
((alarmset)->as_armed_set & (1 << (al_id)))
#define lsquic_alarmset_are_set(alarmset, flags) \
((alarmset)->as_armed_set & (flags))
/* Timers "fire," alarms "ring." */
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *, lsquic_time_t now);

View File

@ -13,12 +13,14 @@
#ifdef WIN32
#include <vc_compat.h>
#endif
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_attq.h"
#include "lsquic_malo.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
@ -212,7 +214,6 @@ attq_remove (struct attq *q, struct lsquic_conn *conn)
assert(conn->cn_attq_elem == el);
conn->cn_attq_elem = NULL;
lsquic_malo_put(el);
q->aq_heap[ idx ] = q->aq_heap[ --q->aq_nelem ];
q->aq_heap[ idx ]->ae_heap_idx = idx;
@ -229,6 +230,7 @@ attq_remove (struct attq *q, struct lsquic_conn *conn)
}
else if (q->aq_nelem > 1 && idx < q->aq_nelem)
attq_heapify(q, idx);
lsquic_malo_put(el);
attq_verify(q);
}

1050
src/liblsquic/lsquic_bbr.c Normal file

File diff suppressed because it is too large Load Diff

210
src/liblsquic/lsquic_bbr.h Normal file
View File

@ -0,0 +1,210 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_BBR_H
#define LSQUIC_BBR_H
/* Our BBR implementation is copied from Chromium with some modifications.
* Besides the obvious translation from C++ to C, differences are:
*
* 1. Instead of OnCongestionEvent(), the ACK information is processed at the
* same time as the ACK itself using cci_begin_ack(), cci_ack(), and
* cci_end_ack() methods. This is done to fit with the flow in
* lsquic_send_ctl_got_ack().
*
* 2. The bandwidth sampler does not use a hash. Instead, the sample
* information is attached directly to the packet via po_bwp_state.
*
* In this file and in lsquic_bbr.c, C++-style comments are those copied
* verbatim from Chromium. C-style comments are ours.
*
* Code is based on bbr_sender.cc 1a578a76c16abc942205a1a80584a288c262d03a in
* the "quiche" repository. (Not to be confused with Cloudflare's "quiche".)
*
* The BBR I-D is here:
* https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00
*
* As for quiche, see
* http://www.bernstein-plus-sons.com/RPDEQ.html
*
* Chromium copyright notice follows.
*/
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE.chrome file.
struct lsquic_bbr
{
struct lsquic_conn *bbr_conn;
enum bbr_mode
{
BBR_MODE_STARTUP,
BBR_MODE_DRAIN,
BBR_MODE_PROBE_BW,
BBR_MODE_PROBE_RTT,
} bbr_mode;
enum
{
BBR_RS_NOT_IN_RECOVERY,
BBR_RS_CONSERVATION,
BBR_RS_GROWTH,
} bbr_recovery_state;
enum
{
BBR_FLAG_IN_ACK = 1 << 0, /* cci_begin_ack() has been called */
BBR_FLAG_LAST_SAMPLE_APP_LIMITED = 1 << 1,
BBR_FLAG_HAS_NON_APP_LIMITED = 1 << 2,
BBR_FLAG_APP_LIMITED_SINCE_LAST_PROBE_RTT
= 1 << 3,
BBR_FLAG_PROBE_RTT_DISABLED_IF_APP_LIMITED
= 1 << 4,
BBR_FLAG_PROBE_RTT_SKIPPED_IF_SIMILAR_RTT
= 1 << 5,
BBR_FLAG_EXIT_STARTUP_ON_LOSS = 1 << 6,
BBR_FLAG_IS_AT_FULL_BANDWIDTH = 1 << 7,
BBR_FLAG_EXITING_QUIESCENCE = 1 << 8,
BBR_FLAG_PROBE_RTT_ROUND_PASSED = 1 << 9,
BBR_FLAG_FLEXIBLE_APP_LIMITED = 1 << 10,
// If true, will not exit low gain mode until bytes_in_flight drops
// below BDP or it's time for high gain mode.
BBR_FLAG_DRAIN_TO_TARGET = 1 << 11,
// When true, expire the windowed ack aggregation values in STARTUP
// when bandwidth increases more than 25%.
BBR_FLAG_EXPIRE_ACK_AGG_IN_STARTUP
= 1 << 12,
// If true, use a CWND of 0.75*BDP during probe_rtt instead of 4
// packets.
BBR_FLAG_PROBE_RTT_BASED_ON_BDP = 1 << 13,
// When true, pace at 1.5x and disable packet conservation in STARTUP.
BBR_FLAG_SLOWER_STARTUP = 1 << 14,
// When true, add the most recent ack aggregation measurement during STARTUP.
BBR_FLAG_ENABLE_ACK_AGG_IN_STARTUP
= 1 << 15,
// When true, disables packet conservation in STARTUP.
BBR_FLAG_RATE_BASED_STARTUP = 1 << 16,
} bbr_flags;
// Number of round-trips in PROBE_BW mode, used for determining the current
// pacing gain cycle.
unsigned bbr_cycle_current_offset;
const struct lsquic_rtt_stats
*bbr_rtt_stats;
struct bw_sampler bbr_bw_sampler;
/*
" BBR.BtlBwFilter: The max filter used to estimate BBR.BtlBw.
*/
struct minmax bbr_max_bandwidth;
// Tracks the maximum number of bytes acked faster than the sending rate.
struct minmax bbr_max_ack_height;
// The initial value of the bbr_cwnd.
uint64_t bbr_init_cwnd;
// The smallest value the bbr_cwnd can achieve.
uint64_t bbr_min_cwnd;
// The largest value the bbr_cwnd can achieve.
uint64_t bbr_max_cwnd;
// The maximum allowed number of bytes in flight.
uint64_t bbr_cwnd;
// The time this aggregation started and the number of bytes acked during it.
lsquic_time_t bbr_aggregation_epoch_start_time;
uint64_t bbr_aggregation_epoch_bytes;
lsquic_packno_t bbr_last_sent_packno;
lsquic_packno_t bbr_current_round_trip_end;
// Receiving acknowledgement of a packet after |bbr_end_recovery_at| will
// cause BBR to exit the recovery mode. A value above zero indicates at
// least one loss has been detected, so it must not be set back to zero.
lsquic_packno_t bbr_end_recovery_at;
/*
" BBR.round_count: Count of packet-timed round trips.
*/
uint64_t bbr_round_count;
/* Not documented in the draft: */
uint64_t bbr_full_bw;
/* Not documented in the draft: */
uint64_t bbr_full_bw_count;
/*
" BBR.pacing_rate: The current pacing rate for a BBR flow, which
" controls inter-packet spacing.
*/
struct bandwidth bbr_pacing_rate;
// Sum of bytes lost in STARTUP.
uint64_t bbr_startup_bytes_lost;
/*
" BBR.pacing_gain: The dynamic gain factor used to scale BBR.BtlBw to
" produce BBR.pacing_rate.
*/
float bbr_pacing_gain;
// The pacing gain applied during the STARTUP phase.
float bbr_high_gain;
// The CWND gain applied during the STARTUP phase.
float bbr_high_cwnd_gain;
// The pacing gain applied during the DRAIN phase.
float bbr_drain_gain;
// The number of RTTs to stay in STARTUP mode. Defaults to 3.
unsigned bbr_num_startup_rtts;
// Number of rounds during which there was no significant bandwidth
// increase.
unsigned bbr_round_wo_bw_gain;
/*
" BBR.cwnd_gain: The dynamic gain factor used to scale the estimated
" BDP to produce a congestion window (cwnd).
*/
float bbr_cwnd_gain;
// The bandwidth compared to which the increase is measured.
struct bandwidth bbr_bw_at_last_round;
// The time at which the last pacing gain cycle was started.
lsquic_time_t bbr_last_cycle_start;
// Time at which PROBE_RTT has to be exited. Setting it to zero indicates
// that the time is yet unknown as the number of packets in flight has not
// reached the required value.
lsquic_time_t bbr_exit_probe_rtt_at;
lsquic_time_t bbr_min_rtt_since_last_probe;
lsquic_time_t bbr_min_rtt;
lsquic_time_t bbr_min_rtt_timestamp;
// A window used to limit the number of bytes in flight during loss recovery
uint64_t bbr_recovery_window;
/* Accumulate information from a single ACK. Gets processed when
* cci_end_ack() is called.
*/
struct
{
TAILQ_HEAD(, bw_sample) samples;
lsquic_time_t ack_time;
lsquic_packno_t max_packno;
uint64_t acked_bytes;
uint64_t lost_bytes;
uint64_t total_bytes_acked_before;
uint64_t in_flight;
int has_losses;
} bbr_ack_state;
};
extern const struct cong_ctl_if lsquic_cong_bbr_if;
#endif

View File

@ -0,0 +1,273 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/queue.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_hash.h"
#include "lsquic.h"
#include "lsquic_conn.h"
#include "lsquic_malo.h"
#include "lsquic_util.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_bw_sampler.h"
#define LSQUIC_LOGGER_MODULE LSQLM_BW_SAMPLER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(sampler->bws_conn)
#include "lsquic_logger.h"
int
lsquic_bw_sampler_init (struct bw_sampler *sampler, struct lsquic_conn *conn,
enum quic_ft_bit retx_frames)
{
struct malo *malo;
assert(lsquic_is_zero(sampler, sizeof(*sampler)));
malo = lsquic_malo_create(sizeof(struct bwp_state));
if (!malo)
return -1;
sampler->bws_malo = malo;
sampler->bws_conn = conn;
sampler->bws_retx_frames = retx_frames;
LSQ_DEBUG("init");
return 0;
}
void
lsquic_bw_sampler_app_limited (struct bw_sampler *sampler)
{
sampler->bws_flags |= BWS_APP_LIMITED;
sampler->bws_end_of_app_limited_phase = sampler->bws_last_sent_packno;
LSQ_DEBUG("app limited, end of limited phase is %"PRIu64,
sampler->bws_end_of_app_limited_phase);
}
void
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);
}
/* This module only fails when it is unable to allocate memory. This rarely
* happens, so we avoid having to check return values and abort the connection
* instead.
*/
static void
bw_sampler_abort_conn (struct bw_sampler *sampler)
{
if (!(sampler->bws_flags & BWS_CONN_ABORTED))
{
sampler->bws_flags |= BWS_CONN_ABORTED;
LSQ_WARN("aborting connection");
sampler->bws_conn->cn_if->ci_internal_error(sampler->bws_conn,
"resources exhausted");
}
}
#define BW_WARN_ONCE(msg...) do { \
if (!(sampler->bws_flags & BWS_WARNED)) \
{ \
sampler->bws_flags |= BWS_WARNED; \
LSQ_WARN(msg); \
} \
} while (0)
void
lsquic_bw_sampler_packet_sent (struct bw_sampler *sampler,
struct lsquic_packet_out *packet_out, uint64_t in_flight)
{
struct bwp_state *state;
unsigned short sent_sz;
if (packet_out->po_bwp_state)
{
BW_WARN_ONCE("sent: packet %"PRIu64" already has state",
packet_out->po_packno);
return;
}
sampler->bws_last_sent_packno = packet_out->po_packno;
if (!(packet_out->po_frame_types & sampler->bws_retx_frames))
return;
sent_sz = lsquic_packet_out_sent_sz(sampler->bws_conn, packet_out);
sampler->bws_total_sent += sent_sz;
// If there are no packets in flight, the time at which the new transmission
// opens can be treated as the A_0 point for the purpose of bandwidth
// sampling. This underestimates bandwidth to some extent, and produces some
// artificially low samples for most packets in flight, but it provides with
// samples at important points where we would not have them otherwise, most
// importantly at the beginning of the connection.
if (in_flight == 0)
{
sampler->bws_last_acked_packet_time = packet_out->po_sent;
sampler->bws_last_acked_total_sent = sampler->bws_total_sent;
// In this situation ack compression is not a concern, set send rate to
// effectively infinite.
sampler->bws_last_acked_sent_time = packet_out->po_sent;
}
state = lsquic_malo_get(sampler->bws_malo);
if (!state)
{
bw_sampler_abort_conn(sampler);
return;
}
state->bwps_send_state = (struct bwps_send_state) {
.total_bytes_sent = sampler->bws_total_sent,
.total_bytes_acked = sampler->bws_total_acked,
.total_bytes_lost = sampler->bws_total_lost,
.is_app_limited = !!(sampler->bws_flags & BWS_APP_LIMITED),
};
state->bwps_sent_at_last_ack = sampler->bws_last_acked_total_sent;
state->bwps_last_ack_sent_time = sampler->bws_last_acked_sent_time;
state->bwps_last_ack_ack_time = sampler->bws_last_acked_packet_time;
state->bwps_packet_size = sent_sz;
packet_out->po_bwp_state = state;
LSQ_DEBUG("add info for packet %"PRIu64, packet_out->po_packno);
}
void
lsquic_bw_sampler_packet_lost (struct bw_sampler *sampler,
struct lsquic_packet_out *packet_out)
{
if (!packet_out->po_bwp_state)
return;
sampler->bws_total_lost += packet_out->po_bwp_state->bwps_packet_size;
lsquic_malo_put(packet_out->po_bwp_state);
packet_out->po_bwp_state = NULL;
LSQ_DEBUG("packet %"PRIu64" lost, total_lost goes to %"PRIu64,
packet_out->po_packno, sampler->bws_total_lost);
}
struct bw_sample *
lsquic_bw_sampler_packet_acked (struct bw_sampler *sampler,
struct lsquic_packet_out *packet_out, lsquic_time_t ack_time)
{
const struct bwp_state *state;
struct bw_sample *sample;
struct bandwidth send_rate, ack_rate;
lsquic_time_t rtt;
unsigned short sent_sz;
int is_app_limited;
if (!packet_out->po_bwp_state)
return 0;
state = packet_out->po_bwp_state;
sent_sz = lsquic_packet_out_sent_sz(sampler->bws_conn, packet_out);
sampler->bws_total_acked += sent_sz;
sampler->bws_last_acked_total_sent = state->bwps_send_state.total_bytes_sent;
sampler->bws_last_acked_sent_time = packet_out->po_sent;
sampler->bws_last_acked_packet_time = ack_time;
// Exit app-limited phase once a packet that was sent while the connection
// is not app-limited is acknowledged.
if ((sampler->bws_flags & BWS_APP_LIMITED)
&& packet_out->po_packno > sampler->bws_end_of_app_limited_phase)
{
sampler->bws_flags &= ~BWS_APP_LIMITED;
LSQ_DEBUG("exit app-limited phase due to packet %"PRIu64" being acked",
packet_out->po_packno);
}
// There might have been no packets acknowledged at the moment when the
// current packet was sent. In that case, there is no bandwidth sample to
// make.
if (state->bwps_last_ack_sent_time == 0)
goto no_sample;
// Infinite rate indicates that the sampler is supposed to discard the
// current send rate sample and use only the ack rate.
if (packet_out->po_sent > state->bwps_last_ack_sent_time)
send_rate = BW_FROM_BYTES_AND_DELTA(
state->bwps_send_state.total_bytes_sent
- state->bwps_sent_at_last_ack,
packet_out->po_sent - state->bwps_last_ack_sent_time);
else
send_rate = BW_INFINITE();
// During the slope calculation, ensure that ack time of the current packet is
// always larger than the time of the previous packet, otherwise division by
// zero or integer underflow can occur.
if (ack_time <= state->bwps_last_ack_ack_time)
{
BW_WARN_ONCE("Time of the previously acked packet (%"PRIu64") is "
"is larger than the ack time of the current packet (%"PRIu64")",
state->bwps_last_ack_ack_time, ack_time);
goto no_sample;
}
ack_rate = BW_FROM_BYTES_AND_DELTA(
sampler->bws_total_acked - state->bwps_send_state.total_bytes_acked,
ack_time - state->bwps_last_ack_ack_time);
LSQ_DEBUG("send rate: %"PRIu64"; ack rate: %"PRIu64, send_rate.value,
ack_rate.value);
// Note: this sample does not account for delayed acknowledgement time.
// This means that the RTT measurements here can be artificially high,
// especially on low bandwidth connections.
rtt = ack_time - packet_out->po_sent;
is_app_limited = state->bwps_send_state.is_app_limited;
/* After this point, we switch `sample' to point to `state' and don't
* reference `state' anymore.
*/
sample = (void *) packet_out->po_bwp_state;
packet_out->po_bwp_state = NULL;
if (BW_VALUE(&send_rate) < BW_VALUE(&ack_rate))
sample->bandwidth = send_rate;
else
sample->bandwidth = ack_rate;
sample->rtt = rtt;
sample->is_app_limited = is_app_limited;
LSQ_DEBUG("packet %"PRIu64" acked, bandwidth: %"PRIu64" bps",
packet_out->po_packno, BW_VALUE(&sample->bandwidth));
return sample;
no_sample:
lsquic_malo_put(packet_out->po_bwp_state);
packet_out->po_bwp_state = NULL;
return NULL;;
}
unsigned
lsquic_bw_sampler_entry_count (const struct bw_sampler *sampler)
{
void *el;
unsigned count;
count = 0;
for (el = lsquic_malo_first(sampler->bws_malo); el;
el = lsquic_malo_next(sampler->bws_malo))
++count;
return count;
}

View File

@ -0,0 +1,111 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_BW_SAMPLER_H
#define LSQUIC_BW_SAMPLER_H 1
/* Translated from Chromium. */
// Copyright 2016 The Chromium Authors. All rights reserved.
struct lsquic_packet_out;
/* This struct provides a type for bits per second units. It's made into
* a struct so that it is a little harder to make a mistake. The Chromium
* equivalent of this is QuicBandwidth. Use macros to operate.
*/
struct bandwidth
{
uint64_t value; /* Bits per second */
};
#define BW_INFINITE() ((struct bandwidth) { .value = UINT64_MAX, })
#define BW_ZERO() ((struct bandwidth) { .value = 0, })
#define BW_FROM_BYTES_AND_DELTA(bytes_, usecs_) \
((struct bandwidth) { .value = (bytes_) * 8 * 1000000 / (usecs_), })
#define BW_IS_ZERO(bw_) ((bw_)->value == 0)
#define BW_TO_BYTES_PER_SEC(bw_) ((bw_)->value / 8)
#define BW_VALUE(bw_) (+(bw_)->value)
#define BW_TIMES(bw_, factor_) \
((struct bandwidth) { .value = BW_VALUE(bw_) * (factor_), })
#define BW(initial_value_) ((struct bandwidth) { .value = (initial_value_) })
struct bw_sampler
{
struct lsquic_conn *bws_conn;
uint64_t bws_total_sent,
bws_total_acked,
bws_total_lost;
/* Value of bws_total_sent at the time last ACKed packet was sent. Only
* valid if bws_last_acked_sent_time is valid.
*/
uint64_t bws_last_acked_total_sent;
/* Time when last acked packet was sent. Zero if no valid timestamp is
* available.
*/
lsquic_time_t bws_last_acked_sent_time;
lsquic_time_t bws_last_acked_packet_time;
lsquic_packno_t bws_last_sent_packno;
lsquic_packno_t bws_end_of_app_limited_phase;
struct malo *bws_malo; /* For struct osp_state objects */
enum quic_ft_bit bws_retx_frames;
enum {
BWS_CONN_ABORTED = 1 << 0,
BWS_WARNED = 1 << 1,
BWS_APP_LIMITED = 1 << 2,
} bws_flags;
};
struct bw_sample
{
TAILQ_ENTRY(bw_sample) next;
struct bandwidth bandwidth;
lsquic_time_t rtt;
int is_app_limited;
};
int
lsquic_bw_sampler_init (struct bw_sampler *, struct lsquic_conn *,
enum quic_ft_bit);
void
lsquic_bw_sampler_packet_sent (struct bw_sampler *, struct lsquic_packet_out *,
uint64_t in_flight);
/* Free returned sample via lsquic_malo_put() after you're done */
struct bw_sample *
lsquic_bw_sampler_packet_acked (struct bw_sampler *, struct lsquic_packet_out *,
lsquic_time_t ack_time);
void
lsquic_bw_sampler_packet_lost (struct bw_sampler *, struct lsquic_packet_out *);
void
lsquic_bw_sampler_app_limited (struct bw_sampler *);
void
lsquic_bw_sampler_cleanup (struct bw_sampler *);
unsigned
lsquic_bw_sampler_entry_count (const struct bw_sampler *);
#define lsquic_bw_sampler_total_acked(sampler_) (+(sampler_)->bws_total_acked)
/* The following types are exposed in the header file because of unit tests.
* Do not use outside of lsquic_bw_sampler.c.
*/
struct bwps_send_state
{
uint64_t total_bytes_sent,
total_bytes_acked,
total_bytes_lost;
int is_app_limited;
};
struct bwp_state /* BWP State stands for Bandwidth Packet State */
{
struct bwps_send_state bwps_send_state;
uint64_t bwps_sent_at_last_ack;
lsquic_time_t bwps_last_ack_sent_time;
lsquic_time_t bwps_last_ack_ack_time;
unsigned short bwps_packet_size;
};
#endif

View File

@ -4,9 +4,9 @@
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#include <sys/endian.h>
#define bswap_16 bswap16
#define bswap_32 bswap32
#define bswap_64 bswap64
#define bswap_16 bswap16
#define bswap_32 bswap32
#define bswap_64 bswap64
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define bswap_16 OSSwapInt16
@ -21,4 +21,19 @@
#include <byteswap.h>
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define READ_UINT(varname, varwidth, src, nbytes) do { \
varname = 0; \
memcpy((unsigned char *) &(varname) + varwidth / 8 - (nbytes), (src), \
(nbytes)); \
varname = bswap_##varwidth(varname); \
} while (0)
#else
#define READ_UINT(varname, varwidth, src, nbytes) do { \
varname = 0; \
memcpy((unsigned char *) &(varname) + varwidth / 8 - (nbytes), (src), \
(nbytes)); \
} while (0)
#endif
#endif

View File

@ -11,6 +11,9 @@
#include "lsquic_rtt.h"
#include "lsquic_conn_flow.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_stream.h"
#include "lsquic_conn_public.h"
#include "lsquic_mm.h"
@ -20,7 +23,7 @@
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CFCW
#define LSQUIC_LOG_CONN_ID fc->cf_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(fc->cf_conn_pub->lconn)
#include "lsquic_logger.h"

View File

@ -11,19 +11,23 @@
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_int_types.h"
#include "lsquic.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h"
#include "lsquic_enc_sess.h"
#include "lsquic_chsk_stream.h"
#include "lsquic_ver_neg.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_mm.h"
#include "lsquic_sizes.h"
#include "lsquic_full_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(c_hsk->lconn)
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(c_hsk->lconn)
#include "lsquic_logger.h"
@ -34,6 +38,15 @@ hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
LSQ_DEBUG("stream created");
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
if (getenv("LSQUIC_DISABLE_HANDSHAKE"))
{
LSQ_WARN("Handshake disabled: faking it");
c_hsk->lconn->cn_flags |= LSCONN_NO_CRYPTO;
c_hsk->lconn->cn_if->ci_handshake_ok(c_hsk->lconn);
return (void *) c_hsk;
}
#endif
lsquic_stream_wantwrite(stream, 1);
@ -80,7 +93,7 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
}
c_hsk->buf_off += nread;
s = c_hsk->lconn->cn_esf->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session,
s = c_hsk->lconn->cn_esf.g->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session,
c_hsk->buf_in, c_hsk->buf_off);
LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply returned %d", s);
switch (s)
@ -104,10 +117,10 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
if (c_hsk->lconn->cn_esf->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
if (c_hsk->lconn->cn_esf.g->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
{
LSQ_DEBUG("handshake is successful, inform connection");
status = (c_hsk->lconn->cn_esf->esf_did_zero_rtt_succeed(
status = (c_hsk->lconn->cn_esf_c->esf_did_zero_rtt_succeed(
c_hsk->lconn->cn_enc_session)) ? LSQ_HSK_0RTT_OK : LSQ_HSK_OK;
c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, status);
}
@ -118,6 +131,13 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
lsquic_stream_wantwrite(stream, 1);
}
break;
case HS_SREJ:
LSQ_DEBUG("got HS_SREJ");
c_hsk->buf_off = 0;
lsquic_stream_wantread(stream, 0);
if (0 == lsquic_gquic_full_conn_srej(c_hsk->lconn))
lsquic_stream_wantwrite(stream, 1);
break;
default:
LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s);
/* fallthru */
@ -154,7 +174,7 @@ hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
}
len = 4 * 1024;
if (0 != c_hsk->lconn->cn_esf->esf_gen_chlo(c_hsk->lconn->cn_enc_session,
if (0 != c_hsk->lconn->cn_esf.g->esf_gen_chlo(c_hsk->lconn->cn_enc_session,
c_hsk->ver_neg->vn_ver, buf, &len))
{
LSQ_WARN("cannot create CHLO message");

View File

@ -0,0 +1,68 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_cong_ctl.h -- congestion control interface
*/
#ifndef LSQUIC_CONG_CTL_H
#define LSQUIC_CONG_CTL_H
struct lsquic_conn_public;
struct lsquic_packet_out;
enum quic_ft_bit;
/* All the methods don't quite match up between Cubic and BBR. Thus, some of
* the methods are optional; they are marked as such in the comments below.
* Reconciling Cubic and BBR to have similar interface is left as an exercise
* for the future.
*/
struct cong_ctl_if
{
void
(*cci_init) (void *cong_ctl, const struct lsquic_conn_public *,
enum quic_ft_bit);
void
(*cci_ack) (void *cong_ctl, struct lsquic_packet_out *, unsigned packet_sz,
lsquic_time_t now, int app_limited);
void
(*cci_loss) (void *cong_ctl);
/* Optional method */
void
(*cci_begin_ack) (void *cong_ctl, lsquic_time_t ack_time,
uint64_t in_flight);
/* Optional method */
void
(*cci_end_ack) (void *cong_ctl, uint64_t in_flight);
/* Optional method */
void
(*cci_sent) (void *cong_ctl, struct lsquic_packet_out *,
uint64_t in_flight);
/* Optional method */
void
(*cci_lost) (void *cong_ctl, struct lsquic_packet_out *,
unsigned packet_sz);
void
(*cci_timeout) (void *cong_ctl);
void
(*cci_was_quiet) (void *cong_ctl, lsquic_time_t now, uint64_t in_flight);
uint64_t
(*cci_get_cwnd) (void *cong_ctl);
uint64_t
(*cci_pacing_rate) (void *cong_ctl, int in_recovery);
void
(*cci_cleanup) (void *cong_ctl);
};
#endif

View File

@ -2,68 +2,63 @@
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <openssl/rand.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_in.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h"
#include "lsquic_enc_sess.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_ev_log.h"
#include "lsquic_logger.h"
lsquic_cid_t
const lsquic_cid_t *
lsquic_conn_id (const lsquic_conn_t *lconn)
{
return lconn->cn_cid;
/* TODO */
return lsquic_conn_log_cid(lconn);
}
void *
lsquic_conn_get_peer_ctx( const lsquic_conn_t *lconn)
lsquic_conn_get_peer_ctx (struct lsquic_conn *lconn,
const struct sockaddr *local_sa)
{
return lconn->cn_peer_ctx;
const struct network_path *path;
path = lconn->cn_if->ci_get_path(lconn, local_sa);
return path->np_peer_ctx;
}
void
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn,
const struct sockaddr *local, const struct sockaddr *peer)
unsigned char
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, void *peer_ctx,
const struct sockaddr *local_sa, const struct sockaddr *peer_sa)
{
assert(local->sa_family == peer->sa_family);
switch (local->sa_family)
{
case AF_INET:
lconn->cn_flags |= LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA;
memcpy(lconn->cn_local_addr, local, sizeof(struct sockaddr_in));
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in));
break;
case AF_INET6:
lconn->cn_flags |= LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA;
memcpy(lconn->cn_local_addr, local, sizeof(struct sockaddr_in6));
memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in6));
break;
}
return lconn->cn_if->ci_record_addrs(lconn, peer_ctx, local_sa, peer_sa);
}
int
lsquic_conn_get_sockaddr (const lsquic_conn_t *lconn,
lsquic_conn_get_sockaddr (struct lsquic_conn *lconn,
const struct sockaddr **local, const struct sockaddr **peer)
{
if ((lconn->cn_flags & (LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA)) ==
(LSCONN_HAS_PEER_SA|LSCONN_HAS_LOCAL_SA))
{
*local = (struct sockaddr *) lconn->cn_local_addr;
*peer = (struct sockaddr *) lconn->cn_peer_addr;
return 0;
}
else
return -1;
const struct network_path *path;
path = lconn->cn_if->ci_get_path(lconn, NULL);
*local = NP_LOCAL_SA(path);
*peer = NP_PEER_SA(path);
return 0;
}
@ -73,8 +68,8 @@ lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
{
assert(!(packet_in->pi_flags & PI_OWN_DATA));
/* The size should be guarded in lsquic_engine_packet_in(): */
assert(packet_in->pi_data_sz <= QUIC_MAX_PACKET_SZ);
unsigned char *const copy = lsquic_mm_get_1370(&enpub->enp_mm);
assert(packet_in->pi_data_sz <= GQUIC_MAX_PACKET_SZ);
unsigned char *const copy = lsquic_mm_get_packet_in_buf(&enpub->enp_mm, 1370);
if (!copy)
{
LSQ_WARN("cannot allocate memory to copy incoming packet data");
@ -87,51 +82,6 @@ lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
}
int
lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
struct lsquic_engine_public *enpub,
lsquic_packet_in_t *packet_in)
{
size_t header_len, data_len;
enum enc_level enc_level;
size_t out_len = 0;
unsigned char *copy = lsquic_mm_get_1370(&enpub->enp_mm);
if (!copy)
{
LSQ_WARN("cannot allocate memory to copy incoming packet data");
return -1;
}
header_len = packet_in->pi_header_sz;
data_len = packet_in->pi_data_sz - packet_in->pi_header_sz;
enc_level = lconn->cn_esf->esf_decrypt(lconn->cn_enc_session,
lconn->cn_version, 0,
packet_in->pi_packno, packet_in->pi_data,
&header_len, data_len,
lsquic_packet_in_nonce(packet_in),
copy, 1370, &out_len);
if ((enum enc_level) -1 == enc_level)
{
lsquic_mm_put_1370(&enpub->enp_mm, copy);
EV_LOG_CONN_EVENT(lconn->cn_cid, "could not decrypt packet %"PRIu64,
packet_in->pi_packno);
return -1;
}
assert(header_len + out_len <= 1370);
if (packet_in->pi_flags & PI_OWN_DATA)
lsquic_mm_put_1370(&enpub->enp_mm, packet_in->pi_data);
packet_in->pi_data = copy;
packet_in->pi_flags |= PI_OWN_DATA | PI_DECRYPTED
| (enc_level << PIBIT_ENC_LEV_SHIFT);
packet_in->pi_header_sz = header_len;
packet_in->pi_data_sz = out_len + header_len;
EV_LOG_CONN_EVENT(lconn->cn_cid, "decrypted packet %"PRIu64" crypto: %s",
packet_in->pi_packno, lsquic_enclev2str[ enc_level ]);
return 0;
}
enum lsquic_version
lsquic_conn_quic_version (const lsquic_conn_t *lconn)
{
@ -142,24 +92,185 @@ lsquic_conn_quic_version (const lsquic_conn_t *lconn)
}
struct stack_st_X509 *
lsquic_conn_get_server_cert_chain (struct lsquic_conn *lconn)
enum lsquic_crypto_ver
lsquic_conn_crypto_ver (const lsquic_conn_t *lconn)
{
return LSQ_CRY_QUIC;
}
const char *
lsquic_conn_crypto_cipher (const lsquic_conn_t *lconn)
{
if (lconn->cn_enc_session)
return lconn->cn_esf->esf_get_server_cert_chain(lconn->cn_enc_session);
return lconn->cn_esf_c->esf_cipher(lconn->cn_enc_session);
else
return NULL;
}
ssize_t
lsquic_conn_get_zero_rtt(const lsquic_conn_t *lconn,
unsigned char *zero_rtt, size_t zero_rtt_len)
int
lsquic_conn_crypto_keysize (const lsquic_conn_t *lconn)
{
ssize_t ret = -1;
if (lconn->cn_enc_session && (lconn->cn_flags & LSCONN_VER_SET))
ret = lconn->cn_esf->esf_get_zero_rtt(lconn->cn_enc_session,
lconn->cn_version,
zero_rtt, zero_rtt_len);
return ret;
if (lconn->cn_enc_session)
return lconn->cn_esf_c->esf_keysize(lconn->cn_enc_session);
else
return -1;
}
int
lsquic_conn_crypto_alg_keysize (const lsquic_conn_t *lconn)
{
if (lconn->cn_enc_session)
return lconn->cn_esf_c->esf_alg_keysize(lconn->cn_enc_session);
else
return -1;
}
struct stack_st_X509 *
lsquic_conn_get_server_cert_chain (struct lsquic_conn *lconn)
{
if (lconn->cn_enc_session)
return lconn->cn_esf_c->esf_get_server_cert_chain(lconn->cn_enc_session);
else
return NULL;
}
void
lsquic_conn_make_stream (struct lsquic_conn *lconn)
{
lconn->cn_if->ci_make_stream(lconn);
}
unsigned
lsquic_conn_n_pending_streams (const struct lsquic_conn *lconn)
{
return lconn->cn_if->ci_n_pending_streams(lconn);
}
unsigned
lsquic_conn_n_avail_streams (const struct lsquic_conn *lconn)
{
return lconn->cn_if->ci_n_avail_streams(lconn);
}
unsigned
lsquic_conn_cancel_pending_streams (struct lsquic_conn *lconn, unsigned count)
{
return lconn->cn_if->ci_cancel_pending_streams(lconn, count);
}
void
lsquic_conn_going_away (struct lsquic_conn *lconn)
{
lconn->cn_if->ci_going_away(lconn);
}
void
lsquic_conn_close (struct lsquic_conn *lconn)
{
lconn->cn_if->ci_close(lconn);
}
int
lsquic_conn_is_push_enabled (lsquic_conn_t *lconn)
{
return lconn->cn_if->ci_is_push_enabled(lconn);
}
struct lsquic_stream *
lsquic_conn_get_stream_by_id (struct lsquic_conn *lconn,
lsquic_stream_id_t stream_id)
{
return lconn->cn_if->ci_get_stream_by_id(lconn, stream_id);
}
struct lsquic_engine *
lsquic_conn_get_engine (struct lsquic_conn *lconn)
{
return lconn->cn_if->ci_get_engine(lconn);
}
int
lsquic_conn_push_stream (struct lsquic_conn *lconn, void *hset,
struct lsquic_stream *stream, const struct iovec* url,
const struct iovec *authority, const struct lsquic_http_headers *headers)
{
return lconn->cn_if->ci_push_stream(lconn, hset, stream, url, authority,
headers);
}
lsquic_conn_ctx_t *
lsquic_conn_get_ctx (const struct lsquic_conn *lconn)
{
return lconn->cn_if->ci_get_ctx(lconn);
}
void
lsquic_conn_set_ctx (struct lsquic_conn *lconn, lsquic_conn_ctx_t *ctx)
{
lconn->cn_if->ci_set_ctx(lconn, ctx);
}
void
lsquic_conn_abort (struct lsquic_conn *lconn)
{
lconn->cn_if->ci_abort(lconn);
}
void
lsquic_generate_cid (lsquic_cid_t *cid, size_t len)
{
if (!len)
/* If not set, generate ID between 8 and MAX_CID_LEN bytes in length */
len = 8 + rand() % (MAX_CID_LEN - 7);
RAND_bytes(cid->idbuf, len);
cid->len = len;
}
void
lsquic_generate_cid_gquic (lsquic_cid_t *cid)
{
lsquic_generate_cid(cid, GQUIC_CID_LEN);
}
void
lsquic_conn_retire_cid (struct lsquic_conn *lconn)
{
if (lconn->cn_if->ci_retire_cid)
lconn->cn_if->ci_retire_cid(lconn);
}
enum LSQUIC_CONN_STATUS
lsquic_conn_status (struct lsquic_conn *lconn, char *errbuf, size_t bufsz)
{
return lconn->cn_if->ci_status(lconn, errbuf, bufsz);
}
const lsquic_cid_t *
lsquic_conn_log_cid (const struct lsquic_conn *lconn)
{
if (lconn->cn_if && lconn->cn_if->ci_get_log_cid)
return lconn->cn_if->ci_get_log_cid(lconn);
return CN_SCID(lconn);
}

View File

@ -2,6 +2,11 @@
/*
* lsquic_conn.h -- Connection interface
*
* There are two types of connections: full (lsquic_full_conn.h) and mini
* (lsquic_mini_conn.h). The function pointers and struct in this header
* file provide a unified interface engine.c can use to interact with
* either of the connection types. For this to work, struct lsquic_conn
* must be the first element of struct full_conn and struct mini_conn.
*/
#ifndef LSQUIC_CONN_H
#define LSQUIC_CONN_H
@ -15,7 +20,6 @@
#endif
struct lsquic_conn;
struct lsquic_enc_session;
struct lsquic_engine_public;
struct lsquic_packet_out;
struct lsquic_packet_in;
@ -30,8 +34,9 @@ enum lsquic_conn_flags {
LSCONN_TICKED = (1 << 0),
LSCONN_HAS_OUTGOING = (1 << 1),
LSCONN_HASHED = (1 << 2),
LSCONN_HAS_PEER_SA = (1 << 4),
LSCONN_HAS_LOCAL_SA = (1 << 5),
LSCONN_MINI = (1 << 3), /* This is a mini connection */
LSCONN_UNUSED_4 = (1 << 4),
LSCONN_UNUSED_5 = (1 << 5),
LSCONN_HANDSHAKE_DONE = (1 << 6),
LSCONN_CLOSING = (1 << 7),
LSCONN_PEER_GOING_AWAY= (1 << 8),
@ -42,8 +47,17 @@ enum lsquic_conn_flags {
LSCONN_COI_ACTIVE = (1 <<13),
LSCONN_COI_INACTIVE = (1 <<14),
LSCONN_SEND_BLOCKED = (1 <<15), /* Send connection blocked frame */
LSCONN_PROMOTED = (1 <<16), /* Promoted. Only set if LSCONN_MINI is set */
LSCONN_NEVER_TICKABLE = (1 <<17), /* Do not put onto the Tickable Queue */
LSCONN_UNUSED_18 = (1 <<18),
LSCONN_ATTQ = (1 <<19),
LSCONN_SKIP_ON_PROC = (1 <<20),
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
LSCONN_NO_CRYPTO = (1 <<21),
#endif
LSCONN_SERVER = (1 <<22),
LSCONN_IETF = (1 <<23),
LSCONN_RETRY_CONN = (1 <<24), /* This is a retry connection */
};
/* A connection may have things to send and be closed at the same time.
@ -51,10 +65,29 @@ enum lsquic_conn_flags {
enum tick_st {
TICK_SEND = (1 << 0),
TICK_CLOSE = (1 << 1),
TICK_PROMOTE = (1 << 2), /* Promote mini connection to full connection */
};
#define TICK_QUIET 0
struct network_path
{
union {
unsigned char buf[sizeof(struct sockaddr_in6)];
struct sockaddr sockaddr;
} np_local_addr_u;
#define np_local_addr np_local_addr_u.buf
unsigned char np_peer_addr[sizeof(struct sockaddr_in6)];
void *np_peer_ctx;
lsquic_cid_t np_dcid;
unsigned short np_pack_size;
unsigned char np_path_id; /* Used for logging */
};
#define NP_LOCAL_SA(path_) (&(path_)->np_local_addr_u.sockaddr)
#define NP_PEER_SA(path_) ((struct sockaddr *) (path_)->np_peer_addr)
#define NP_IS_IPv6(path_) (AF_INET6 == NP_LOCAL_SA(path_)->sa_family)
struct conn_iface
{
enum tick_st
@ -63,8 +96,16 @@ struct conn_iface
void
(*ci_packet_in) (struct lsquic_conn *, struct lsquic_packet_in *);
/* Note: all packets "checked out" by calling this method should be
* returned back to the connection via ci_packet_sent() or
* ci_packet_not_sent() calls before the connection is ticked next.
* The connection, in turn, should not perform any extra processing
* (especially schedule more packets) during any of these method
* calls. This is because the checked out packets are not accounted
* for by the congestion controller.
*/
struct lsquic_packet_out *
(*ci_next_packet_to_send) (struct lsquic_conn *);
(*ci_next_packet_to_send) (struct lsquic_conn *, size_t);
void
(*ci_packet_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
@ -95,39 +136,198 @@ struct conn_iface
const struct conn_stats *
(*ci_get_stats) (struct lsquic_conn *);
#endif
void
(*ci_client_call_on_new) (struct lsquic_conn *);
enum LSQUIC_CONN_STATUS
(*ci_status) (struct lsquic_conn *, char *errbuf, size_t bufsz);
unsigned
(*ci_n_avail_streams) (const struct lsquic_conn *);
unsigned
(*ci_n_pending_streams) (const struct lsquic_conn *);
unsigned
(*ci_cancel_pending_streams) (struct lsquic_conn *, unsigned n);
void
(*ci_going_away) (struct lsquic_conn *);
int
(*ci_is_push_enabled) (struct lsquic_conn *);
struct lsquic_stream *
(*ci_get_stream_by_id) (struct lsquic_conn *, lsquic_stream_id_t stream_id);
struct lsquic_engine *
(*ci_get_engine) (struct lsquic_conn *);
struct lsquic_conn_ctx *
(*ci_get_ctx) (const struct lsquic_conn *);
void
(*ci_set_ctx) (struct lsquic_conn *, struct lsquic_conn_ctx *);
void
(*ci_make_stream) (struct lsquic_conn *);
void
(*ci_abort) (struct lsquic_conn *);
void
(*ci_retire_cid) (struct lsquic_conn *);
void
(*ci_close) (struct lsquic_conn *);
void
(*ci_stateless_reset) (struct lsquic_conn *);
int
(*ci_crypto_keysize) (const struct lsquic_conn *);
int
(*ci_crypto_alg_keysize) (const struct lsquic_conn *);
enum lsquic_crypto_ver
(*ci_crypto_ver) (const struct lsquic_conn *);
const char *
(*ci_crypto_cipher) (const struct lsquic_conn *);
int
(*ci_push_stream) (struct lsquic_conn *, void *hset, struct lsquic_stream *,
const struct iovec* url, const struct iovec* authority,
const struct lsquic_http_headers *headers);
/* Use this to abort the connection when unlikely errors occur */
void
(*ci_internal_error) (struct lsquic_conn *, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
;
/* Abort connection with error */
void
(*ci_abort_error) (struct lsquic_conn *, int is_app, unsigned error_code,
const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 4, 5)))
#endif
;
void
(*ci_tls_alert) (struct lsquic_conn *, uint8_t);
/* Returns 0 if connection is to be deleted immediately */
lsquic_time_t
(*ci_drain_time) (const struct lsquic_conn *);
/* Returns true if it's time to report the connection's CIDs' liveness */
int
(*ci_report_live) (struct lsquic_conn *, lsquic_time_t now);
/* If `local_sa' is NULL, return default path */
struct network_path *
(*ci_get_path) (struct lsquic_conn *, const struct sockaddr *local_sa);
unsigned char
(*ci_record_addrs) (struct lsquic_conn *, void *peer_ctx,
const struct sockaddr *local_sa, const struct sockaddr *peer_sa);
const lsquic_cid_t *
(*ci_get_log_cid) (const struct lsquic_conn *);
};
struct cert_susp_head;
#define LSCONN_CCE_BITS 3
#define LSCONN_MAX_CCES (1 << LSCONN_CCE_BITS)
struct conn_cid_elem
{
struct lsquic_hash_elem cce_hash_el; /* Must be first element */
lsquic_cid_t cce_cid;
union {
unsigned seqno;
unsigned short port;
} cce_u;
#define cce_seqno cce_u.seqno
#define cce_port cce_u.port
enum conn_cce_flags {
CCE_USED = 1 << 0, /* Connection ID has been used */
CCE_SEQNO = 1 << 1, /* cce_seqno is set (CIDs in Initial
* packets have no sequence number).
*/
CCE_REG = 1 << 2, /* CID has been registered */
CCE_PORT = 1 << 3, /* It's not a CID element at all:
* cce_port is the hash value.
*/
} cce_flags;
};
struct lsquic_conn
{
void *cn_peer_ctx;
struct lsquic_enc_session *cn_enc_session;
const struct enc_session_funcs
*cn_esf;
lsquic_cid_t cn_cid;
void *cn_enc_session;
const struct enc_session_funcs_common
*cn_esf_c;
union {
const struct enc_session_funcs_gquic *g;
const struct enc_session_funcs_iquic *i;
} cn_esf;
#define cn_cid cn_cces[0].cce_cid
STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn;
/* This and cn_next_closed_conn could be made into a union, as new full
* connections are never closed.
*/
STAILQ_ENTRY(lsquic_conn) cn_next_new_full;
TAILQ_ENTRY(lsquic_conn) cn_next_ticked;
TAILQ_ENTRY(lsquic_conn) cn_next_out,
cn_next_hash;
TAILQ_ENTRY(lsquic_conn) cn_next_out;
TAILQ_ENTRY(lsquic_conn) cn_next_susp_cert;
/* Reuse this entry, as evanecsent connections are never suspended: */
#define cn_next_pr cn_next_susp_cert
const struct conn_iface *cn_if;
const struct parse_funcs *cn_pf;
struct attq_elem *cn_attq_elem;
struct cert_susp_head *cn_cert_susp_head;
lsquic_time_t cn_last_sent;
lsquic_time_t cn_last_ticked;
struct conn_cid_elem *cn_cces; /* At least one is available */
enum lsquic_conn_flags cn_flags;
enum lsquic_version cn_version;
unsigned cn_hash;
unsigned short cn_pack_size;
unsigned char cn_local_addr[sizeof(struct sockaddr_in6)];
union {
unsigned char buf[sizeof(struct sockaddr_in6)];
struct sockaddr sa;
} cn_peer_addr_u;
#define cn_peer_addr cn_peer_addr_u.buf
enum lsquic_version cn_version:8;
unsigned char cn_cces_mask; /* Those that are set */
unsigned char cn_n_cces; /* Number of CCEs in cn_cces */
unsigned char cn_cur_cce_idx;
#if LSQUIC_TEST
struct conn_cid_elem cn_cces_buf[8];
#define LSCONN_INITIALIZER_CID(lsconn_, cid_) { \
.cn_cces = (lsconn_).cn_cces_buf, \
.cn_cces_buf[0].cce_seqno = 0, \
.cn_cces_buf[0].cce_flags = CCE_SEQNO, \
.cn_cces_buf[0].cce_cid = (cid_), \
.cn_n_cces = 8, .cn_cces_mask = 1, }
#define LSCONN_INITIALIZER_CIDLEN(lsconn_, len_) { \
.cn_cces = (lsconn_).cn_cces_buf, \
.cn_cces_buf[0].cce_seqno = 0, \
.cn_cces_buf[0].cce_flags = CCE_SEQNO, \
.cn_cces_buf[0].cce_cid = { .len = len_ }, \
.cn_n_cces = 8, .cn_cces_mask = 1, }
#define LSCONN_INITIALIZE(lsconn_) do { \
(lsconn_)->cn_cces = (lsconn_)->cn_cces_buf; \
(lsconn_)->cn_n_cces = 8; (lsconn_)->cn_cces_mask = 1; } while (0)
#endif
};
void
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, const struct sockaddr *local,
const struct sockaddr *peer);
#define END_OF_CCES(conn) ((conn)->cn_cces + (conn)->cn_n_cces)
#define CN_SCID(conn) (&(conn)->cn_cces[(conn)->cn_cur_cce_idx].cce_cid)
unsigned char
lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, void *peer_ctx,
const struct sockaddr *local_sa, const struct sockaddr *peer_sa);
int
lsquic_conn_decrypt_packet (lsquic_conn_t *lconn,
@ -137,6 +337,15 @@ int
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
struct lsquic_engine_public *, struct lsquic_packet_in *);
void
lsquic_generate_cid (lsquic_cid_t *cid, size_t len);
void
lsquic_generate_cid_gquic (lsquic_cid_t *cid);
void
lsquic_conn_retire_cid (lsquic_conn_t *lconn);
#define lsquic_conn_adv_time(c) ((c)->cn_attq_elem->ae_adv_time)
#if LSQUIC_CONN_STATS
@ -164,6 +373,7 @@ struct conn_stats {
unsigned long stream_frames;
unsigned long acks;
unsigned long packets; /* Number of sent packets */
unsigned long acked_via_loss; /* Number of packets acked via loss record */
unsigned long retx_packets; /* Number of retransmitted packets */
unsigned long bytes; /* Overall bytes out */
unsigned long headers_uncomp; /* Sum of uncompressed header bytes */

View File

@ -19,6 +19,10 @@ struct lsquic_send_ctl;
#if LSQUIC_CONN_STATS
struct conn_stats;
#endif
struct qpack_enc_hdl;
struct qpack_dec_hdl;
struct h3_prio_tree;
struct network_path;
struct lsquic_conn_public {
struct lsquic_streams_tailq sending_streams, /* Send RST_STREAM, BLOCKED, and WUF frames */
@ -33,11 +37,25 @@ struct lsquic_conn_public {
struct malo *packet_out_malo;
struct lsquic_conn *lconn;
struct lsquic_mm *mm;
struct headers_stream *hs;
union {
struct {
struct headers_stream *hs;
} gquic;
struct {
struct qpack_enc_hdl *qeh;
struct qpack_dec_hdl *qdh;
struct h3_prio_tree *prio_tree;
struct lsquic_hash *promises;
} ietf;
} u;
enum {
CP_STREAM_UNBLOCKED = 1 << 0, /* Set when a stream becomes unblocked */
} cp_flags;
struct lsquic_send_ctl *send_ctl;
#if LSQUIC_CONN_STATS
struct conn_stats *conn_stats;
#endif
const struct network_path *path;
};
#endif

View File

@ -198,6 +198,75 @@ int get_common_cert(uint64_t hash, uint32_t index, lsquic_str_t *buf)
}
static int
comp_ls_str (lsquic_str_t * a, const void * b, size_t b_len)
{
size_t a_len;
int r;
a_len = lsquic_str_len(a);
r = memcmp(lsquic_str_buf(a), b, a_len < b_len ? a_len : b_len);
if (r)
return r;
else
return (a_len > b_len) - (b_len > a_len);
}
/* 0, matched -1, error */
int match_common_cert(lsquic_str_t * cert, lsquic_str_t * common_set_hashes,
uint64_t* out_hash, uint32_t* out_index)
{
size_t i, j;
int n;
uint64_t hash;
size_t min, max, mid;
if (lsquic_str_len(common_set_hashes) % sizeof(uint64_t) != 0)
return -1;
for (i = 0; i < lsquic_str_len(common_set_hashes) / sizeof(uint64_t); i++)
{
memcpy(&hash, lsquic_str_buf(common_set_hashes) + i * sizeof(uint64_t),
sizeof(uint64_t));
for (j = 0; j < common_certs_num; j++)
{
if (common_cert_set[j].hash != hash)
continue;
if (common_cert_set[j].num_certs == 0)
continue;
min = 0;
max = common_cert_set[j].num_certs - 1;
while (max >= min)
{
mid = min + ((max - min) / 2);
n = comp_ls_str(cert, common_cert_set[j].certs[mid],
common_cert_set[j].lens[mid]);
if (n < 0)
{
if (mid == 0)
break;
max = mid - 1;
}
else if (n > 0)
min = mid + 1;
else
{
*out_hash = hash;
*out_index = mid;
return 0;
}
}
}
}
return -1;
}
/* result is written to dict */
static void
make_zlib_dict_for_entries(cert_entry_t *entries,
@ -240,6 +309,59 @@ void get_certs_hash(lsquic_str_t *certs, size_t certs_count, uint64_t *hashs)
}
static void get_certs_entries(lsquic_str_t **certs, size_t certs_count,
lsquic_str_t *client_common_set_hashes,
lsquic_str_t *client_cached_cert_hashes,
cert_entry_t *entries)
{
size_t i;
int j;
cert_entry_t *entry;
uint64_t hash, cached_hash;
bool cached;
const bool cached_valid = (lsquic_str_len(client_cached_cert_hashes) % sizeof(uint64_t) == 0)
&& (lsquic_str_len(client_cached_cert_hashes) > 0);
assert(&entries[certs_count - 1]);
for (i = 0; i<certs_count; ++i)
{
entry = &entries[i];
if (cached_valid)
{
cached = false;
hash = fnv1a_64((const uint8_t *)lsquic_str_buf(certs[i]), lsquic_str_len(certs[i]));
for (j = 0; j < (int)lsquic_str_len(client_cached_cert_hashes);
j += sizeof(uint64_t))
{
memcpy(&cached_hash, lsquic_str_buf(client_cached_cert_hashes) + j,
sizeof(uint64_t));
if (hash != cached_hash)
continue;
entry->type = ENTRY_CACHED;
entry->hash = hash;
cached = true;
break;
}
if (cached)
continue;
}
if (0 == match_common_cert(certs[i], client_common_set_hashes,
&entry->set_hash, &entry->index))
{
entry->type = ENTRY_COMMON;
continue;
}
entry->type = ENTRY_COMPRESSED;
}
}
size_t get_entries_size(cert_entry_t *entries, size_t entries_count)
{
size_t i;
@ -265,7 +387,6 @@ size_t get_entries_size(cert_entry_t *entries, size_t entries_count)
return entries_size;
}
void serialize_cert_entries(uint8_t* out, int *out_len, cert_entry_t *entries,
size_t entries_count)
{
@ -438,6 +559,116 @@ static int parse_entries(const unsigned char **in_out, const unsigned char *cons
}
/* return 0 for OK */
int compress_certs(lsquic_str_t **certs, size_t certs_count,
lsquic_str_t *client_common_set_hashes,
lsquic_str_t *client_cached_cert_hashes,
lsquic_str_t *result)
{
int rv;
size_t i;
size_t uncompressed_size = 0, compressed_size = 0 ;
z_stream z;
lsquic_str_t *dict;
size_t entries_size, result_length;
int out_len;
uint8_t* out;
uint32_t tmp_size_32;
cert_entry_t *entries;
entries = malloc(sizeof(cert_entry_t) * certs_count);
if (!entries)
return -1;
dict = lsquic_str_new(NULL, 0);
if (!dict)
goto err;
get_certs_entries(certs, certs_count, client_common_set_hashes,
client_cached_cert_hashes, entries);
for (i = 0; i < certs_count; i++)
{
if (entries[i].type == ENTRY_COMPRESSED)
{
/*uint32_t length + cert content*/
uncompressed_size += 4 + lsquic_str_len(certs[i]);
}
}
if (uncompressed_size > 0)
{
memset(&z, 0, sizeof(z));
if (Z_OK != deflateInit(&z, Z_DEFAULT_COMPRESSION))
goto err;
make_zlib_dict_for_entries(entries, certs, certs_count, dict);
if(Z_OK != deflateSetDictionary(&z, (const unsigned char *)lsquic_str_buf(dict), lsquic_str_len(dict)))
goto err;
compressed_size = deflateBound(&z, uncompressed_size);
}
entries_size = get_entries_size(entries, certs_count);
result_length = entries_size + (uncompressed_size > 0 ? 4 : 0) +
compressed_size;
lsquic_str_prealloc(result, result_length);
out = (unsigned char *)lsquic_str_buf(result);
serialize_cert_entries(out, &out_len, entries, certs_count);
out += entries_size;
if (uncompressed_size == 0)
{
lsquic_str_setlen(result, entries_size);
rv = 0;
goto cleanup;
}
tmp_size_32 = uncompressed_size;
memcpy(out, &tmp_size_32, sizeof(uint32_t));
out += sizeof(uint32_t);
z.next_out = out;
z.avail_out = compressed_size;
for (i = 0; i < certs_count; ++i)
{
if (entries[i].type != ENTRY_COMPRESSED)
continue;
tmp_size_32 = lsquic_str_len(certs[i]);
z.next_in = (uint8_t*)(&tmp_size_32);
z.avail_in = sizeof(tmp_size_32);
if (Z_OK != deflate(&z, Z_NO_FLUSH) || z.avail_in)
goto err;
z.next_in = (unsigned char *)lsquic_str_buf(certs[i]);
z.avail_in = lsquic_str_len(certs[i]);
if (Z_OK != deflate(&z, Z_NO_FLUSH) || z.avail_in)
goto err;
}
z.avail_in = 0;
if (Z_STREAM_END != deflate(&z, Z_FINISH))
goto err;
rv = 0;
result_length -= z.avail_out;
lsquic_str_setlen(result, result_length);
cleanup:
free(entries);
if (dict)
lsquic_str_delete(dict);
if (uncompressed_size)
deflateEnd(&z);
return rv;
err:
rv = -1;
goto cleanup;
}
/* 0: ok */
int decompress_certs(const unsigned char *in, const unsigned char *in_end,
lsquic_str_t *cached_certs, size_t cached_certs_count,

View File

@ -10,6 +10,7 @@ struct lsquic_str;
extern "C" {
#endif
enum entry_type {
END_OF_LIST = 0,
ENTRY_COMPRESSED = 1,
@ -24,6 +25,7 @@ typedef struct cert_entry_st {
uint64_t set_hash;
} cert_entry_t;
typedef struct common_cert_st
{
size_t num_certs;
@ -34,6 +36,16 @@ typedef struct common_cert_st
struct lsquic_str * get_common_certs_hash();
int get_common_cert(uint64_t hash, uint32_t index, struct lsquic_str *buf);
int match_common_cert(struct lsquic_str * cert, struct lsquic_str * common_set_hashes,
uint64_t* out_hash, uint32_t* out_index);
int compress_certs(struct lsquic_str **certs, size_t certs_count,
struct lsquic_str *client_common_set_hashes,
struct lsquic_str *client_cached_cert_hashes,
struct lsquic_str *result);
int get_certs_count(struct lsquic_str *compressed_crt_buf);
int decompress_certs(const unsigned char *in, const unsigned char *in_end,
struct lsquic_str *cached_certs, size_t cached_certs_count,
@ -43,8 +55,10 @@ int decompress_certs(const unsigned char *in, const unsigned char *in_end,
void
lsquic_crt_cleanup (void);
#ifdef __cplusplus
}
#endif
#endif //__LSQUIC_CRT_COMPRESS_H__

View File

@ -17,7 +17,6 @@
#include "lsquic_types.h"
#include "lsquic_crypto.h"
#include "lsquic_alarmset.h"
#include "lsquic_parse.h"
#include "lsquic_util.h"
#include "lsquic_str.h"
@ -81,17 +80,6 @@ void fnv1a_inc(uint128 *hash, const uint8_t *data, int len)
}
}
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t *data2, int len2)
{
uint128 hash;
memcpy(&hash, &s_init_hash, 16);
fnv1a_inc(&hash, data1, len1);
if (data2)
fnv1a_inc(&hash, data2, len2);
return hash;
}
uint128 fnv1a_128_3(const uint8_t *data1, int len1,
const uint8_t *data2, int len2,
const uint8_t *data3, int len3)
@ -105,12 +93,6 @@ uint128 fnv1a_128_3(const uint8_t *data1, int len1,
return hash;
}
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md)
{
uint128 hash = fnv1a_128_2(data1, len1, data2, len2);
memcpy(md, (void *)&hash, 16);
}
/* HS_PKT_HASH_LENGTH bytes of md */
void serialize_fnv128_short(uint128 v, uint8_t *md)
{
@ -167,16 +149,6 @@ void fnv1a_inc(uint128 *hash, const uint8_t * data, int len)
}
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t * data2, int len2)
{
uint128 hash = {UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)};
fnv1a_inc(&hash, data1, len1);
if (data2)
fnv1a_inc(&hash, data2, len2);
return hash;
}
uint128 fnv1a_128_3(const uint8_t * data1, int len1,
const uint8_t * data2, int len2,
const uint8_t * data3, int len3)
@ -189,13 +161,6 @@ uint128 fnv1a_128_3(const uint8_t * data1, int len1,
}
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md)
{
uint128 hash = fnv1a_128_2(data1, len1, data2, len2);
memcpy(md, (void *)&hash.lo_, 8);
memcpy(md + 8, (void *)&hash.hi_, 8);
}
/* HS_PKT_HASH_LENGTH bytes of md */
void serialize_fnv128_short(uint128 v, uint8_t *md)
{
@ -206,55 +171,8 @@ void serialize_fnv128_short(uint128 v, uint8_t *md)
#endif
uint128 fnv1a_128(const uint8_t * data, int len)
{
return fnv1a_128_2(data, len , NULL, 0);
}
void fnv1a_128_s(const uint8_t * data, int len, uint8_t *md)
{
fnv1a_128_2_s(data, len, NULL, 0, md);
}
/* packet data = header + MD + payload */
/* return 0 if OK */
int verify_hs_pkt(const uint8_t *pkg_data, size_t header_len, size_t pkg_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
if (pkg_len < header_len + HS_PKT_HASH_LENGTH)
return -1;
hash = fnv1a_128_2(pkg_data, header_len, pkg_data + header_len + HS_PKT_HASH_LENGTH,
pkg_len - header_len - HS_PKT_HASH_LENGTH);
serialize_fnv128_short(hash, md);
return memcmp(md, pkg_data + header_len, HS_PKT_HASH_LENGTH);
}
/* packet data = header + MD + payload, update the MD part */
int update_hs_pkt_hash(uint8_t *pkg_data, int header_len, int pkg_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
if (pkg_len < header_len + HS_PKT_HASH_LENGTH)
return -1;
hash = fnv1a_128_2(pkg_data, header_len, pkg_data + header_len + HS_PKT_HASH_LENGTH,
pkg_len - header_len - HS_PKT_HASH_LENGTH);
serialize_fnv128_short(hash, md);
memcpy(pkg_data + header_len, md, HS_PKT_HASH_LENGTH);
return 0;
}
int get_hs_pkt_hash_len()
{
return HS_PKT_HASH_LENGTH;
}
void sha256(const uint8_t *buf, int len, uint8_t *h)
static void sha256(const uint8_t *buf, int len, uint8_t *h)
{
SHA256_CTX ctx;
SHA256_Init(&ctx);
@ -499,17 +417,6 @@ int aes_aead_dec(EVP_AEAD_CTX *key,
}
}
/* aes 128, 16 bytes */
int aes_get_key_length()
{
return 16;
}
void gen_nonce_s(char *buf, int length)
{
rand_bytes(buf, length);
}
/* 32 bytes client nonce with 4 bytes tm, 8 bytes orbit */
void gen_nonce_c(unsigned char *buf, uint64_t orbit)
{
@ -558,17 +465,6 @@ X509 *bio_to_crt(const void *buf, int len, int type)
}
int read_rsa_priv_key(const uint8_t *buf, int len, EVP_PKEY *pkey)
{
RSA *rsa = RSA_private_key_from_bytes(buf, len);
if (!rsa)
return -1;
return EVP_PKEY_assign_RSA(pkey, rsa);
}
int gen_prof(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *priv_key, uint8_t *buf, size_t *buf_len)

View File

@ -13,6 +13,9 @@ extern "C" {
#endif
struct lsquic_str;
struct evp_aead_ctx_st;
struct evp_pkey_st;
struct x509_st;
#if defined( __x86_64 )||defined( __x86_64__ )
typedef __uint128_t uint128;
@ -53,49 +56,32 @@ int c255_gen_share_key(unsigned char *priv_key, unsigned char *peer_pub_key, uns
uint64_t fnv1a_64(const uint8_t * data, int len);
void fnv1a_64_s(const uint8_t * data, int len, char *md);
uint128 fnv1a_128(const uint8_t * data, int len);
void fnv1a_128_s(const uint8_t * data , int len, uint8_t *md);
uint128 fnv1a_128_2(const uint8_t * data1, int len1, const uint8_t * data2, int len2);
uint128 fnv1a_128_3(const uint8_t * data1, int len1,
const uint8_t * data2, int len2,
const uint8_t * data3, int len3);
void fnv1a_128_2_s(const uint8_t * data1, int len1, const uint8_t * data2, int len2, uint8_t *md);
void serialize_fnv128_short(uint128 v, uint8_t *md);
/* before session handshake complete */
int verify_hs_pkt(const uint8_t *pkg_data, size_t header_len, size_t pkg_len);
int update_hs_pkt_hash(uint8_t *pkg_data, int header_len, int pkg_len);
int get_hs_pkt_hash_len();
/*16 bytes of h outputted */
void sha256(const uint8_t *buf, int len, uint8_t *h);
/* Encrypt plaint text to cipher test */
int aes_aead_enc(EVP_AEAD_CTX *key,
int aes_aead_enc(struct evp_aead_ctx_st *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *plain, size_t plain_len,
uint8_t *cypher, size_t *cypher_len);
int aes_aead_dec(EVP_AEAD_CTX *key,
int aes_aead_dec(struct evp_aead_ctx_st *key,
const uint8_t *ad, size_t ad_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *cypher, size_t cypher_len,
uint8_t *plain, size_t *plain_len);
int aes_get_key_length();
void gen_nonce_s(char *buf, int length);
/* 32 bytes client nonce with 4 bytes tm, 8 bytes orbit */
void gen_nonce_c(unsigned char *buf, uint64_t orbit);
EVP_PKEY *PEM_to_key(const char *buf, int len);
struct evp_pkey_st *PEM_to_key(const char *buf, int len);
X509 *bio_to_crt(const void *buf, int len, int type);
struct x509_st *bio_to_crt(const void *buf, int len, int type);
int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_len,
uint16_t c_key_len, uint8_t *c_key,
@ -108,13 +94,13 @@ void lshkdf_extract(const unsigned char *ikm, int ikm_len, const unsigned char *
int gen_prof(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *priv_key, uint8_t *buf, size_t *len);
const struct evp_pkey_st *priv_key, uint8_t *buf, size_t *len);
int verify_prof0(const uint8_t *chlo_data, size_t chlo_data_len,
const uint8_t *scfg_data, uint32_t scfg_data_len,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len);
const struct evp_pkey_st *pub_key, const uint8_t *buf, size_t len);
int verify_prof(const uint8_t *chlo_data, size_t chlo_data_len, struct lsquic_str * scfg,
const EVP_PKEY *pub_key, const uint8_t *buf, size_t len);
const struct evp_pkey_st *pub_key, const uint8_t *buf, size_t len);
#ifdef __cplusplus

View File

@ -8,17 +8,29 @@
#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_cubic.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_cubic.h"
#define LSQUIC_LOGGER_MODULE LSQLM_CUBIC
#define LSQUIC_LOG_CONN_ID cubic->cu_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(cubic->cu_conn)
#include "lsquic_logger.h"
#define FAST_CONVERGENCE 1
@ -31,7 +43,7 @@
static void
cubic_reset (struct lsquic_cubic *cubic)
{
memset(cubic, 0, offsetof(struct lsquic_cubic, cu_cid));
memset(cubic, 0, offsetof(struct lsquic_cubic, cu_conn));
cubic->cu_cwnd = 32 * TCP_MSS;
cubic->cu_last_max_cwnd = 32 * TCP_MSS;
cubic->cu_tcp_cwnd = 32 * TCP_MSS;
@ -95,13 +107,23 @@ cubic_update (struct lsquic_cubic *cubic, lsquic_time_t now, unsigned n_bytes)
void
lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
enum cubic_flags flags)
lsquic_cubic_set_flags (struct lsquic_cubic *cubic, enum cubic_flags flags)
{
LSQ_DEBUG("%s(cubic, 0x%X)", __func__, flags);
cubic->cu_flags = flags;
}
static void
lsquic_cubic_init (void *cong_ctl, const struct lsquic_conn_public *conn_pub,
enum quic_ft_bit UNUSED_retx_frames)
{
struct lsquic_cubic *const cubic = cong_ctl;
cubic_reset(cubic);
cubic->cu_ssthresh = 10000 * TCP_MSS; /* Emulate "unbounded" slow start */
cubic->cu_cid = cid;
cubic->cu_flags = flags;
cubic->cu_conn = conn_pub->lconn;
cubic->cu_rtt_stats = &conn_pub->rtt_stats;
cubic->cu_flags = DEFAULT_CUBIC_FLAGS;
#ifndef NDEBUG
const char *s;
s = getenv("LSQUIC_CUBIC_SAMPLING_RATE");
@ -110,7 +132,7 @@ lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
else
#endif
cubic->cu_sampling_rate = 100000;
LSQ_DEBUG("%s(cubic, %"PRIu64", 0x%X)", __func__, cid, flags);
LSQ_DEBUG("%s(cubic, $conn)", __func__);
LSQ_INFO("initialized");
}
@ -127,18 +149,23 @@ lsquic_cubic_init_ext (struct lsquic_cubic *cubic, lsquic_cid_t cid,
} while (0)
void
lsquic_cubic_was_quiet (struct lsquic_cubic *cubic, lsquic_time_t now)
static void
lsquic_cubic_was_quiet (void *cong_ctl, lsquic_time_t now, uint64_t in_flight)
{
struct lsquic_cubic *const cubic = cong_ctl;
LSQ_DEBUG("%s(cubic, %"PRIu64")", __func__, now);
cubic->cu_epoch_start = 0;
}
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now_time,
lsquic_time_t rtt, int app_limited, unsigned n_bytes)
static void
lsquic_cubic_ack (void *cong_ctl, struct lsquic_packet_out *packet_out,
unsigned n_bytes, lsquic_time_t now_time, int app_limited)
{
struct lsquic_cubic *const cubic = cong_ctl;
lsquic_time_t rtt;
rtt = now_time - packet_out->po_sent;
LSQ_DEBUG("%s(cubic, %"PRIu64", %"PRIu64", %d, %u)", __func__, now_time, rtt,
app_limited, n_bytes);
if (0 == cubic->cu_min_delay || rtt < cubic->cu_min_delay)
@ -162,9 +189,10 @@ lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now_time,
}
void
lsquic_cubic_loss (struct lsquic_cubic *cubic)
static void
lsquic_cubic_loss (void *cong_ctl)
{
struct lsquic_cubic *const cubic = cong_ctl;
LSQ_DEBUG("%s(cubic)", __func__);
cubic->cu_epoch_start = 0;
if (FAST_CONVERGENCE && cubic->cu_cwnd < cubic->cu_last_max_cwnd)
@ -180,9 +208,10 @@ lsquic_cubic_loss (struct lsquic_cubic *cubic)
}
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic)
static void
lsquic_cubic_timeout (void *cong_ctl)
{
struct lsquic_cubic *const cubic = cong_ctl;
unsigned long cwnd;
cwnd = cubic->cu_cwnd;
@ -194,3 +223,61 @@ lsquic_cubic_timeout (struct lsquic_cubic *cubic)
LSQ_INFO("timeout, cwnd: %lu", cubic->cu_cwnd);
LOG_CWND(cubic);
}
static void
lsquic_cubic_cleanup (void *cong_ctl)
{
}
static uint64_t
lsquic_cubic_get_cwnd (void *cong_ctl)
{
struct lsquic_cubic *const cubic = cong_ctl;
return cubic->cu_cwnd;
}
static int
in_slow_start (void *cong_ctl)
{
struct lsquic_cubic *const cubic = cong_ctl;
return cubic->cu_cwnd < cubic->cu_ssthresh;
}
static uint64_t
lsquic_cubic_pacing_rate (void *cong_ctl, int in_recovery)
{
struct lsquic_cubic *const cubic = cong_ctl;
uint64_t bandwidth, pacing_rate;
lsquic_time_t srtt;
srtt = lsquic_rtt_stats_get_srtt(cubic->cu_rtt_stats);
if (srtt == 0)
srtt = 50000;
bandwidth = cubic->cu_cwnd * 1000000 / srtt;
if (in_slow_start(cubic))
pacing_rate = bandwidth * 2;
else if (in_recovery)
pacing_rate = bandwidth;
else
pacing_rate = bandwidth + bandwidth / 4;
return pacing_rate;
}
const struct cong_ctl_if lsquic_cong_cubic_if =
{
.cci_ack = lsquic_cubic_ack,
.cci_cleanup = lsquic_cubic_cleanup,
.cci_get_cwnd = lsquic_cubic_get_cwnd,
.cci_init = lsquic_cubic_init,
.cci_pacing_rate = lsquic_cubic_pacing_rate,
.cci_loss = lsquic_cubic_loss,
.cci_timeout = lsquic_cubic_timeout,
.cci_was_quiet = lsquic_cubic_was_quiet,
};

View File

@ -6,6 +6,8 @@
#ifndef LSQUIC_CUBIC_H
#define LSQUIC_CUBIC_H 1
struct lsquic_conn;
struct lsquic_cubic {
lsquic_time_t cu_min_delay;
lsquic_time_t cu_epoch_start;
@ -15,7 +17,10 @@ struct lsquic_cubic {
unsigned long cu_cwnd;
unsigned long cu_tcp_cwnd;
unsigned long cu_ssthresh;
lsquic_cid_t cu_cid; /* Used for logging */
const struct lsquic_conn
*cu_conn; /* Used for logging */
const struct lsquic_rtt_stats
*cu_rtt_stats;
enum cubic_flags {
CU_TCP_FRIENDLY = (1 << 0),
} cu_flags;
@ -27,28 +32,9 @@ struct lsquic_cubic {
#define TCP_MSS 1460
void
lsquic_cubic_init_ext (struct lsquic_cubic *, lsquic_cid_t, enum cubic_flags);
#define lsquic_cubic_init(cubic, cid) \
lsquic_cubic_init_ext(cubic, cid, DEFAULT_CUBIC_FLAGS)
extern const struct cong_ctl_if lsquic_cong_cubic_if;
void
lsquic_cubic_ack (struct lsquic_cubic *cubic, lsquic_time_t now,
lsquic_time_t rtt, int app_limited, unsigned n_bytes);
void
lsquic_cubic_loss (struct lsquic_cubic *cubic);
void
lsquic_cubic_timeout (struct lsquic_cubic *cubic);
void
lsquic_cubic_was_quiet (struct lsquic_cubic *, lsquic_time_t now);
#define lsquic_cubic_get_cwnd(c) (+(c)->cu_cwnd)
#define lsquic_cubic_in_slow_start(cubic) \
((cubic)->cu_cwnd < (cubic)->cu_ssthresh)
lsquic_cubic_set_flags (struct lsquic_cubic *cubic, enum cubic_flags flags);
#endif

View File

@ -6,11 +6,13 @@
#ifndef LSQUIC_DATA_IN_IF_H
#define LSQUIC_DATA_IN_IF_H 1
struct data_frame;
struct data_in;
struct lsquic_conn_public;
struct stream_frame;
enum ins_frame
{
INS_FRAME_OK,
@ -19,6 +21,7 @@ enum ins_frame
INS_FRAME_OVERLAP,
};
struct data_in_iface
{
void
@ -55,8 +58,15 @@ struct data_in_iface
size_t
(*di_mem_used) (struct data_in *);
void
(*di_dump_state) (struct data_in *);
/* Return number of bytes readable starting at offset `read_offset' */
uint64_t
(*di_readable_bytes) (struct data_in *, uint64_t read_offset);
};
struct data_in
{
const struct data_in_iface *di_if;
@ -70,17 +80,18 @@ struct data_in
} di_flags;
};
/* This implementation does not support overlapping frame and may return
* INS_FRAME_OVERLAP.
*/
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *, uint32_t stream_id);
data_in_nocopy_new (struct lsquic_conn_public *, lsquic_stream_id_t);
/* This implementation supports overlapping frames and will never return
* INS_FRAME_OVERLAP.
*/
struct data_in *
data_in_hash_new (struct lsquic_conn_public *, uint32_t stream_id,
data_in_hash_new (struct lsquic_conn_public *, lsquic_stream_id_t,
uint64_t byteage);
enum ins_frame

View File

@ -13,6 +13,7 @@
#include <vc_compat.h>
#endif
#include "lsquic_types.h"
#include "lsquic_data_in_if.h"
@ -75,13 +76,28 @@ error_di_mem_used (struct data_in *data_in)
}
static void
error_di_dump_state (struct data_in *data_in)
{
}
static uint64_t
error_di_readable_bytes (struct data_in *data_in, uint64_t read_offset)
{
return 0;
}
static const struct data_in_iface di_if_error = {
.di_destroy = error_di_destroy,
.di_dump_state = error_di_dump_state,
.di_empty = error_di_empty,
.di_frame_done = error_di_frame_done,
.di_get_frame = error_di_get_frame,
.di_insert_frame = error_di_insert_frame,
.di_mem_used = error_di_mem_used,
.di_readable_bytes
= error_di_readable_bytes,
.di_switch_impl = error_di_switch_impl,
};

View File

@ -27,6 +27,10 @@
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_varint.h"
#include "lsquic_hash.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
@ -36,7 +40,7 @@
#define LSQUIC_LOGGER_MODULE LSQLM_DI
#define LSQUIC_LOG_CONN_ID hdi->hdi_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(hdi->hdi_conn_pub->lconn)
#define LSQUIC_LOG_STREAM_ID hdi->hdi_stream_id
#include "lsquic_logger.h"
@ -73,7 +77,7 @@ struct hash_data_in
struct dblock_head *hdi_buckets;
struct data_block *hdi_last_block;
struct data_frame hdi_data_frame;
uint32_t hdi_stream_id;
lsquic_stream_id_t hdi_stream_id;
unsigned hdi_count;
unsigned hdi_nbits;
enum {
@ -111,8 +115,8 @@ my_log2 /* silly name to suppress compiler warning */ (unsigned sz)
struct data_in *
data_in_hash_new (struct lsquic_conn_public *conn_pub, uint32_t stream_id,
uint64_t byteage)
data_in_hash_new (struct lsquic_conn_public *conn_pub,
lsquic_stream_id_t stream_id, uint64_t byteage)
{
struct hash_data_in *hdi;
unsigned n;
@ -456,8 +460,6 @@ ctz (unsigned long long x)
if (0 == (x & ((1ULL << 1) - 1))) { n += 1; x >>= 1; }
return n;
}
#endif
@ -638,13 +640,46 @@ hash_di_mem_used (struct data_in *data_in)
}
static void
hash_di_dump_state (struct data_in *data_in)
{
const struct hash_data_in *const hdi = HDI_PTR(data_in);
const struct data_block *block;
unsigned n;
LSQ_DEBUG("hash state: flags: %X; fin off: %"PRIu64"; count: %u",
hdi->hdi_flags, hdi->hdi_fin_off, hdi->hdi_count);
for (n = 0; n < N_BUCKETS(hdi->hdi_nbits); ++n)
TAILQ_FOREACH(block, &hdi->hdi_buckets[n], db_next)
LSQ_DEBUG("block: off: %"PRIu64, block->db_off);
}
static uint64_t
hash_di_readable_bytes (struct data_in *data_in, uint64_t read_offset)
{
const struct data_frame *data_frame;
uint64_t starting_offset;
starting_offset = read_offset;
while (data_frame = hash_di_get_frame(data_in, read_offset),
data_frame && data_frame->df_size - data_frame->df_read_off)
read_offset += data_frame->df_size - data_frame->df_read_off;
return read_offset - starting_offset;
}
static const struct data_in_iface di_if_hash = {
.di_destroy = hash_di_destroy,
.di_dump_state = hash_di_dump_state,
.di_empty = hash_di_empty,
.di_frame_done = hash_di_frame_done,
.di_get_frame = hash_di_get_frame,
.di_insert_frame = hash_di_insert_frame,
.di_mem_used = hash_di_mem_used,
.di_readable_bytes
= hash_di_readable_bytes,
.di_switch_impl = hash_di_switch_impl,
};

View File

@ -57,6 +57,9 @@
#include "lsquic_packet_in.h"
#include "lsquic_rtt.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
@ -66,7 +69,7 @@
#define LSQUIC_LOGGER_MODULE LSQLM_DI
#define LSQUIC_LOG_CONN_ID ncdi->ncdi_conn_pub->lconn->cn_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(ncdi->ncdi_conn_pub->lconn)
#define LSQUIC_LOG_STREAM_ID ncdi->ncdi_stream_id
#include "lsquic_logger.h"
@ -103,7 +106,7 @@ struct nocopy_data_in
struct lsquic_conn_public *ncdi_conn_pub;
uint64_t ncdi_byteage;
uint64_t ncdi_fin_off;
uint32_t ncdi_stream_id;
lsquic_stream_id_t ncdi_stream_id;
unsigned ncdi_n_frames;
unsigned ncdi_n_holes;
unsigned ncdi_cons_far;
@ -125,7 +128,8 @@ static const struct data_in_iface *di_if_nocopy_ptr;
struct data_in *
data_in_nocopy_new (struct lsquic_conn_public *conn_pub, uint32_t stream_id)
data_in_nocopy_new (struct lsquic_conn_public *conn_pub,
lsquic_stream_id_t stream_id)
{
struct nocopy_data_in *ncdi;
@ -164,12 +168,6 @@ nocopy_di_destroy (struct data_in *data_in)
}
#define DF_OFF(frame) (frame)->data_frame.df_offset
#define DF_FIN(frame) (frame)->data_frame.df_fin
#define DF_SIZE(frame) (frame)->data_frame.df_size
#define DF_END(frame) (DF_OFF(frame) + DF_SIZE(frame))
#if LSQUIC_EXTRA_CHECKS
static int
frame_list_is_sane (const struct nocopy_data_in *ncdi)
@ -186,8 +184,6 @@ frame_list_is_sane (const struct nocopy_data_in *ncdi)
}
return ordered && !overlaps;
}
#define CHECK_ORDER(ncdi) assert(frame_list_is_sane(ncdi))
#else
#define CHECK_ORDER(ncdi)
@ -280,6 +276,8 @@ insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
&& read_offset == ncdi->ncdi_fin_off))
return INS_FRAME_DUP | CASE('G');
}
else if (read_offset > DF_OFF(new_frame))
return INS_FRAME_OVERLAP | CASE('N');
goto list_was_empty;
case 3: /* Both left and right neighbors */
case 2: /* Only left neighbor (prev_frame) */
@ -299,6 +297,8 @@ insert_frame (struct nocopy_data_in *ncdi, struct stream_frame *new_frame,
case 1: /* Only right neighbor (next_frame) */
if (DF_END(new_frame) > DF_OFF(next_frame))
return INS_FRAME_OVERLAP | CASE('K');
else if (read_offset > DF_OFF(new_frame))
return INS_FRAME_OVERLAP | CASE('O');
break;
}
@ -512,13 +512,47 @@ nocopy_di_mem_used (struct data_in *data_in)
}
static void
nocopy_di_dump_state (struct data_in *data_in)
{
struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
const struct stream_frame *frame;
LSQ_DEBUG("nocopy state: frames: %u; holes: %u; cons_far: %u",
ncdi->ncdi_n_frames, ncdi->ncdi_n_holes, ncdi->ncdi_cons_far);
TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame)
LSQ_DEBUG("frame: off: %"PRIu64"; read_off: %"PRIu16"; size: %"PRIu16
"; fin: %d", DF_OFF(frame), frame->data_frame.df_read_off,
DF_SIZE(frame), DF_FIN(frame));
}
static uint64_t
nocopy_di_readable_bytes (struct data_in *data_in, uint64_t read_offset)
{
const struct nocopy_data_in *const ncdi = NCDI_PTR(data_in);
const struct stream_frame *frame;
uint64_t starting_offset;
starting_offset = read_offset;
TAILQ_FOREACH(frame, &ncdi->ncdi_frames_in, next_frame)
if (DF_ROFF(frame) == read_offset)
read_offset += DF_END(frame) - DF_ROFF(frame);
return read_offset - starting_offset;
}
static const struct data_in_iface di_if_nocopy = {
.di_destroy = nocopy_di_destroy,
.di_dump_state = nocopy_di_dump_state,
.di_empty = nocopy_di_empty,
.di_frame_done = nocopy_di_frame_done,
.di_get_frame = nocopy_di_get_frame,
.di_insert_frame = nocopy_di_insert_frame,
.di_mem_used = nocopy_di_mem_used,
.di_readable_bytes
= nocopy_di_readable_bytes,
.di_switch_impl = nocopy_di_switch_impl,
};

View File

@ -0,0 +1,348 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_ENC_SESS_H
#define LSQUIC_ENC_SESS_H 1
struct lsquic_engine_public;
struct lsquic_packet_out;
struct lsquic_packet_in;
struct stream_wrapper;
struct ver_neg;
struct lsquic_conn;
struct transport_params;
struct lsquic_cid;
struct ssl_stream_method_st;
struct ssl_st;
struct sockaddr;
struct conn_cid_elem;
#define DNONC_LENGTH 32
#define SRST_LENGTH 16
/* From [draft-ietf-quic-tls-14]:
*
* Data is protected using a number of encryption levels:
*
* o Plaintext
*
* o Early Data (0-RTT) Keys
*
* o Handshake Keys
*
* o Application Data (1-RTT) Keys
*/
/* This enum maps to the list above */
enum enc_level
{
ENC_LEV_CLEAR,
ENC_LEV_EARLY,
ENC_LEV_INIT,
ENC_LEV_FORW,
N_ENC_LEVS
};
enum handshake_error /* TODO: rename this enum */
{
DATA_NOT_ENOUGH = -2,
DATA_FORMAT_ERROR = -1,
HS_ERROR = -1,
DATA_NO_ERROR = 0,
HS_SHLO = 0,
HS_1RTT = 1,
HS_SREJ = 2,
HS_DELAYED = 3,
HS_PK_OFFLOAD = 4,
};
#ifndef LSQUIC_KEEP_ENC_SESS_HISTORY
# ifndef NDEBUG
# define LSQUIC_KEEP_ENC_SESS_HISTORY 1
# else
# define LSQUIC_KEEP_ENC_SESS_HISTORY 0
# endif
#endif
#if LSQUIC_KEEP_ENC_SESS_HISTORY
#define ESHIST_BITS 7
#define ESHIST_MASK ((1 << ESHIST_BITS) - 1)
#define ESHIST_STR_SIZE ((1 << ESHIST_BITS) + 1)
#endif
enum enc_packout { ENCPA_OK, ENCPA_NOMEM, ENCPA_BADCRYPT, };
enum dec_packin {
DECPI_OK,
DECPI_NOMEM,
DECPI_TOO_SHORT,
DECPI_NOT_YET,
DECPI_BADCRYPT,
DECPI_VIOLATION,
};
typedef void enc_session_t;
struct enc_session_funcs_common
{
/* Global initialization: call once per implementation */
int (*esf_global_init)(int flags);
/* Global cleanup: call once per implementation */
void (*esf_global_cleanup) (void);
const char *
(*esf_cipher) (enc_session_t *);
int
(*esf_keysize) (enc_session_t *);
int
(*esf_alg_keysize) (enc_session_t *);
const char *
(*esf_get_sni) (enc_session_t *);
enum enc_packout
(*esf_encrypt_packet) (enc_session_t *, const struct lsquic_engine_public *,
struct lsquic_conn *, struct lsquic_packet_out *);
enum dec_packin
(*esf_decrypt_packet)(enc_session_t *, struct lsquic_engine_public *,
const struct lsquic_conn *, struct lsquic_packet_in *);
struct stack_st_X509 *
(*esf_get_server_cert_chain) (enc_session_t *);
int
(*esf_verify_reset_token) (enc_session_t *, const unsigned char *, size_t);
int
(*esf_did_zero_rtt_succeed) (enc_session_t *);
int
(*esf_is_zero_rtt_enabled) (enc_session_t *);
unsigned
esf_tag_len;
};
struct enc_session_funcs_gquic
{
#if LSQUIC_KEEP_ENC_SESS_HISTORY
/* Grab encryption session history */
void (*esf_get_hist) (enc_session_t *,
char buf[ESHIST_STR_SIZE]);
#endif
/* Destroy enc session */
void (*esf_destroy)(enc_session_t *enc_session);
/* Return true if handshake has been completed */
int (*esf_is_hsk_done)(enc_session_t *enc_session);
/* Get value of setting specified by `tag' */
int (*esf_get_peer_setting) (enc_session_t *, uint32_t tag,
uint32_t *val);
/* Get value of peer option (that from COPT array) */
int (*esf_get_peer_option) (enc_session_t *enc_session,
uint32_t tag);
/* Create server session */
enc_session_t *
(*esf_create_server) (lsquic_cid_t cid, const struct lsquic_engine_public *);
/* out_len should have init value as the max length of out */
enum handshake_error
(*esf_handle_chlo) (enc_session_t *enc_session, enum lsquic_version,
const uint8_t *in, int in_len, time_t t,
const struct sockaddr *ip_addr, const struct sockaddr *local,
uint8_t *out, size_t *out_len,
uint8_t nonce[DNONC_LENGTH], int *nonce_set);
void (*esf_hsk_destroy)(void *hsk_ctx);
#ifndef NDEBUG
/* Need to expose this function for testing */
int (*esf_determine_diversification_key) (enc_session_t *,
uint8_t *diversification_nonce, int is_client);
#endif
const char *
(*esf_get_ua) (enc_session_t *);
int
(*esf_have_key_gt_one) (enc_session_t *enc_session);
#ifndef NDEBUG
/* Functions that are only relevant in maintest. We may want to get rid
* of them somehow and only use the public API to test.
*/
uint8_t
(*esf_have_key) (enc_session_t *);
void
(*esf_set_have_key) (enc_session_t *, uint8_t);
const unsigned char *
(*esf_get_enc_key_i) (enc_session_t *);
const unsigned char *
(*esf_get_dec_key_i) (enc_session_t *);
const unsigned char *
(*esf_get_enc_key_nonce_i) (enc_session_t *);
const unsigned char *
(*esf_get_dec_key_nonce_i) (enc_session_t *);
const unsigned char *
(*esf_get_enc_key_nonce_f) (enc_session_t *);
const unsigned char *
(*esf_get_dec_key_nonce_f) (enc_session_t *);
#endif /* !defined(NDEBUG) */
#if LSQUIC_ENABLE_HANDSHAKE_DISABLE
void
(*esf_set_handshake_completed) (enc_session_t *);
#endif
/* Create client session */
enc_session_t *
(*esf_create_client) (const char *domain, lsquic_cid_t cid,
const struct lsquic_engine_public *,
const unsigned char *, size_t);
/* -1 error, 0, OK, response in `buf' */
int
(*esf_gen_chlo) (enc_session_t *, enum lsquic_version,
uint8_t *buf, size_t *len);
int
(*esf_handle_chlo_reply) (enc_session_t *,
const uint8_t *data, int len);
size_t
(*esf_mem_used)(enc_session_t *);
/* Zero-rtt serialization needs the knowledge of the QUIC version, that's
* why there is a separate method for thus. Plus, we want to be able to
* call it after the "handshake is done" callback is called.
*/
void (*esf_maybe_dispatch_zero_rtt) (enc_session_t *,
struct lsquic_conn *conn,
void (*cb)(struct lsquic_conn *, const unsigned char *, size_t));
void (*esf_reset_cid) (enc_session_t *, const lsquic_cid_t *);
};
enum iquic_handshake_status {
IHS_WANT_READ,
IHS_WANT_WRITE,
IHS_STOP,
};
struct crypto_stream_if
{
ssize_t (*csi_write) (void *stream, const void *buf, size_t len);
int (*csi_flush) (void *stream);
ssize_t (*csi_readf) (void *stream,
size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx);
int (*csi_wantwrite) (void *stream, int is_want);
int (*csi_wantread) (void *stream, int is_want);
enum enc_level
(*csi_enc_level) (void *stream);
};
struct enc_session_funcs_iquic
{
enc_session_t *
(*esfi_create_client) (const char *domain, struct lsquic_engine_public *,
struct lsquic_conn *, const struct lsquic_cid *,
const struct ver_neg *, void *(crypto_streams)[4],
const struct crypto_stream_if *,
const unsigned char *, size_t);
void
(*esfi_destroy) (enc_session_t *);
struct ssl_st *
(*esfi_get_ssl) (enc_session_t *);
struct transport_params *
(*esfi_get_peer_transport_params) (enc_session_t *);
int
(*esfi_reset_dcid) (enc_session_t *, const struct lsquic_cid *,
const struct lsquic_cid *);
int
(*esfi_init_server) (enc_session_t *);
void
(*esfi_set_conn) (enc_session_t *, struct lsquic_conn *);
void
(*esfi_set_streams) (enc_session_t *, void *(crypto_streams)[4],
const struct crypto_stream_if *);
enc_session_t *
(*esfi_create_server) (struct lsquic_engine_public *, struct lsquic_conn *,
const struct lsquic_cid *,
void *(crypto_streams)[4],
const struct crypto_stream_if *,
const struct lsquic_cid *odcid);
void
(*esfi_shake_stream)(enc_session_t *, struct lsquic_stream *,
const char *);
void
(*esfi_1rtt_acked)(enc_session_t *);
};
extern
#ifdef NDEBUG
const
#endif
struct enc_session_funcs_common lsquic_enc_session_common_gquic_1;
extern const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1;
extern
#ifdef NDEBUG
const
#endif
struct enc_session_funcs_gquic lsquic_enc_session_gquic_gquic_1;
extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
#define select_esf_common_by_ver(ver) ( \
ver == LSQVER_ID22 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
&lsquic_enc_session_common_gquic_1 )
#define select_esf_gquic_by_ver(ver) ( \
ver ? &lsquic_enc_session_gquic_gquic_1 : &lsquic_enc_session_gquic_gquic_1)
#define select_esf_iquic_by_ver(ver) ( \
ver ? &lsquic_enc_session_iquic_ietf_v1 : &lsquic_enc_session_iquic_ietf_v1)
extern const char *const lsquic_enclev2str[];
extern const struct lsquic_stream_if lsquic_cry_sm_if;
extern const struct lsquic_stream_if lsquic_mini_cry_sm_if;
/* RFC 7301, Section 3.2 */
#define ALERT_NO_APPLICATION_PROTOCOL 120
enum lsquic_version
lsquic_zero_rtt_version (const unsigned char *, size_t);
/* This is seems to be true for all of the ciphers used by IETF QUIC.
* XXX: Perhaps add a check?
*/
#define IQUIC_TAG_LEN 16
#endif

View File

@ -0,0 +1,34 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_enc_sess.h"
#include "lsquic_version.h"
const char *const lsquic_enclev2str[] =
{
[ENC_LEV_EARLY] = "early",
[ENC_LEV_CLEAR] = "clear",
[ENC_LEV_INIT] = "initial",
[ENC_LEV_FORW] = "forw-secure",
};
enum lsquic_version
lsquic_zero_rtt_version (const unsigned char *buf, size_t bufsz)
{
lsquic_ver_tag_t tag;
if (bufsz >= sizeof(tag))
{
memcpy(&tag, buf, sizeof(tag));
return lsquic_tag2ver(tag);
}
else
return -1;
}

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#if ENG_HIST_ENABLED
#define LSQUIC_LOGGER_MODULE LSQLM_ENG_HIST
#include "lsquic_types.h"
#include "lsquic_logger.h"

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,9 @@
/*
* lsquic_engine_public.h -- Engine's "public interface"
*
* This structure is used to bundle things in engine that connections
* need. This way, the space per mini connection is one pointer instead
* of several.
*/
#ifndef LSQUIC_ENGINE_PUBLIC_H
@ -10,10 +13,32 @@
struct lsquic_conn;
struct lsquic_engine;
struct stack_st_X509;
struct lsquic_hash;
struct lsquic_stream_if;
struct ssl_ctx_st;
enum warning_type
{
WT_ACKPARSE_MINI,
WT_ACKPARSE_FULL,
N_WARNING_TYPES,
};
#define WARNING_INTERVAL (24ULL * 3600ULL * 1000000ULL)
struct lsquic_engine_public {
struct lsquic_mm enp_mm;
struct lsquic_engine_settings enp_settings;
struct token_generator *enp_tokgen;
lsquic_lookup_cert_f enp_lookup_cert;
void *enp_cert_lu_ctx;
struct ssl_ctx_st * (*enp_get_ssl_ctx)(void *peer_ctx);
const struct lsquic_shared_hash_if
*enp_shi;
void *enp_shi_ctx;
lsquic_time_t enp_last_warning[N_WARNING_TYPES];
const struct lsquic_stream_if *enp_stream_if;
void *enp_stream_if_ctx;
const struct lsquic_hset_if *enp_hsi_if;
void *enp_hsi_ctx;
int (*enp_verify_cert)(void *verify_ctx,
@ -22,13 +47,18 @@ struct lsquic_engine_public {
const struct lsquic_packout_mem_if
*enp_pmi;
void *enp_pmi_ctx;
const struct lsquic_keylog_if *enp_kli;
void *enp_kli_ctx;
struct lsquic_engine *enp_engine;
struct lsquic_hash *enp_srst_hash;
enum {
ENPUB_PROC = (1 << 0), /* Being processed by one of the user-facing
* functions.
*/
ENPUB_CAN_SEND = (1 << 1),
} enp_flags;
unsigned char enp_ver_tags_buf[ sizeof(lsquic_ver_tag_t) * N_LSQVER ];
unsigned enp_ver_tags_len;
};
/* Put connection onto the Tickable Queue if it is not already on it. If
@ -44,4 +74,16 @@ void
lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
lsquic_conn_t *, lsquic_time_t);
void
lsquic_engine_retire_cid (struct lsquic_engine_public *,
struct lsquic_conn *, unsigned cce_idx, lsquic_time_t now);
int
lsquic_engine_add_cid (struct lsquic_engine_public *,
struct lsquic_conn *, unsigned cce_idx);
struct lsquic_conn *
lsquic_engine_find_conn (const struct lsquic_engine_public *pub,
const lsquic_cid_t *cid);
#endif

View File

@ -14,14 +14,21 @@
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_frame_common.h"
#include "lsquic_headers.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h"
#include "lsquic_frame_reader.h"
#include "lsquic_enc_sess.h"
#include "lsquic_ev_log.h"
#include "lsquic_sizes.h"
#include "lsquic_trans_params.h"
#include "lsquic_util.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_EVENT
#include "lsquic_logger.h"
@ -39,24 +46,41 @@
/* VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV */
void
lsquic_ev_log_packet_in (lsquic_cid_t cid, const lsquic_packet_in_t *packet_in)
lsquic_ev_log_packet_in (const lsquic_cid_t *cid,
const lsquic_packet_in_t *packet_in)
{
switch (packet_in->pi_flags & (
PI_FROM_MINI|
PI_GQUIC))
{
case PI_FROM_MINI|PI_GQUIC:
LCID("packet in: %"PRIu64" (from mini)", packet_in->pi_packno);
break;
case PI_FROM_MINI:
LCID("packet in: %"PRIu64" (from mini), type: %s, ecn: %u",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
lsquic_packet_in_ecn(packet_in));
break;
case PI_GQUIC:
LCID("packet in: %"PRIu64, packet_in->pi_packno);
LCID("packet in: %"PRIu64", size: %u", packet_in->pi_packno,
(unsigned) (packet_in->pi_data_sz + GQUIC_PACKET_HASH_SZ));
break;
default:
LCID("packet in: %"PRIu64", type: %s",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type]);
LCID("packet in: %"PRIu64", type: %s, size: %u; ecn: %u, spin: %d; "
"path: %hhu",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
(unsigned) (packet_in->pi_data_sz + IQUIC_TAG_LEN),
lsquic_packet_in_ecn(packet_in),
/* spin bit value is only valid for short packet headers */
lsquic_packet_in_spin_bit(packet_in), packet_in->pi_path_id);
break;
}
}
void
lsquic_ev_log_ack_frame_in (lsquic_cid_t cid, const struct ack_info *acki)
lsquic_ev_log_ack_frame_in (const lsquic_cid_t *cid,
const struct ack_info *acki)
{
size_t sz;
char *buf;
@ -70,81 +94,101 @@ lsquic_ev_log_ack_frame_in (lsquic_cid_t cid, const struct ack_info *acki)
void
lsquic_ev_log_stream_frame_in (lsquic_cid_t cid,
lsquic_ev_log_stream_frame_in (const lsquic_cid_t *cid,
const struct stream_frame *frame)
{
LCID("STREAM frame in: stream %u; offset %"PRIu64"; size %"PRIu16
LCID("STREAM frame in: stream %"PRIu64"; offset %"PRIu64"; size %"PRIu16
"; fin: %d", frame->stream_id, frame->data_frame.df_offset,
frame->data_frame.df_size, (int) frame->data_frame.df_fin);
}
void
lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t cid, lsquic_packno_t least)
lsquic_ev_log_crypto_frame_in (const lsquic_cid_t *cid,
const struct stream_frame *frame, unsigned enc_level)
{
LCID("CRYPTO frame in: level %u; offset %"PRIu64"; size %"PRIu16,
enc_level, frame->data_frame.df_offset, frame->data_frame.df_size);
}
void
lsquic_ev_log_stop_waiting_frame_in (const lsquic_cid_t *cid,
lsquic_packno_t least)
{
LCID("STOP_WAITING frame in: least unacked packno %"PRIu64, least);
}
void
lsquic_ev_log_window_update_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset)
lsquic_ev_log_window_update_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint64_t offset)
{
LCID("WINDOW_UPDATE frame in: stream %"PRIu32"; offset %"PRIu64,
LCID("WINDOW_UPDATE frame in: stream %"PRIu64"; offset %"PRIu64,
stream_id, offset);
}
void
lsquic_ev_log_blocked_frame_in (lsquic_cid_t cid, uint32_t stream_id)
lsquic_ev_log_blocked_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id)
{
LCID("BLOCKED frame in: stream %"PRIu32, stream_id);
LCID("BLOCKED frame in: stream %"PRIu64, stream_id);
}
void
lsquic_ev_log_connection_close_frame_in (lsquic_cid_t cid,
uint32_t error_code, int reason_len, const char *reason)
lsquic_ev_log_connection_close_frame_in (const lsquic_cid_t *cid,
uint64_t error_code, int reason_len, const char *reason)
{
LCID("CONNECTION_CLOSE frame in: error code %"PRIu32", reason: %.*s",
LCID("CONNECTION_CLOSE frame in: error code %"PRIu64", reason: %.*s",
error_code, reason_len, reason);
}
void
lsquic_ev_log_goaway_frame_in (lsquic_cid_t cid, uint32_t error_code,
uint32_t stream_id, int reason_len, const char *reason)
lsquic_ev_log_goaway_frame_in (const lsquic_cid_t *cid, uint32_t error_code,
lsquic_stream_id_t stream_id, int reason_len, const char *reason)
{
LCID("GOAWAY frame in: error code %"PRIu32", stream %"PRIu32
LCID("GOAWAY frame in: error code %"PRIu32", stream %"PRIu64
", reason: %.*s", error_code, stream_id, reason_len, reason);
}
void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset, uint32_t error_code)
lsquic_ev_log_rst_stream_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint64_t offset, uint64_t error_code)
{
LCID("RST_STREAM frame in: error code %"PRIu32", stream %"PRIu32
LCID("RST_STREAM frame in: error code %"PRIu64", stream %"PRIu64
", offset: %"PRIu64, error_code, stream_id, offset);
}
void
lsquic_ev_log_padding_frame_in (lsquic_cid_t cid, size_t len)
lsquic_ev_log_stop_sending_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint64_t error_code)
{
LCID("STOP_SENDING frame in: error code %"PRIu64", stream %"PRIu64,
error_code, stream_id);
}
void
lsquic_ev_log_padding_frame_in (const lsquic_cid_t *cid, size_t len)
{
LCID("PADDING frame in of %zd bytes", len);
}
void
lsquic_ev_log_ping_frame_in (lsquic_cid_t cid)
lsquic_ev_log_ping_frame_in (const lsquic_cid_t *cid)
{
LCID("PING frame in");
}
void
lsquic_ev_log_packet_created (lsquic_cid_t cid,
lsquic_ev_log_packet_created (const lsquic_cid_t *cid,
const struct lsquic_packet_out *packet_out)
{
LCID("created packet %"PRIu64"; flags: version=%d, nonce=%d, conn_id=%d",
@ -156,16 +200,18 @@ lsquic_ev_log_packet_created (lsquic_cid_t cid,
void
lsquic_ev_log_packet_sent (lsquic_cid_t cid,
lsquic_ev_log_packet_sent (const lsquic_cid_t *cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
if (lsquic_packet_out_verneg(packet_out))
LCID("sent version negotiation packet, size %hu",
packet_out->po_data_sz);
else if (lsquic_packet_out_retry(packet_out))
LCID("sent stateless retry packet, size %hu", packet_out->po_data_sz);
else if (lsquic_packet_out_pubres(packet_out))
LCID("sent public reset packet, size %hu", packet_out->po_data_sz);
else if (packet_out->po_flags & PO_GQUIC)
else if (packet_out->po_lflags & POL_GQUIC)
LCID("sent packet %"PRIu64", size %hu, frame types: %s",
packet_out->po_packno, packet_out->po_enc_data_sz,
/* Frame types is a list of different frames types contained
@ -176,7 +222,7 @@ lsquic_ev_log_packet_sent (lsquic_cid_t cid,
packet_out->po_frame_types));
else
LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame "
"types: %s",
"types: %s, ecn: %u, spin: %d; kp: %u, path: %hhu",
packet_out->po_packno, lsquic_hety2str[packet_out->po_header_type],
lsquic_enclev2str[ lsquic_packet_out_enc_level(packet_out) ],
packet_out->po_enc_data_sz,
@ -185,12 +231,17 @@ lsquic_ev_log_packet_sent (lsquic_cid_t cid,
* printed.
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types));
packet_out->po_frame_types),
lsquic_packet_out_ecn(packet_out),
/* spin bit value is only valid for short packet headers */
lsquic_packet_out_spin_bit(packet_out),
lsquic_packet_out_kp(packet_out),
packet_out->po_path->np_path_id);
}
void
lsquic_ev_log_packet_not_sent (lsquic_cid_t cid,
lsquic_ev_log_packet_not_sent (const lsquic_cid_t *cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
@ -205,17 +256,17 @@ lsquic_ev_log_packet_not_sent (lsquic_cid_t cid,
void
lsquic_ev_log_http_headers_in (lsquic_cid_t cid, int is_server,
lsquic_ev_log_http_headers_in (const lsquic_cid_t *cid, int is_server,
const struct uncompressed_headers *uh)
{
const struct http1x_headers *h1h;
const char *cr, *p;
if (uh->uh_flags & UH_PP)
LCID("read push promise; stream %"PRIu32", promised stream %"PRIu32,
LCID("read push promise; stream %"PRIu64", promised stream %"PRIu64,
uh->uh_stream_id, uh->uh_oth_stream_id);
else
LCID("read %s headers; stream: %"PRIu32", depends on stream: %"PRIu32
LCID("read %s headers; stream: %"PRIu64", depends on stream: %"PRIu64
", weight: %hu, exclusive: %d, fin: %d",
is_server ? "request" : "response",
uh->uh_stream_id, uh->uh_oth_stream_id, uh->uh_weight,
@ -237,7 +288,7 @@ lsquic_ev_log_http_headers_in (lsquic_cid_t cid, int is_server,
void
lsquic_ev_log_action_stream_frame (lsquic_cid_t cid,
lsquic_ev_log_action_stream_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *buf, size_t bufsz,
const char *what)
{
@ -246,7 +297,7 @@ lsquic_ev_log_action_stream_frame (lsquic_cid_t cid,
len = pf->pf_parse_stream_frame(buf, bufsz, &frame);
if (len > 0)
LCID("%s STREAM frame: stream %"PRIu32", offset: %"PRIu64
LCID("%s STREAM frame: stream %"PRIu64", offset: %"PRIu64
", size: %"PRIu16", fin: %d", what, frame.stream_id,
frame.data_frame.df_offset, frame.data_frame.df_size,
frame.data_frame.df_fin);
@ -256,15 +307,33 @@ lsquic_ev_log_action_stream_frame (lsquic_cid_t cid,
void
lsquic_ev_log_generated_ack_frame (lsquic_cid_t cid, const struct parse_funcs *pf,
const unsigned char *ack_buf, size_t ack_buf_sz)
lsquic_ev_log_generated_crypto_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *buf, size_t bufsz)
{
struct stream_frame frame;
int len;
len = pf->pf_parse_crypto_frame(buf, bufsz, &frame);
if (len > 0)
LCID("generated CRYPTO frame: offset: %"PRIu64", size: %"PRIu16,
frame.data_frame.df_offset, frame.data_frame.df_size);
else
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse CRYPTO frame");
}
void
lsquic_ev_log_generated_ack_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *ack_buf,
size_t ack_buf_sz)
{
struct ack_info acki;
size_t sz;
char *buf;
int len;
len = pf->pf_parse_ack_frame(ack_buf, ack_buf_sz, &acki);
len = pf->pf_parse_ack_frame(ack_buf, ack_buf_sz, &acki,
TP_DEF_ACK_DELAY_EXP);
if (len < 0)
{
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse ACK frame");
@ -280,7 +349,98 @@ lsquic_ev_log_generated_ack_frame (lsquic_cid_t cid, const struct parse_funcs *p
void
lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t cid,
lsquic_ev_log_generated_new_token_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *frame_buf,
size_t frame_buf_sz)
{
const unsigned char *token;
size_t sz;
char *buf;
int len;
len = pf->pf_parse_new_token_frame(frame_buf, frame_buf_sz, &token, &sz);
if (len < 0)
{
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse NEW_TOKEN frame");
return;
}
buf = malloc(sz * 2 + 1);
if (buf)
{
lsquic_hexstr(token, sz, buf, sz * 2 + 1);
LCID("generated NEW_TOKEN frame: %s", buf);
free(buf);
}
}
void
lsquic_ev_log_generated_path_chal_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *frame_buf,
size_t frame_buf_sz)
{
uint64_t chal;
int len;
char hexbuf[sizeof(chal) * 2 + 1];
len = pf->pf_parse_path_chal_frame(frame_buf, frame_buf_sz, &chal);
if (len > 0)
LCID("generated PATH_CHALLENGE(%s) frame",
HEXSTR((unsigned char *) &chal, sizeof(chal), hexbuf));
else
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse PATH_CHALLENGE frame");
}
void
lsquic_ev_log_generated_path_resp_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *frame_buf,
size_t frame_buf_sz)
{
uint64_t resp;
int len;
char hexbuf[sizeof(resp) * 2 + 1];
len = pf->pf_parse_path_resp_frame(frame_buf, frame_buf_sz, &resp);
if (len > 0)
LCID("generated PATH_RESPONSE(%s) frame",
HEXSTR((unsigned char *) &resp, sizeof(resp), hexbuf));
else
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse PATH_RESPONSE frame");
}
void
lsquic_ev_log_generated_new_connection_id_frame (const lsquic_cid_t *cid,
const struct parse_funcs *pf, const unsigned char *frame_buf,
size_t frame_buf_sz)
{
const unsigned char *token;
lsquic_cid_t new_cid;
uint64_t seqno, retire_prior_to;
int len;
char token_buf[IQUIC_SRESET_TOKEN_SZ * 2 + 1];
char cid_buf[MAX_CID_LEN * 2 + 1];
len = pf->pf_parse_new_conn_id(frame_buf, frame_buf_sz, &seqno,
&retire_prior_to, &new_cid, &token);
if (len < 0)
{
LSQ_LOG2(LSQ_LOG_WARN, "cannot parse NEW_CONNECTION_ID frame");
return;
}
lsquic_hexstr(new_cid.idbuf, new_cid.len, cid_buf, sizeof(cid_buf));
lsquic_hexstr(token, IQUIC_SRESET_TOKEN_SZ, token_buf, sizeof(token_buf));
LCID("generated NEW_CONNECTION_ID frame: seqno: %"PRIu64"; retire prior "
"to: %"PRIu64"; cid: %s; token: %s", seqno, retire_prior_to,
cid_buf, token_buf);
}
void
lsquic_ev_log_generated_stop_waiting_frame (const lsquic_cid_t *cid,
lsquic_packno_t lunack)
{
LCID("generated STOP_WAITING frame; least unacked: %"PRIu64, lunack);
@ -288,16 +448,26 @@ lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t cid,
void
lsquic_ev_log_generated_http_headers (lsquic_cid_t cid, uint32_t stream_id,
lsquic_ev_log_generated_stop_sending_frame (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint16_t error_code)
{
LCID("generated STOP_SENDING frame; stream ID: %"PRIu64"; error code: "
"%"PRIu16, stream_id, error_code);
}
void
lsquic_ev_log_generated_http_headers (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id,
int is_server, const struct http_prio_frame *prio_frame,
const struct lsquic_http_headers *headers)
{
uint32_t dep_stream_id;
lsquic_stream_id_t dep_stream_id;
int exclusive, i;
unsigned short weight;
if (is_server)
LCID("generated HTTP response HEADERS for stream %"PRIu32, stream_id);
LCID("generated HTTP response HEADERS for stream %"PRIu64, stream_id);
else
{
memcpy(&dep_stream_id, prio_frame->hpf_stream_id, 4);
@ -305,8 +475,8 @@ lsquic_ev_log_generated_http_headers (lsquic_cid_t cid, uint32_t stream_id,
exclusive = dep_stream_id >> 31;
dep_stream_id &= ~(1 << 31);
weight = prio_frame->hpf_weight + 1;
LCID("generated HTTP request HEADERS for stream %"PRIu32
", dep stream: %"PRIu32", weight: %hu, exclusive: %d", stream_id,
LCID("generated HTTP request HEADERS for stream %"PRIu64
", dep stream: %"PRIu64", weight: %hu, exclusive: %d", stream_id,
dep_stream_id, weight, exclusive);
}
@ -320,15 +490,15 @@ lsquic_ev_log_generated_http_headers (lsquic_cid_t cid, uint32_t stream_id,
void
lsquic_ev_log_generated_http_push_promise (lsquic_cid_t cid,
uint32_t stream_id, uint32_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers)
lsquic_ev_log_generated_http_push_promise (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, lsquic_stream_id_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers)
{
int i;
LCID("generated HTTP PUSH_PROMISE for stream %"PRIu32"; promised stream %"
PRIu32, stream_id, promised_stream_id);
LCID("generated HTTP PUSH_PROMISE for stream %"PRIu64"; promised stream %"
PRIu64, stream_id, promised_stream_id);
for (i = 0; i < headers->count; ++i)
LCID(" %.*s: %.*s",
@ -346,9 +516,8 @@ lsquic_ev_log_generated_http_push_promise (lsquic_cid_t cid,
(char *) extra_headers->headers[i].value.iov_base);
}
void
lsquic_ev_log_create_connection (lsquic_cid_t cid,
lsquic_ev_log_create_connection (const lsquic_cid_t *cid,
const struct sockaddr *local_sa,
const struct sockaddr *peer_sa)
{
@ -357,21 +526,21 @@ lsquic_ev_log_create_connection (lsquic_cid_t cid,
void
lsquic_ev_log_hsk_completed (lsquic_cid_t cid)
lsquic_ev_log_hsk_completed (const lsquic_cid_t *cid)
{
LCID("handshake completed");
}
void
lsquic_ev_log_zero_rtt (lsquic_cid_t cid)
lsquic_ev_log_zero_rtt (const lsquic_cid_t *cid)
{
LCID("zero_rtt successful");
}
void
lsquic_ev_log_check_certs (lsquic_cid_t cid, const lsquic_str_t **certs,
lsquic_ev_log_check_certs (const lsquic_cid_t *cid, const lsquic_str_t **certs,
size_t count)
{
LCID("check certs");
@ -379,7 +548,7 @@ lsquic_ev_log_check_certs (lsquic_cid_t cid, const lsquic_str_t **certs,
void
lsquic_ev_log_version_negotiation (lsquic_cid_t cid,
lsquic_ev_log_version_negotiation (const lsquic_cid_t *cid,
const char *action, const char *ver)
{
LCID("version negotiation: %s version %s", action, ver);

View File

@ -32,7 +32,7 @@ struct uncompressed_headers;
} while (0)
void
lsquic_ev_log_packet_in (lsquic_cid_t, const struct lsquic_packet_in *);
lsquic_ev_log_packet_in (const lsquic_cid_t *, const struct lsquic_packet_in *);
#define EV_LOG_PACKET_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -40,7 +40,7 @@ lsquic_ev_log_packet_in (lsquic_cid_t, const struct lsquic_packet_in *);
} while (0)
void
lsquic_ev_log_ack_frame_in (lsquic_cid_t, const struct ack_info *);
lsquic_ev_log_ack_frame_in (const lsquic_cid_t *, const struct ack_info *);
#define EV_LOG_ACK_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -48,7 +48,8 @@ lsquic_ev_log_ack_frame_in (lsquic_cid_t, const struct ack_info *);
} while (0)
void
lsquic_ev_log_stream_frame_in (lsquic_cid_t, const struct stream_frame *);
lsquic_ev_log_stream_frame_in (const lsquic_cid_t *,
const struct stream_frame *);
#define EV_LOG_STREAM_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -56,7 +57,16 @@ lsquic_ev_log_stream_frame_in (lsquic_cid_t, const struct stream_frame *);
} while (0)
void
lsquic_ev_log_window_update_frame_in (lsquic_cid_t, uint32_t stream_id,
lsquic_ev_log_crypto_frame_in (const lsquic_cid_t *,
const struct stream_frame *, unsigned enc_level);
#define EV_LOG_CRYPTO_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_crypto_frame_in(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_window_update_frame_in (const lsquic_cid_t *, lsquic_stream_id_t,
uint64_t offset);
#define EV_LOG_WINDOW_UPDATE_FRAME_IN(...) do { \
@ -65,7 +75,7 @@ lsquic_ev_log_window_update_frame_in (lsquic_cid_t, uint32_t stream_id,
} while (0)
void
lsquic_ev_log_blocked_frame_in (lsquic_cid_t, uint32_t stream_id);
lsquic_ev_log_blocked_frame_in (const lsquic_cid_t *, lsquic_stream_id_t);
#define EV_LOG_BLOCKED_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -73,7 +83,7 @@ lsquic_ev_log_blocked_frame_in (lsquic_cid_t, uint32_t stream_id);
} while (0)
void
lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t, lsquic_packno_t);
lsquic_ev_log_stop_waiting_frame_in (const lsquic_cid_t *, lsquic_packno_t);
#define EV_LOG_STOP_WAITING_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -81,8 +91,8 @@ lsquic_ev_log_stop_waiting_frame_in (lsquic_cid_t, lsquic_packno_t);
} while (0)
void
lsquic_ev_log_connection_close_frame_in (lsquic_cid_t, uint32_t error_code,
int reason_len, const char *reason);
lsquic_ev_log_connection_close_frame_in (const lsquic_cid_t *,
uint64_t error_code, int reason_len, const char *reason);
#define EV_LOG_CONNECTION_CLOSE_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -90,8 +100,8 @@ lsquic_ev_log_connection_close_frame_in (lsquic_cid_t, uint32_t error_code,
} while (0)
void
lsquic_ev_log_goaway_frame_in (lsquic_cid_t, uint32_t error_code,
uint32_t stream_id, int reason_len, const char *reason);
lsquic_ev_log_goaway_frame_in (const lsquic_cid_t *, uint32_t error_code,
lsquic_stream_id_t, int reason_len, const char *reason);
#define EV_LOG_GOAWAY_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -99,8 +109,8 @@ lsquic_ev_log_goaway_frame_in (lsquic_cid_t, uint32_t error_code,
} while (0)
void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t, uint32_t stream_id,
uint64_t offset, uint32_t error_code);
lsquic_ev_log_rst_stream_frame_in (const lsquic_cid_t *, lsquic_stream_id_t,
uint64_t offset, uint64_t error_code);
#define EV_LOG_RST_STREAM_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -108,7 +118,16 @@ lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t, uint32_t stream_id,
} while (0)
void
lsquic_ev_log_padding_frame_in (lsquic_cid_t, size_t len);
lsquic_ev_log_stop_sending_frame_in (const lsquic_cid_t *,lsquic_stream_id_t,
uint64_t error_code);
#define EV_LOG_STOP_SENDING_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_stop_sending_frame_in(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_padding_frame_in (const lsquic_cid_t *, size_t len);
#define EV_LOG_PADDING_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -116,7 +135,7 @@ lsquic_ev_log_padding_frame_in (lsquic_cid_t, size_t len);
} while (0)
void
lsquic_ev_log_ping_frame_in (lsquic_cid_t);
lsquic_ev_log_ping_frame_in (const lsquic_cid_t *);
#define EV_LOG_PING_FRAME_IN(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -124,7 +143,8 @@ lsquic_ev_log_ping_frame_in (lsquic_cid_t);
} while (0)
void
lsquic_ev_log_packet_created (lsquic_cid_t, const struct lsquic_packet_out *);
lsquic_ev_log_packet_created (const lsquic_cid_t *,
const struct lsquic_packet_out *);
#define EV_LOG_PACKET_CREATED(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -132,7 +152,8 @@ lsquic_ev_log_packet_created (lsquic_cid_t, const struct lsquic_packet_out *);
} while (0)
void
lsquic_ev_log_packet_sent (lsquic_cid_t, const struct lsquic_packet_out *);
lsquic_ev_log_packet_sent (const lsquic_cid_t *,
const struct lsquic_packet_out *);
#define EV_LOG_PACKET_SENT(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -140,7 +161,8 @@ lsquic_ev_log_packet_sent (lsquic_cid_t, const struct lsquic_packet_out *);
} while (0)
void
lsquic_ev_log_packet_not_sent (lsquic_cid_t, const struct lsquic_packet_out *);
lsquic_ev_log_packet_not_sent (const lsquic_cid_t *,
const struct lsquic_packet_out *);
#define EV_LOG_PACKET_NOT_SENT(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -148,7 +170,7 @@ lsquic_ev_log_packet_not_sent (lsquic_cid_t, const struct lsquic_packet_out *);
} while (0)
void
lsquic_ev_log_http_headers_in (lsquic_cid_t, int is_server,
lsquic_ev_log_http_headers_in (const lsquic_cid_t *, int is_server,
const struct uncompressed_headers *);
#define EV_LOG_HTTP_HEADERS_IN(...) do { \
@ -157,7 +179,8 @@ lsquic_ev_log_http_headers_in (lsquic_cid_t, int is_server,
} while (0)
void
lsquic_ev_log_action_stream_frame (lsquic_cid_t, const struct parse_funcs *pf,
lsquic_ev_log_action_stream_frame (const lsquic_cid_t *,
const struct parse_funcs *pf,
const unsigned char *, size_t len, const char *action);
#define EV_LOG_GENERATED_STREAM_FRAME(...) do { \
@ -171,8 +194,18 @@ lsquic_ev_log_action_stream_frame (lsquic_cid_t, const struct parse_funcs *pf,
} while (0)
void
lsquic_ev_log_generated_ack_frame (lsquic_cid_t, const struct parse_funcs *,
const unsigned char *, size_t len);
lsquic_ev_log_generated_crypto_frame (const lsquic_cid_t *,
const struct parse_funcs *pf,
const unsigned char *, size_t len);
#define EV_LOG_GENERATED_CRYPTO_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_crypto_frame(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_generated_ack_frame (const lsquic_cid_t *,
const struct parse_funcs *, const unsigned char *, size_t len);
#define EV_LOG_GENERATED_ACK_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -180,7 +213,44 @@ lsquic_ev_log_generated_ack_frame (lsquic_cid_t, const struct parse_funcs *,
} while (0)
void
lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t, lsquic_packno_t);
lsquic_ev_log_generated_new_token_frame (const lsquic_cid_t *,
const struct parse_funcs *, const unsigned char *, size_t len);
#define EV_LOG_GENERATED_NEW_TOKEN_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_new_token_frame(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_generated_path_chal_frame (const lsquic_cid_t *,
const struct parse_funcs *, const unsigned char *, size_t len);
#define EV_LOG_GENERATED_PATH_CHAL_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_path_chal_frame(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_generated_path_resp_frame (const lsquic_cid_t *,
const struct parse_funcs *, const unsigned char *, size_t len);
#define EV_LOG_GENERATED_PATH_RESP_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_path_resp_frame(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_generated_new_connection_id_frame (const lsquic_cid_t *,
const struct parse_funcs *, const unsigned char *, size_t len);
#define EV_LOG_GENERATED_NEW_CONNECTION_ID_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_new_connection_id_frame(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_generated_stop_waiting_frame (const lsquic_cid_t *,
lsquic_packno_t);
#define EV_LOG_GENERATED_STOP_WAITING_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -188,7 +258,16 @@ lsquic_ev_log_generated_stop_waiting_frame (lsquic_cid_t, lsquic_packno_t);
} while (0)
void
lsquic_ev_log_generated_http_headers (lsquic_cid_t, uint32_t stream_id,
lsquic_ev_log_generated_stop_sending_frame (const lsquic_cid_t *,
lsquic_stream_id_t, uint16_t);
#define EV_LOG_GENERATED_STOP_SENDING_FRAME(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
lsquic_ev_log_generated_stop_sending_frame(__VA_ARGS__); \
} while (0)
void
lsquic_ev_log_generated_http_headers (const lsquic_cid_t *, lsquic_stream_id_t,
int is_server, const struct http_prio_frame *,
const struct lsquic_http_headers *);
@ -199,10 +278,10 @@ lsquic_ev_log_generated_http_headers (lsquic_cid_t, uint32_t stream_id,
} while (0)
void
lsquic_ev_log_generated_http_push_promise (lsquic_cid_t, uint32_t stream_id,
uint32_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers);
lsquic_ev_log_generated_http_push_promise (const lsquic_cid_t *,
lsquic_stream_id_t stream_id, lsquic_stream_id_t promised_stream_id,
const struct lsquic_http_headers *headers,
const struct lsquic_http_headers *extra_headers);
#define EV_LOG_GENERATED_HTTP_PUSH_PROMISE(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -210,7 +289,7 @@ lsquic_ev_log_generated_http_push_promise (lsquic_cid_t, uint32_t stream_id,
} while (0)
void
lsquic_ev_log_create_connection (lsquic_cid_t, const struct sockaddr *,
lsquic_ev_log_create_connection (const lsquic_cid_t *, const struct sockaddr *,
const struct sockaddr *);
#define EV_LOG_CREATE_CONN(...) do { \
@ -221,7 +300,7 @@ lsquic_ev_log_create_connection (lsquic_cid_t, const struct sockaddr *,
} while (0)
void
lsquic_ev_log_hsk_completed (lsquic_cid_t);
lsquic_ev_log_hsk_completed (const lsquic_cid_t *);
#define EV_LOG_HSK_COMPLETED(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -232,7 +311,7 @@ lsquic_ev_log_hsk_completed (lsquic_cid_t);
void
lsquic_ev_log_zero_rtt (lsquic_cid_t);
lsquic_ev_log_zero_rtt (const lsquic_cid_t *);
#define EV_LOG_ZERO_RTT(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -242,7 +321,7 @@ lsquic_ev_log_zero_rtt (lsquic_cid_t);
} while (0)
void
lsquic_ev_log_check_certs (lsquic_cid_t, const lsquic_str_t **, size_t);
lsquic_ev_log_check_certs (const lsquic_cid_t *, const lsquic_str_t **, size_t);
#define EV_LOG_CHECK_CERTS(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \
@ -252,7 +331,7 @@ lsquic_ev_log_check_certs (lsquic_cid_t, const lsquic_str_t **, size_t);
} while (0)
void
lsquic_ev_log_version_negotiation (lsquic_cid_t, const char *, const char *);
lsquic_ev_log_version_negotiation (const lsquic_cid_t *, const char *, const char *);
#define EV_LOG_VER_NEG(...) do { \
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) \

View File

@ -0,0 +1,148 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frab_list.c -- List of buffer for simple reading and writing
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_frab_list.h"
static void *
fral_alloc (void *ctx, size_t size)
{
return malloc(size);
}
static void
fral_free (void *ctx, void *obj)
{
free(obj);
}
void
lsquic_frab_list_init (struct frab_list *fral, unsigned short buf_size,
void * (*alloc)(void *alloc_ctx, size_t size),
void (*free)(void *alloc_ctx, void *obj), void *alloc_ctx)
{
TAILQ_INIT(&fral->fl_frabs);
fral->fl_alloc_ctx = alloc_ctx;
fral->fl_alloc = alloc ? alloc : fral_alloc;
fral->fl_free = free ? free : fral_free;
fral->fl_buf_size = buf_size;
fral->fl_size = 0;
}
void
lsquic_frab_list_cleanup (struct frab_list *fral)
{
struct frame_buf *frab, *next;
for (frab = TAILQ_FIRST(&fral->fl_frabs); frab; frab = next)
{
next = TAILQ_NEXT(frab, frab_next);
fral->fl_free(fral->fl_alloc_ctx, frab);
}
}
static struct frame_buf *
fral_get_frab (struct frab_list *fral)
{
struct frame_buf *frab;
frab = fral->fl_alloc(fral->fl_alloc_ctx, fral->fl_buf_size);
if (frab)
{
memset(frab, 0, sizeof(*frab));
frab->frab_buf_size = fral->fl_buf_size;
}
return frab;
}
int
lsquic_frab_list_write (struct frab_list *fral, const void *buf, size_t bufsz)
{
const unsigned char *p = buf;
const unsigned char *const end = p + bufsz;
struct frame_buf *frab;
unsigned ntowrite;
while (p < end)
{
frab = TAILQ_LAST(&fral->fl_frabs, frame_buf_head);
if (!(frab && (ntowrite = frab_left_to_write(frab)) > 0))
{
frab = fral_get_frab(fral);
if (!frab)
return -1;
TAILQ_INSERT_TAIL(&fral->fl_frabs, frab, frab_next);
ntowrite = frab_left_to_write(frab);
}
if ((ptrdiff_t) ntowrite > end - p)
ntowrite = end - p;
memcpy(frab_write_to(frab), p, ntowrite);
p += ntowrite;
frab->frab_size += ntowrite;
}
fral->fl_size += bufsz;
return 0;
}
size_t
lsquic_frab_list_size (void *ctx)
{
struct frab_list *fral = ctx;
return fral->fl_size;
}
size_t
lsquic_frab_list_read (void *ctx, void *buf, size_t bufsz)
{
struct frab_list *const fral = ctx;
unsigned char *p = buf;
unsigned char *const end = p + bufsz;
struct frame_buf *frab;
size_t ntocopy;
while (p < end && (frab = TAILQ_FIRST(&fral->fl_frabs)))
{
ntocopy = end - p;
if (ntocopy > (size_t) frab_left_to_read(frab))
ntocopy = frab_left_to_read(frab);
memcpy(p, frab->frab_buf + frab->frab_off, ntocopy);
fral->fl_size -= ntocopy;
frab->frab_off += ntocopy;
p += ntocopy;
if (frab->frab_off == frab->frab_size)
{
TAILQ_REMOVE(&fral->fl_frabs, frab, frab_next);
fral->fl_free(fral->fl_alloc_ctx, frab);
}
}
return p - (unsigned char *) buf;
}
size_t
lsquic_frab_list_mem_used (const struct frab_list *fral)
{
struct frame_buf *frab;
size_t size;
size = sizeof(*fral);
TAILQ_FOREACH(frab, &fral->fl_frabs, frab_next)
size += fral->fl_buf_size;
return size;
}

View File

@ -0,0 +1,59 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_frab_list.h -- List of buffer for simple reading and writing
*
* Useful for buffering data that cannot be packetized immediately.
*/
#ifndef LSQUIC_FRAB_LIST_H
#define LSQUIC_FRAB_LIST_H 1
struct frame_buf
{
TAILQ_ENTRY(frame_buf) frab_next;
unsigned short frab_size,
frab_off,
frab_buf_size; /* Total bytes in frab_buf */
unsigned char frab_buf[0];
};
#define frab_left_to_read(f) ((f)->frab_size - (f)->frab_off)
#define frab_left_to_write(f) ((f)->frab_buf_size - \
(unsigned short) sizeof(*(f)) - (f)->frab_size)
#define frab_write_to(f) ((f)->frab_buf + (f)->frab_size)
TAILQ_HEAD(frame_buf_head, frame_buf);
struct frab_list
{
struct frame_buf_head fl_frabs;
void * (*fl_alloc)(void *alloc_ctx, size_t size);
void (*fl_free)(void *alloc_ctx, void *obj);
void *fl_alloc_ctx;
size_t fl_size; /* Size of payload in frab_list */
unsigned short fl_buf_size; /* Size of frame_buf */
};
void
lsquic_frab_list_init (struct frab_list *, unsigned short buf_size,
void * (*fl_alloc)(void *alloc_ctx, size_t size),
void (*fl_free)(void *alloc_ctx, void *obj), void *fl_alloc_ctx);
void
lsquic_frab_list_cleanup (struct frab_list *);
int
lsquic_frab_list_write (struct frab_list *, const void *, size_t);
size_t
lsquic_frab_list_size (void *);
size_t
lsquic_frab_list_read (void *, void *, size_t);
#define lsquic_frab_list_empty(fral) TAILQ_EMPTY(&(fral)->fl_frabs)
size_t
lsquic_frab_list_mem_used (const struct frab_list *);
#endif

View File

@ -22,10 +22,12 @@
#include "lsquic_http1x_if.h"
#include "lsquic_headers.h"
#include "lsquic_ev_log.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_READER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(fr->fr_stream))
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(lsquic_stream_conn(\
fr->fr_stream))
#include "lsquic_logger.h"
@ -200,7 +202,7 @@ lsquic_frame_reader_new (enum frame_reader_flags flags,
if (hsi_if == lsquic_http1x_if)
{
fr->fr_h1x_ctor_ctx = (struct http1x_ctor_ctx) {
.cid = LSQUIC_LOG_CONN_ID,
.conn = lsquic_stream_conn(stream),
.max_headers_sz = fr->fr_max_headers_sz,
.is_server = fr->fr_flags & FRF_SERVER,
};
@ -515,8 +517,9 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
enum frame_reader_error err;
int s;
uint32_t name_idx;
unsigned name_len, val_len;
lshpack_strlen_t name_len, val_len;
char *buf;
uint32_t stream_id32;
struct uncompressed_headers *uh = NULL;
void *hset = NULL;
@ -544,6 +547,8 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
buf, buf + 16 * 1024, &name_len, &val_len, &name_idx);
if (s == 0)
{
if (name_idx > 61 /* XXX 61 */)
name_idx = 0; /* Work around bug in ls-hpack */
err = (enum frame_reader_error)
fr->fr_hsi_if->hsi_process_header(hset, name_idx, buf,
name_len, buf + name_len, val_len);
@ -575,9 +580,9 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
goto stream_error;
}
memcpy(&uh->uh_stream_id, fr->fr_state.header.hfh_stream_id,
sizeof(uh->uh_stream_id));
uh->uh_stream_id = ntohl(uh->uh_stream_id);
memcpy(&stream_id32, fr->fr_state.header.hfh_stream_id,
sizeof(stream_id32));
uh->uh_stream_id = ntohl(stream_id32);
uh->uh_oth_stream_id = hs->oth_stream_id;
if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
{

View File

@ -39,6 +39,7 @@ enum frame_reader_error
FR_ERR_DUPLICATE_PSEH = LSQUIC_HDR_ERR_DUPLICATE_PSDO_HDR,
FR_ERR_INCOMPL_REQ_PSEH = LSQUIC_HDR_ERR_INCOMPL_REQ_PSDO_HDR,
FR_ERR_UNNEC_REQ_PSEH = LSQUIC_HDR_ERR_UNNEC_REQ_PSDO_HDR,
FR_ERR_BAD_REQ_HEADER = LSQUIC_HDR_ERR_BAD_REQ_HEADER,
FR_ERR_INCOMPL_RESP_PSEH = LSQUIC_HDR_ERR_INCOMPL_RESP_PSDO_HDR,
FR_ERR_UNNEC_RESP_PSEH = LSQUIC_HDR_ERR_UNNEC_RESP_PSDO_HDR,
FR_ERR_UNKNOWN_PSEH = LSQUIC_HDR_ERR_UNKNOWN_PSDO_HDR,
@ -68,10 +69,10 @@ struct frame_reader_callbacks
void (*frc_on_push_promise) (void *frame_cb_ctx, struct uncompressed_headers *);
void (*frc_on_settings) (void *frame_cb_ctx, uint16_t setting_id,
uint32_t setting_value);
void (*frc_on_priority) (void *frame_cb_ctx, uint32_t stream_id,
int exclusive, uint32_t dep_stream_id,
void (*frc_on_priority) (void *frame_cb_ctx, lsquic_stream_id_t stream_id,
int exclusive, lsquic_stream_id_t dep_stream_id,
unsigned weight);
void (*frc_on_error) (void *frame_cb_ctx, uint32_t stream_id,
void (*frc_on_error) (void *frame_cb_ctx, lsquic_stream_id_t stream_id,
enum frame_reader_error);
};

View File

@ -22,55 +22,30 @@
#include "lsquic_mm.h"
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_frame_writer.h"
#include "lsquic_frame_common.h"
#include "lsquic_frab_list.h"
#include "lsquic_ev_log.h"
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(fw->fw_stream))
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
lsquic_stream_conn(fw->fw_stream))
#include "lsquic_logger.h"
#ifndef LSQUIC_FRAB_SZ
# define LSQUIC_FRAB_SZ 0x1000
#endif
struct frame_buf
{
TAILQ_ENTRY(frame_buf) frab_next;
unsigned short frab_size,
frab_off;
unsigned char frab_buf[
LSQUIC_FRAB_SZ
- sizeof(TAILQ_ENTRY(frame_buf))
- sizeof(unsigned short) * 2
];
};
#define frab_left_to_read(f) ((f)->frab_size - (f)->frab_off)
#define frab_left_to_write(f) ((unsigned short) sizeof((f)->frab_buf) - (f)->frab_size)
#define frab_write_to(f) ((f)->frab_buf + (f)->frab_size)
/* Make sure that frab_buf is at least five bytes long, otherwise a frame
* won't fit into two adjacent frabs.
*/
typedef char three_byte_frab_buf[(sizeof(((struct frame_buf *)0)->frab_buf) >= 5) ?1 : - 1];
TAILQ_HEAD(frame_buf_head, frame_buf);
struct lsquic_frame_writer
{
struct lsquic_stream *fw_stream;
fw_write_f fw_write;
fw_writef_f fw_writef;
struct lsquic_mm *fw_mm;
struct lshpack_enc *fw_henc;
#if LSQUIC_CONN_STATS
struct conn_stats *fw_conn_stats;
#endif
struct frame_buf_head fw_frabs;
struct frab_list fw_fral;
unsigned fw_max_frame_sz;
uint32_t fw_max_header_list_sz; /* 0 means unlimited */
enum {
@ -88,9 +63,16 @@ struct lsquic_frame_writer
#define ABS_MIN_FRAME_SIZE MAX(SETTINGS_FRAME_SZ, \
sizeof(struct http_prio_frame))
static void *
fw_alloc (void *ctx, size_t size)
{
return lsquic_mm_get_4k(ctx);
}
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
unsigned max_frame_sz, struct lshpack_enc *henc, fw_write_f write,
unsigned max_frame_sz, struct lshpack_enc *henc, fw_writef_f writef,
#if LSQUIC_CONN_STATS
struct conn_stats *conn_stats,
#endif
@ -124,17 +106,18 @@ lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
fw->fw_mm = mm;
fw->fw_henc = henc;
fw->fw_stream = stream;
fw->fw_write = write;
fw->fw_writef = writef;
fw->fw_max_frame_sz = max_frame_sz;
fw->fw_max_header_list_sz = 0;
if (is_server)
fw->fw_flags = FW_SERVER;
else
fw->fw_flags = 0;
TAILQ_INIT(&fw->fw_frabs);
#if LSQUIC_CONN_STATS
fw->fw_conn_stats = conn_stats;
#endif
lsquic_frab_list_init(&fw->fw_fral, 0x1000, fw_alloc,
(void (*)(void *, void *)) lsquic_mm_put_4k, mm);
return fw;
}
@ -142,99 +125,34 @@ lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
void
lsquic_frame_writer_destroy (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
while ((frab = TAILQ_FIRST(&fw->fw_frabs)))
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
lsquic_mm_put_4k(fw->fw_mm, frab);
}
lsquic_frab_list_cleanup(&fw->fw_fral);
free(fw);
}
static struct frame_buf *
fw_get_frab (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
frab = lsquic_mm_get_4k(fw->fw_mm);
if (frab)
memset(frab, 0, offsetof(struct frame_buf, frab_buf));
return frab;
}
static void
fw_put_frab (struct lsquic_frame_writer *fw, struct frame_buf *frab)
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
lsquic_mm_put_4k(fw->fw_mm, frab);
}
static int
fw_write_to_frab (struct lsquic_frame_writer *fw, const void *buf, size_t bufsz)
{
const unsigned char *p = buf;
const unsigned char *const end = p + bufsz;
struct frame_buf *frab;
unsigned ntowrite;
while (p < end)
{
frab = TAILQ_LAST(&fw->fw_frabs, frame_buf_head);
if (!(frab && (ntowrite = frab_left_to_write(frab)) > 0))
{
frab = fw_get_frab(fw);
if (!frab)
return -1;
TAILQ_INSERT_TAIL(&fw->fw_frabs, frab, frab_next);
ntowrite = frab_left_to_write(frab);
}
if (ntowrite > bufsz)
ntowrite = bufsz;
memcpy(frab_write_to(frab), p, ntowrite);
p += ntowrite;
bufsz -= ntowrite;
frab->frab_size += ntowrite;
}
return 0;
}
int
lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *fw)
{
return !TAILQ_EMPTY(&fw->fw_frabs);
return !lsquic_frab_list_empty(&fw->fw_fral);
}
int
lsquic_frame_writer_flush (struct lsquic_frame_writer *fw)
{
struct frame_buf *frab;
struct lsquic_reader reader = {
.lsqr_read = lsquic_frab_list_read,
.lsqr_size = lsquic_frab_list_size,
.lsqr_ctx = &fw->fw_fral,
};
ssize_t nw;
while ((frab = TAILQ_FIRST(&fw->fw_frabs)))
{
size_t ntowrite = frab_left_to_read(frab);
ssize_t nw = fw->fw_write(fw->fw_stream,
frab->frab_buf + frab->frab_off, ntowrite);
if (nw > 0)
{
frab->frab_off += nw;
if (frab->frab_off == frab->frab_size)
{
TAILQ_REMOVE(&fw->fw_frabs, frab, frab_next);
fw_put_frab(fw, frab);
}
}
else if (nw == 0)
break;
else
return -1;
}
nw = fw->fw_writef(fw->fw_stream, &reader);
return 0;
if (nw >= 0)
return 0;
else
return -1;
}
@ -249,7 +167,8 @@ struct header_framer_ctx
unsigned hfc_max_frame_sz; /* Maximum frame size. We always fill it. */
unsigned hfc_cur_sz; /* Number of bytes in the current frame. */
unsigned hfc_n_frames; /* Number of frames written. */
uint32_t hfc_stream_id; /* Stream ID */
lsquic_stream_id_t
hfc_stream_id; /* Stream ID */
enum http_frame_header_flags
hfc_first_flags;
enum http_frame_type
@ -259,8 +178,8 @@ struct header_framer_ctx
static void
hfc_init (struct header_framer_ctx *hfc, struct lsquic_frame_writer *fw,
unsigned max_frame_sz, enum http_frame_type frame_type,
uint32_t stream_id, enum http_frame_header_flags first_flags)
unsigned max_frame_sz, enum http_frame_type frame_type,
lsquic_stream_id_t stream_id, enum http_frame_header_flags first_flags)
{
memset(hfc, 0, sizeof(*hfc));
hfc->hfc_fw = fw;
@ -275,7 +194,7 @@ hfc_init (struct header_framer_ctx *hfc, struct lsquic_frame_writer *fw,
static void
hfc_save_ptr (struct header_framer_ctx *hfc)
{
hfc->hfc_header_ptr.frab = TAILQ_LAST(&hfc->hfc_fw->fw_frabs, frame_buf_head);
hfc->hfc_header_ptr.frab = TAILQ_LAST(&hfc->hfc_fw->fw_fral.fl_frabs, frame_buf_head);
hfc->hfc_header_ptr.off = hfc->hfc_header_ptr.frab->frab_size;
}
@ -339,7 +258,7 @@ hfc_write (struct header_framer_ctx *hfc, const void *buf, size_t sz)
{
if (hfc->hfc_n_frames > 0)
hfc_terminate_frame(hfc, 0);
s = fw_write_to_frab(hfc->hfc_fw, "123456789",
s = lsquic_frab_list_write(&hfc->hfc_fw->fw_fral, "123456789",
sizeof(struct http_frame_header));
if (s < 0)
return s;
@ -353,7 +272,7 @@ hfc_write (struct header_framer_ctx *hfc, const void *buf, size_t sz)
avail = sz;
if (avail)
{
s = fw_write_to_frab(hfc->hfc_fw, p, avail);
s = lsquic_frab_list_write(&hfc->hfc_fw->fw_fral, p, avail);
if (s < 0)
return s;
hfc->hfc_cur_sz += avail;
@ -421,6 +340,12 @@ check_headers_size (const struct lsquic_frame_writer *fw,
if (headers_sz <= fw->fw_max_header_list_sz)
return 0;
else if (fw->fw_flags & FW_SERVER)
{
LSQ_INFO("Sending headers larger (%u bytes) than max allowed (%u)",
headers_sz, fw->fw_max_header_list_sz);
return 0;
}
else
{
LSQ_INFO("Headers size %u is larger than max allowed (%u)",
@ -480,9 +405,7 @@ write_headers (struct lsquic_frame_writer *fw,
}
else
{
LSQ_WARN("error encoding header");
errno = EBADMSG;
return -1;
/* Ignore errors, matching HTTP2 behavior in our server code */
}
}
@ -492,7 +415,7 @@ write_headers (struct lsquic_frame_writer *fw,
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
uint32_t stream_id,
lsquic_stream_id_t stream_id,
const struct lsquic_http_headers *headers,
int eos, unsigned weight)
{
@ -553,10 +476,12 @@ lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *extra_headers)
lsquic_stream_id_t stream_id64, lsquic_stream_id_t promised_stream_id64,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *extra_headers)
{
uint32_t stream_id = stream_id64;
uint32_t promised_stream_id = promised_stream_id64;
struct header_framer_ctx hfc;
struct http_push_promise_frame push_frame;
lsquic_http_header_t mpas_headers[4];
@ -659,7 +584,7 @@ write_settings (struct lsquic_frame_writer *fw,
fh.hfh_length[1] = payload_length >> 8;
fh.hfh_length[2] = payload_length;
s = fw_write_to_frab(fw, &fh, sizeof(fh));
s = lsquic_frab_list_write(&fw->fw_fral, &fh, sizeof(fh));
if (s != 0)
return s;
@ -667,8 +592,8 @@ write_settings (struct lsquic_frame_writer *fw,
{
id = htons(settings->id);
val = htonl(settings->value);
if (0 != (s = fw_write_to_frab(fw, &id, sizeof(id))) ||
0 != (s = fw_write_to_frab(fw, &val, sizeof(val))))
if (0 != (s = lsquic_frab_list_write(&fw->fw_fral, &id, sizeof(id))) ||
0 != (s = lsquic_frab_list_write(&fw->fw_fral, &val, sizeof(val))))
return s;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP SETTINGS frame: "
"%s=%"PRIu32, lsquic_http_setting_id2str(settings->id),
@ -680,7 +605,6 @@ write_settings (struct lsquic_frame_writer *fw,
return 0;
}
int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings)
@ -711,9 +635,11 @@ lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
uint32_t stream_id, int exclusive, uint32_t stream_dep_id,
unsigned weight)
lsquic_stream_id_t stream_id64, int exclusive,
lsquic_stream_id_t stream_dep_id64, unsigned weight)
{
uint32_t stream_id = stream_id64;
uint32_t stream_dep_id = stream_dep_id64;
unsigned char buf[ sizeof(struct http_frame_header) +
sizeof(struct http_prio_frame) ];
struct http_frame_header *fh = (void *) &buf[0];
@ -741,7 +667,7 @@ lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
memcpy(prio_frame->hpf_stream_id, &stream_id, 4);
prio_frame->hpf_weight = weight - 1;
s = fw_write_to_frab(fw, buf, sizeof(buf));
s = lsquic_frab_list_write(&fw->fw_fral, buf, sizeof(buf));
if (s != 0)
return s;
@ -756,12 +682,11 @@ lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
size_t
lsquic_frame_writer_mem_used (const struct lsquic_frame_writer *fw)
{
const struct frame_buf *frab;
size_t size;
size = sizeof(*fw);
TAILQ_FOREACH(frab, &fw->fw_frabs, frab_next)
size += sizeof(*frab);
size = sizeof(*fw)
+ lsquic_frab_list_mem_used(&fw->fw_fral)
- sizeof(fw->fw_fral);
return size;
}

View File

@ -9,6 +9,7 @@
#include <stddef.h>
#include <stdint.h>
/* Same as H2_TMP_HDR_BUFF_SIZE */
#define MAX_HEADERS_SIZE (64 * 1024)
struct iovec;
@ -16,18 +17,19 @@ struct lshpack_enc;
struct lsquic_mm;
struct lsquic_frame_writer;
struct lsquic_stream;
struct lsquic_reader;
struct lsquic_http_headers;
struct lsquic_http2_setting;
#if LSQUIC_CONN_STATS
struct conn_stats;
#endif
typedef ssize_t (*fw_write_f)(struct lsquic_stream *, const void *, size_t);
typedef ssize_t (*fw_writef_f)(struct lsquic_stream *, struct lsquic_reader *);
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *, struct lsquic_stream *,
unsigned max_frame_sz, struct lshpack_enc *,
fw_write_f,
fw_writef_f,
#if LSQUIC_CONN_STATS
struct conn_stats *,
#endif
@ -44,7 +46,7 @@ lsquic_frame_writer_flush (struct lsquic_frame_writer *);
int
lsquic_frame_writer_write_headers (struct lsquic_frame_writer *,
uint32_t stream_id,
lsquic_stream_id_t stream_id,
const struct lsquic_http_headers *,
int eos, unsigned weight);
@ -54,14 +56,14 @@ lsquic_frame_writer_write_settings (struct lsquic_frame_writer *,
int
lsquic_frame_writer_write_priority (struct lsquic_frame_writer *,
uint32_t stream_id, int exclusive, uint32_t stream_dep_id,
unsigned priority);
lsquic_stream_id_t stream_id, int exclusive,
lsquic_stream_id_t stream_dep_id, unsigned priority);
int
lsquic_frame_writer_write_promise (struct lsquic_frame_writer *,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers);
lsquic_stream_id_t stream_id, lsquic_stream_id_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers);
void
lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *,

File diff suppressed because it is too large Load Diff

View File

@ -3,18 +3,56 @@
#define LSQUIC_FULL_CONN_H
struct lsquic_conn;
struct lsquic_stream_if;
struct lsquic_engine_public;
struct lsquic_conn *
full_conn_client_new (struct lsquic_engine_public *,
const struct lsquic_stream_if *,
void *stream_if_ctx,
lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size,
int is_ipv4,
const unsigned char *zero_rtt, size_t zero_rtt_len);
void
full_conn_client_call_on_new (struct lsquic_conn *);
struct lsquic_conn *
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size, int is_ipv4,
const unsigned char *zero_rtt, size_t,
const unsigned char *token, size_t);
typedef struct lsquic_conn *
(*server_conn_ctor_f) (struct lsquic_engine_public *,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
struct lsquic_conn *mini_conn);
struct lsquic_conn *
lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
struct lsquic_conn *mini_conn);
struct lsquic_conn *
lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *,
unsigned flags /* Only FC_SERVER and FC_HTTP */,
struct lsquic_conn *mini_conn);
struct dcid_elem
{
/* This is never both in the hash and on the retirement list */
union {
struct lsquic_hash_elem hash_el;
TAILQ_ENTRY(dcid_elem) next_to_ret;
} de_u;
#define de_hash_el de_u.hash_el
#define de_next_to_ret de_u.next_to_ret
lsquic_cid_t de_cid;
unsigned de_seqno;
enum {
DE_SRST = 1 << 0, /* de_srst is set */
DE_ASSIGNED = 1 << 1, /* de_cid has been assigned to a path */
} de_flags;
unsigned char de_srst[IQUIC_SRESET_TOKEN_SZ];
};
int
lsquic_gquic_full_conn_srej (struct lsquic_conn *);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
#include "lsquic_types.h"
#include "lsquic.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h"
#include "lsquic_enc_sess.h"
#include "lsquic_util.h"
@ -15,12 +15,17 @@ int
lsquic_global_init (int flags)
{
lsquic_init_timers();
return lsquic_enc_session_gquic_1.esf_global_init(flags);
if (0 != lsquic_enc_session_common_gquic_1.esf_global_init(flags))
return -1;
if (0 != lsquic_enc_session_common_ietf_v1.esf_global_init(flags))
return -1;
return 0;
}
void
lsquic_global_cleanup (void)
{
lsquic_enc_session_gquic_1.esf_global_cleanup();
lsquic_enc_session_common_gquic_1.esf_global_cleanup();
lsquic_enc_session_common_ietf_v1.esf_global_cleanup();
}

View File

@ -0,0 +1,854 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <sys/types.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_stream.h"
#include "lsquic_h3_prio.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PRIO
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(tree->h3pt_conn)
#include "lsquic_logger.h"
/* The tree supports up to 2^16 - 1 elements, which should suffice.
* Zero is not a valid value (thus -1 elements).
*/
typedef unsigned short elem_idx_t;
/* Because stream IDs are 62-bit integers and we the HTTP/3 element type
* only has four values (enum h3_elem_type), we can combine the two into
* a single value. h3_id_t's lower 62 bits contain the ID, while the
* high 2 bits contain element type. This makes searching faster.
*/
typedef uint64_t h3_id_t;
typedef unsigned char active_mark_t;
#define H3_EL_ID(type, id) ((((uint64_t) (type)) << 62) | id)
#define ROOT_IDX 1
#define MIN(a, b) ((a) < (b) ? (a) : (b))
struct h3_prio_elem
{
h3_id_t h3pe_id;
struct lsquic_stream *h3pe_stream;
/* Time at which stream was closed: */
lsquic_time_t h3pe_closed_at;
/* Tree neighbors: */
elem_idx_t h3pe_parent,
h3pe_first_child,
h3pe_left,
h3pe_right;
/* Closed streams are kept on a separate queue for efficient pruning: */
elem_idx_t h3pe_next_closed;
/* Used as tiebreaker between elements with the same weight: streams
* added earlier to the iterator have higher priority.
*/
elem_idx_t h3pe_iter_order;
#define h3pe_taken h3pe_iter_order
enum {
H3PE_FLAG_CLOSED = 1 << 0,
} h3pe_flags:8;
h3_weight_t h3pe_weight;
/* These marks are part of the iterator state */
active_mark_t h3pe_active_self;
active_mark_t h3pe_active_path;
};
#define EL_TYPE(el) ((enum h3_elem_type)((el)->h3pe_id >> 62))
#define EL_ID(el) ((uint64_t)((el)->h3pe_id & ((1ull << 62) - 1)))
#define CALC_EL_IDX(tree, el) (+((el) - (tree)->h3pt_els))
/* The weight and the iterator order are combined into a single value to
* reduce the number of branches.
*/
typedef uint32_t iter_prio_t;
#define EL_ITER_PRIO(el) (((uint32_t) ((el)->h3pe_weight << 16)) | \
(el)->h3pe_iter_order)
#define MAX_ITER_PRIO ((1u << 24) - 1)
#define MAX_CRIT_STREAMS (4 /* crypto streams */ \
+ 3 /* outgoing control, encoder, and decoder */ \
+ 3 /* incoming control, encoder, and decoder */)
struct h3_iter
{
const char *h3it_log_id;
elem_idx_t h3it_cursor;
elem_idx_t h3it_count;
active_mark_t h3it_active;
/* Critical streams do not participate in the regular HTTP/3 priority
* mechanism. They have an implicit priority which is higher than
* that of the regular request or push streams. The iterator holds
* references to them only for the duration of the iteration.
*/
unsigned h3it_crit_off;
unsigned h3it_crit_count;
struct lsquic_stream *h3it_crit_streams[MAX_CRIT_STREAMS];
};
struct h3_prio_tree
{
const struct lsquic_conn *h3pt_conn; /* Used for logging */
/* Element 0 does not contain a valid value. Its only use is to store
* the linear search sentinel.
*/
struct h3_prio_elem *h3pt_els;
struct h3_iter h3pt_iter;
unsigned h3pt_nalloc; /* Including element 0 */
unsigned h3pt_nelem; /* Including element 0 */
unsigned h3pt_max_ph; /* Maximum placeholder ID */
/* STAILQ analog: first element is the oldest, newly closed stream
* elements are inserted at the end.
*/
elem_idx_t h3pt_closed_first,
h3pt_closed_last;
};
struct h3_prio_tree *
lsquic_prio_tree_new (const struct lsquic_conn *conn, unsigned n_placeholders)
{
struct h3_prio_tree *tree;
struct h3_prio_elem *els;
unsigned nalloc, nelem;
tree = calloc(1, sizeof(*tree));
if (!tree)
return NULL;
nelem = 1 /* element 0 */ + 1 /* root */;
nalloc = nelem + 4;
els = malloc(nalloc * sizeof(els[0]));
if (!els)
{
free(tree);
return NULL;
}
els[ROOT_IDX] = (struct h3_prio_elem) { .h3pe_id = H3_EL_ID(H3ET_ROOT, 0) };
tree->h3pt_conn = conn;
tree->h3pt_els = els;
tree->h3pt_nalloc = nalloc;
tree->h3pt_nelem = nelem;
tree->h3pt_max_ph = n_placeholders;
LSQ_DEBUG("create tree with maximum %u placeholders", n_placeholders);
return tree;
}
static struct h3_prio_elem *
prio_tree_find_by_h3_id (struct h3_prio_tree *tree, h3_id_t h3_id)
{
struct h3_prio_elem *el;
tree->h3pt_els[0].h3pe_id = h3_id;
for (el = &tree->h3pt_els[tree->h3pt_nelem - 1]; el->h3pe_id != h3_id; --el)
;
if (el > tree->h3pt_els)
return el;
else
return NULL;
}
static struct h3_prio_elem *
prio_tree_find (struct h3_prio_tree *tree, enum h3_elem_type type, uint64_t id)
{
if (type == H3ET_ROOT)
return &tree->h3pt_els[ROOT_IDX];
else
return prio_tree_find_by_h3_id(tree, H3_EL_ID(type, id));
}
static struct h3_prio_elem *
prio_tree_alloc_elem (struct h3_prio_tree *tree)
{
struct h3_prio_elem *els;
unsigned nalloc;
if (tree->h3pt_nalloc > tree->h3pt_nelem)
return &tree->h3pt_els[ tree->h3pt_nelem++ ];
nalloc = MIN(H3_PRIO_MAX_ELEMS + 1, tree->h3pt_nalloc * 2);
if (nalloc <= tree->h3pt_nelem)
{
LSQ_ERROR("number of elements reached maximum");
return NULL;
}
els = realloc(tree->h3pt_els, nalloc * sizeof(tree->h3pt_els[0]));
if (!els)
{
LSQ_WARN("memory allocation failure");
return NULL;
}
tree->h3pt_els = els;
tree->h3pt_nalloc = nalloc;
return &tree->h3pt_els[ tree->h3pt_nelem++ ];
}
static struct h3_prio_elem *
prio_tree_create_elem (struct h3_prio_tree *tree, enum h3_elem_type type,
uint64_t id)
{
struct h3_prio_elem *el, *root;
assert(type != H3ET_ROOT);
if (type == H3ET_PLACEHOLDER && id >= tree->h3pt_max_ph)
{
LSQ_INFO("invalid placeholder id %"PRIu64" is invalid (maximum "
"is %u placeholders", id, tree->h3pt_max_ph);
return NULL;
}
el = prio_tree_alloc_elem(tree);
if (el)
{
root = &tree->h3pt_els[ROOT_IDX];
*el = (struct h3_prio_elem) { .h3pe_id = H3_EL_ID(type, id),
.h3pe_parent = ROOT_IDX,
.h3pe_right = root->h3pe_first_child,
.h3pe_weight = H3_DEFAULT_WEIGHT, };
if (root->h3pe_first_child)
tree->h3pt_els[ root->h3pe_first_child ].h3pe_left
= el - tree->h3pt_els;
root->h3pe_first_child = el - tree->h3pt_els;
}
return el;
}
static void
prio_tree_reparent (struct h3_prio_tree *tree,
struct h3_prio_elem *const child,
struct h3_prio_elem *const parent)
{
struct h3_prio_elem *orig_parent;
elem_idx_t child_idx;
child_idx = CALC_EL_IDX(tree, child);
orig_parent = &tree->h3pt_els[child->h3pe_parent];
if (orig_parent->h3pe_first_child == child_idx)
orig_parent->h3pe_first_child = child->h3pe_right;
else
tree->h3pt_els[child->h3pe_left].h3pe_right = child->h3pe_right;
if (child->h3pe_right)
tree->h3pt_els[child->h3pe_right].h3pe_left = child->h3pe_left;
child->h3pe_left = 0;
child->h3pe_right = parent->h3pe_first_child;
if (child->h3pe_right)
tree->h3pt_els[child->h3pe_right].h3pe_left = child_idx;
parent->h3pe_first_child = child_idx;
child->h3pe_parent = CALC_EL_IDX(tree, parent);
}
static int
prio_tree_is_parent (struct h3_prio_tree *tree,
const struct h3_prio_elem *parent, const struct h3_prio_elem *child)
{
elem_idx_t idx;
assert(parent != child);
tree->h3pt_els[0].h3pe_id = parent->h3pe_id;
idx = child->h3pe_parent;
while (tree->h3pt_els[idx].h3pe_id != parent->h3pe_id)
idx = tree->h3pt_els[idx].h3pe_parent;
return idx > 0;
}
static const char el_type2char[] =
{
[H3ET_ROOT] = 'R',
[H3ET_REQ_STREAM] = 'Q',
[H3ET_PUSH_STREAM] = 'P',
[H3ET_PLACEHOLDER] = 'H',
};
int
lsquic_prio_tree_set_rel (struct h3_prio_tree *tree,
enum h3_elem_type child_type, uint64_t child_id, h3_weight_t child_weight,
enum h3_elem_type parent_type, uint64_t parent_id)
{
struct h3_prio_elem *parent, *child;
parent = prio_tree_find(tree, parent_type, parent_id);
if (!parent)
{
parent = prio_tree_create_elem(tree, parent_type, parent_id);
if (!parent)
return -1;
}
const elem_idx_t parent_idx = CALC_EL_IDX(tree, parent);
child = prio_tree_find(tree, child_type, child_id);
if (!child)
{
child = prio_tree_create_elem(tree, child_type, child_id);
if (!child)
return -1;
/* create() above may have realloced */
parent = &tree->h3pt_els[ parent_idx ];
}
if (child->h3pe_parent != parent_idx)
{
if (prio_tree_is_parent(tree, child, parent))
prio_tree_reparent(tree, parent,
&tree->h3pt_els[child->h3pe_parent]);
prio_tree_reparent(tree, child, parent);
}
else if (child == parent)
return -1; /* This is unlikely, so check for it last */
child->h3pe_weight = child_weight;
LSQ_DEBUG("add rel to %c:%"PRIu64" -> %c:%"PRIu64" with w=%u",
el_type2char[ child_type ], child_id,
el_type2char[ parent_type ], parent_id, child_weight);
return 0;
}
/* Assume that unidirectional streams are push streams */
static enum h3_elem_type
stream_id_2_elem_type (lsquic_stream_id_t stream_id)
{
enum stream_dir dir;
dir = 1 & (stream_id >> SD_SHIFT);
if (dir == SD_BIDI)
return H3ET_REQ_STREAM;
else
return H3ET_PUSH_STREAM;
}
int
lsquic_prio_tree_add_stream (struct h3_prio_tree *tree,
struct lsquic_stream *stream, enum h3_elem_type parent_type,
uint64_t parent_id, h3_weight_t weight)
{
struct h3_prio_elem *parent, *child;
enum h3_elem_type type;
elem_idx_t child_idx;
assert(!lsquic_stream_is_critical(stream));
type = stream_id_2_elem_type(stream->id);
child = prio_tree_find(tree, type, stream->id);
if (child)
{
/* Prioritization information already exists: set the pointer
* and ignore PRIORITY frame information on the request stream.
*/
if (!child->h3pe_stream)
{
child_idx = CALC_EL_IDX(tree, child);
LSQ_DEBUG("reference stream %c:%"PRIu64" in prio element",
el_type2char[ type ], stream->id);
goto link;
}
LSQ_WARN("stream %"PRIu64" is already referenced", stream->id);
return -1;
}
child = prio_tree_create_elem(tree, type, stream->id);
if (!child)
return -1;
child_idx = CALC_EL_IDX(tree, child);
parent = prio_tree_find(tree, parent_type, parent_id);
if (!parent)
{
parent = prio_tree_create_elem(tree, parent_type, parent_id);
if (!parent)
return -1;
/* create() above may have realloced */
child = &tree->h3pt_els[ child_idx ];
}
prio_tree_reparent(tree, child, parent);
child->h3pe_weight = weight;
LSQ_DEBUG("add stream %c:%"PRIu64" -> %c:%"PRIu64" with w=%u",
el_type2char[ type ], stream->id,
el_type2char[ parent_type ], parent_id, weight);
link:
child->h3pe_stream = stream;
stream->sm_h3_prio_idx = child_idx;
return 0;
}
int
lsquic_prio_tree_remove_stream (struct h3_prio_tree *tree,
struct lsquic_stream *stream, lsquic_time_t now)
{
struct h3_prio_elem *el;
el = &tree->h3pt_els[ stream->sm_h3_prio_idx ];
if (stream->sm_h3_prio_idx > 0
&& stream->sm_h3_prio_idx < tree->h3pt_nelem
&& el->h3pe_stream == stream
&& !(el->h3pe_flags & H3PE_FLAG_CLOSED))
{
assert(el->h3pe_stream == stream);
if (tree->h3pt_closed_first)
{
tree->h3pt_els[ tree->h3pt_closed_last ].h3pe_next_closed
= stream->sm_h3_prio_idx;
tree->h3pt_closed_last = stream->sm_h3_prio_idx;
}
else
{
tree->h3pt_closed_first = stream->sm_h3_prio_idx;
tree->h3pt_closed_last = stream->sm_h3_prio_idx;
}
el->h3pe_stream = NULL;
el->h3pe_closed_at = now;
el->h3pe_flags |= H3PE_FLAG_CLOSED;
stream->sm_h3_prio_idx = 0;
LSQ_DEBUG("removed reference to stream %"PRIu64, stream->id);
return 0;
}
else
{
LSQ_WARN("cannot remove stream %"PRIu64, stream->id);
return -1;
}
}
void
lsquic_prio_tree_drop_el (struct h3_prio_tree *tree, struct h3_prio_elem *el)
{
const elem_idx_t
right = el->h3pe_right,
left = el->h3pe_left,
parent = el->h3pe_parent,
el_idx = CALC_EL_IDX(tree, el);
elem_idx_t idx, last;
/* Update links around the element: */
idx = el->h3pe_first_child;
if (idx == 0)
{
if (left)
tree->h3pt_els[ left ].h3pe_right = right;
else
tree->h3pt_els[ parent ].h3pe_first_child = right;
if (right)
tree->h3pt_els[ right ].h3pe_left = left;
}
else
{
if (left)
{
tree->h3pt_els[ left ].h3pe_right = idx;
tree->h3pt_els[ idx ].h3pe_left = left;
}
else
tree->h3pt_els[ parent ].h3pe_first_child = idx;
do
{
last = idx;
tree->h3pt_els[ idx ].h3pe_parent = parent;
idx = tree->h3pt_els[ idx ].h3pe_right;
}
while (idx);
if (right)
{
tree->h3pt_els[ right ].h3pe_left = last;
tree->h3pt_els[ last ].h3pe_right = right;
}
}
/* Move last element into its spot */
if (--tree->h3pt_nelem > el_idx)
{
el = &tree->h3pt_els[ el_idx ];
*el = tree->h3pt_els[ tree->h3pt_nelem ];
for (idx = el->h3pe_first_child; idx;
idx = tree->h3pt_els[ idx ].h3pe_right)
tree->h3pt_els[ idx ].h3pe_parent = el_idx;
if (el->h3pe_left)
tree->h3pt_els[ el->h3pe_left ].h3pe_right = el_idx;
if (el->h3pe_right)
tree->h3pt_els[ el->h3pe_right ].h3pe_left = el_idx;
if (tree->h3pt_els[ el->h3pe_parent ].h3pe_first_child
== tree->h3pt_nelem)
tree->h3pt_els[ el->h3pe_parent ].h3pe_first_child = el_idx;
if (el->h3pe_stream)
el->h3pe_stream->sm_h3_prio_idx = el_idx;
}
}
void
lsquic_prio_tree_prune (struct h3_prio_tree *tree, lsquic_time_t cutoff)
{
struct h3_prio_elem *el;
unsigned count = 0;
while (tree->h3pt_closed_first
&& tree->h3pt_els[ tree->h3pt_closed_first ].h3pe_closed_at < cutoff)
{
el = &tree->h3pt_els[ tree->h3pt_closed_first ];
tree->h3pt_closed_first = el->h3pe_next_closed;
if (tree->h3pt_closed_first == 0)
tree->h3pt_closed_last = 0;
++count;
lsquic_prio_tree_drop_el(tree, el);
}
LSQ_DEBUG("pruned %u element%.*s from the tree", count, count != 1, "s");
}
void
lsquic_prio_tree_destroy (struct h3_prio_tree *tree)
{
LSQ_DEBUG("destroyed");
free(tree->h3pt_els);
free(tree);
}
void
lsquic_prio_tree_iter_reset (struct h3_prio_tree *tree, const char *log_id)
{
struct h3_iter *const iter = &tree->h3pt_iter;
unsigned i;
iter->h3it_log_id = log_id;
iter->h3it_count = 0;
iter->h3it_crit_count = 0;
iter->h3it_crit_off = 0;
iter->h3it_cursor = ROOT_IDX;
iter->h3it_active++;
if (0 == iter->h3it_active)
{
for (i = 0; i < tree->h3pt_nelem; ++i)
{
tree->h3pt_els[i].h3pe_active_self = 0;
tree->h3pt_els[i].h3pe_active_path = 0;
}
iter->h3it_active++;
}
LSQ_DEBUG("reset iterator; log id: `%s'; active mark: %u", log_id,
iter->h3it_active);
}
static int
prio_tree_iter_add_critical (struct h3_prio_tree *tree,
struct lsquic_stream *stream)
{
struct h3_iter *const iter = &tree->h3pt_iter;
if (iter->h3it_crit_count < sizeof(iter->h3it_crit_streams)
/ sizeof(iter->h3it_crit_streams[0]))
{
iter->h3it_crit_streams[ iter->h3it_crit_count++ ] = stream;
LSQ_DEBUG("%s: add critical stream %"PRIu64" at position %u",
iter->h3it_log_id, stream->id, iter->h3it_crit_count - 1);
return 0;
}
else
{
LSQ_WARN("could not add critical stream %"PRIu64" to the iterator: "
"no room", stream->id);
return -1;
}
}
static int
prio_tree_iter_add_regular (struct h3_prio_tree *tree,
struct lsquic_stream *stream)
{
struct h3_iter *const iter = &tree->h3pt_iter;
struct h3_prio_elem *el;
if (stream->sm_h3_prio_idx > 0
&& stream->sm_h3_prio_idx < tree->h3pt_nelem)
{
el = &tree->h3pt_els[stream->sm_h3_prio_idx];
assert(el->h3pe_stream == stream);
el->h3pe_active_self = iter->h3it_active;
el->h3pe_iter_order = iter->h3it_count++;
while (el->h3pe_parent)
{
el = &tree->h3pt_els[ el->h3pe_parent ];
el->h3pe_active_path = iter->h3it_active;
}
LSQ_DEBUG("%s: added stream %"PRIu64" to the iterator",
iter->h3it_log_id, stream->id);
return 0;
}
else
{
LSQ_WARN("%s: stream %"PRIu64" has invalid priority index value: %u",
iter->h3it_log_id, stream->id, stream->sm_h3_prio_idx);
assert(0);
return -1;
}
}
int
lsquic_prio_tree_iter_add (struct h3_prio_tree *tree,
struct lsquic_stream *stream)
{
if (lsquic_stream_is_critical(stream))
return prio_tree_iter_add_critical(tree, stream);
else
return prio_tree_iter_add_regular(tree, stream);
}
struct lsquic_stream *
lsquic_prio_tree_iter_next (struct h3_prio_tree *tree)
{
struct h3_iter *const iter = &tree->h3pt_iter;
struct h3_prio_elem *el_self, *el_path;
iter_prio_t prio_self, prio_path;
elem_idx_t idx;
if (iter->h3it_crit_off < iter->h3it_crit_count)
{
LSQ_DEBUG("%s: return critical stream %"PRIu64" at position %u",
iter->h3it_log_id, iter->h3it_crit_streams[iter->h3it_crit_off]->id,
iter->h3it_crit_off);
return iter->h3it_crit_streams[ iter->h3it_crit_off++ ];
}
top0:
if (!iter->h3it_cursor)
{
LSQ_DEBUG("%s: out of streams", iter->h3it_log_id);
return NULL;
}
top1:
el_self = NULL, el_path = NULL;
prio_self = MAX_ITER_PRIO + 1;
prio_path = MAX_ITER_PRIO + 1;
for (idx = tree->h3pt_els[ iter->h3it_cursor ].h3pe_first_child;
idx;
idx = tree->h3pt_els[ idx ].h3pe_right)
{
if (tree->h3pt_els[ idx ].h3pe_active_self == iter->h3it_active
&& EL_ITER_PRIO(&tree->h3pt_els[ idx ]) < prio_self)
{
el_self = &tree->h3pt_els[ idx ];
prio_self = EL_ITER_PRIO(el_self);
}
if (tree->h3pt_els[ idx ].h3pe_active_path == iter->h3it_active
&& EL_ITER_PRIO(&tree->h3pt_els[ idx ]) < prio_path)
{
el_path = &tree->h3pt_els[ idx ];
prio_path = EL_ITER_PRIO(el_path);
}
}
if (el_self)
{
el_self->h3pe_active_self = 0;
LSQ_DEBUG("%s: return %c stream %"PRIu64, iter->h3it_log_id,
el_type2char[ EL_TYPE(el_self) ], EL_ID(el_self));
return el_self->h3pe_stream;
}
else if (el_path)
{
iter->h3it_cursor = CALC_EL_IDX(tree, el_path);
LSQ_DEBUG("%s: step down to %c:%"PRIu64, iter->h3it_log_id,
el_type2char[ EL_TYPE(el_path) ], EL_ID(el_path));
goto top1;
}
else
{
tree->h3pt_els[ iter->h3it_cursor ].h3pe_active_path = 0;
iter->h3it_cursor = tree->h3pt_els[ iter->h3it_cursor ].h3pe_parent;
LSQ_DEBUG("%s: step up to %c:%"PRIu64, iter->h3it_log_id,
el_type2char[ EL_TYPE(&tree->h3pt_els[ iter->h3it_cursor ]) ],
EL_ID(&tree->h3pt_els[ iter->h3it_cursor ]));
goto top0;
}
}
struct lsquic_stream *
lsquic_prio_tree_highest_non_crit (struct h3_prio_tree *tree)
{
elem_idx_t idx, parent;
struct h3_prio_elem *el;
unsigned weight;
parent = ROOT_IDX;
new_level:
/* Look for the stream */
weight = 1u << sizeof(h3_weight_t) * 8;
el = NULL;
for (idx = tree->h3pt_els[ parent ].h3pe_first_child;
idx;
idx = tree->h3pt_els[ idx ].h3pe_right)
if (tree->h3pt_els[ idx ].h3pe_stream
&& tree->h3pt_els[ idx ].h3pe_weight < weight)
{
el = &tree->h3pt_els[ idx ];
weight = el->h3pe_weight;
}
else
/* Clear new level of crumbs */
tree->h3pt_els[ idx ].h3pe_taken = 0;
if (el)
return el->h3pe_stream;
old_level:
/* Look for paths not taken */
weight = 1u << sizeof(h3_weight_t) * 8;
el = NULL;
for (idx = tree->h3pt_els[ parent ].h3pe_first_child;
idx;
idx = tree->h3pt_els[ idx ].h3pe_right)
if (tree->h3pt_els[ idx ].h3pe_first_child
&& !tree->h3pt_els[ idx ].h3pe_taken
&& tree->h3pt_els[ idx ].h3pe_weight < weight)
{
el = &tree->h3pt_els[ idx ];
weight = el->h3pe_weight;
}
if (el)
{
parent = CALC_EL_IDX(tree, el);
goto new_level;
}
tree->h3pt_els[ parent ].h3pe_taken = 1;
parent = tree->h3pt_els[ parent ].h3pe_parent;
if (parent)
goto old_level;
return NULL;
}
static size_t
prio_tree_node_to_str (const struct h3_prio_tree *tree,
const struct h3_prio_elem *el, char *const buf, char *const end)
{
elem_idx_t next_idx;
char *p;
int sz, comma;
if (buf >= end)
return 0;
p = buf;
sz = snprintf(p, end - p, "(t: %c; id: %"PRIu64"; w: %u",
el_type2char[EL_TYPE(el)], EL_ID(el), el->h3pe_weight);
if (sz > end - p)
return end - buf;
p += sz;
if (el->h3pe_first_child)
{
sz = snprintf(p, end - p, "; c: [");
if (sz > end - p)
return end - buf;
p += sz;
next_idx = el->h3pe_first_child;
comma = 0;
do
{
if (comma)
{
sz = snprintf(p, end - p, ",");
if (sz > end - p)
return end - buf;
p += sz;
}
else
++comma;
el = &tree->h3pt_els[ next_idx ];
p += prio_tree_node_to_str(tree, el, p, end);
if (p >= end)
return end - buf;
}
while ((next_idx = el->h3pe_right));
sz = snprintf(p, end - p, "])");
if (sz > end - p)
return end - buf;
p += sz;
}
else
{
sz = snprintf(p, end - p, ")");
if (sz > end - p)
return end - buf;
p += sz;
}
return p - buf;
}
size_t
lsquic_prio_tree_to_str (const struct h3_prio_tree *tree, char *buf,
size_t buf_sz)
{
return prio_tree_node_to_str(tree, &tree->h3pt_els[ROOT_IDX], buf,
buf + buf_sz);
}
void
lsquic_prio_tree_set_ph (struct h3_prio_tree *tree, unsigned ph)
{
LSQ_DEBUG("set max placeholders to %u", ph);
tree->h3pt_max_ph = ph;
}

View File

@ -0,0 +1,72 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_H3_PRIO_H
#define LSQUIC_H3_PRIO_H 1
#define H3_PRIO_MAX_ELEMS ((1 << 16) - 1)
struct h3_prio_tree;
struct lsquic_conn;
struct lsquic_stream;
/* Same deal as with GQUIC priorities: lower value means higher priority */
typedef uint8_t h3_weight_t;
/* This corresponds to 16: */
#define H3_DEFAULT_WEIGHT 240
enum h3_elem_type
{
H3ET_ROOT,
H3ET_REQ_STREAM,
H3ET_PUSH_STREAM,
H3ET_PLACEHOLDER,
};
struct h3_prio_tree *
lsquic_prio_tree_new (const struct lsquic_conn *, unsigned);
void
lsquic_prio_tree_set_ph (struct h3_prio_tree *, unsigned);
void
lsquic_prio_tree_destroy (struct h3_prio_tree *);
/* Call for PRIORITY frames arriving on request stream */
int
lsquic_prio_tree_add_stream (struct h3_prio_tree *, struct lsquic_stream *,
enum h3_elem_type parent_type, uint64_t parent_id, h3_weight_t);
/* Call for PRIORITY frames on the control stream */
int
lsquic_prio_tree_set_rel (struct h3_prio_tree *,
enum h3_elem_type child_type, uint64_t child_id, h3_weight_t child_weight,
enum h3_elem_type parent_type, uint64_t parent_id);
int
lsquic_prio_tree_remove_stream (struct h3_prio_tree *, struct lsquic_stream *,
lsquic_time_t now);
void
lsquic_prio_tree_prune (struct h3_prio_tree *, lsquic_time_t cutoff);
/* To begin to use the iterator, reset it first */
void
lsquic_prio_tree_iter_reset (struct h3_prio_tree *tree, const char *);
/* Then, add one or more stream objects */
int
lsquic_prio_tree_iter_add (struct h3_prio_tree *tree, struct lsquic_stream *);
struct lsquic_stream *
lsquic_prio_tree_highest_non_crit (struct h3_prio_tree *);
/* Then, call next() until NULL is returned. It is OK to abandon the iterator
* at any time.
*/
struct lsquic_stream *
lsquic_prio_tree_iter_next (struct h3_prio_tree *tree);
size_t
lsquic_prio_tree_to_str (const struct h3_prio_tree *tree, char *, size_t);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,207 +1,115 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HANDSHAKE_SERVER_H
#define LSQUIC_HANDSHAKE_SERVER_H
#ifndef LSQUIC_HANDSHAKE_H
#define LSQUIC_HANDSHAKE_H 1
struct lsquic_engine_public;
struct lsquic_enc_session;
struct stack_st_X509;
typedef struct lsquic_enc_session lsquic_enc_session_t;
#define MAX_SCFG_LENGTH 512
#define MAX_SPUBS_LENGTH 32
#define STK_LENGTH 60
#define SNO_LENGTH 56
#define SCID_LENGTH 16
#define DNONC_LENGTH 32
#define aes128_key_len 16
#define aes128_iv_len 4
#define SRST_LENGTH 16
#define STK_LENGTH 60
#define SCID_LENGTH 16
enum handshake_error /* TODO: rename this enum */
struct lsquic_server_config;
struct sockaddr;
struct lsquic_str;
struct lsquic_packet_in;
struct lsquic_cid;
/* client side, certs and hashs
*/
typedef struct cert_hash_item_st
{
DATA_NOT_ENOUGH = -2,
DATA_FORMAT_ERROR = -1,
HS_ERROR = -1,
DATA_NO_ERROR = 0,
HS_SHLO = 0,
HS_1RTT = 1,
HS_2RTT = 2,
struct lsquic_str* domain; /*with port, such as "xyz.com:8088" as the key */
struct lsquic_str* crts;
struct lsquic_str* hashs;
struct lsquic_hash_elem hash_el;
int count;
} cert_hash_item_t;
#ifndef NDEBUG
void gen_stk(struct lsquic_server_config *, const struct sockaddr *ip_addr, uint64_t tm,
unsigned char stk_out[STK_LENGTH]);
enum hsk_failure_reason
verify_stk0(struct lsquic_server_config *, const struct sockaddr *ip_addr, uint64_t tm,
struct lsquic_str *stk,
unsigned secs_since_stk_generated);
enum hsk_failure_reason
verify_stk(void *, const struct sockaddr *ip_addr,
uint64_t tm, struct lsquic_str *stk);
struct cert_hash_item_st* c_find_certs(const struct lsquic_str *domain);
#endif
#define SNO_LENGTH 56
/* EVP_AEAD_CTX from boringssl pre-18d9f28f0df9f95570. */
struct old_evp_aead_ctx_st {
void *ptr1; /* aead */
void *ptr2; /* aead_state */
};
enum enc_level
{
ENC_LEV_UNSET,
ENC_LEV_CLEAR,
ENC_LEV_INIT,
ENC_LEV_FORW,
};
extern const char *const lsquic_enclev2str[];
/* client */
typedef struct c_cert_item_st
{
struct lsquic_str* crts;
struct lsquic_str* hashs;
int count;
} c_cert_item_t;
/* client side need to store 0rtt info per STK */
typedef struct lsquic_session_cache_info_st
/* Server need refresh SCFG once a day */
/* can not use sizeof() to get the size */
typedef struct SCFG_info_st
{
unsigned char sscid[SCID_LENGTH];
unsigned char spubs[32]; /* server pub key for next time 0rtt */
uint32_t ver; /* one VERSION */
uint32_t aead;
uint32_t kexs;
uint32_t pdmd;
uint64_t orbt;
uint64_t expy;
int scfg_flag; /* 0, no-init, 1, no parse, 2, parsed */
struct lsquic_str sstk;
struct lsquic_str scfg;
struct lsquic_str sni_key; /* This is only used as key */
unsigned char priv_key[32];
unsigned char skt_key[16];
uint32_t aead; /* Fixed, ONLY AESG */
uint32_t kexs; /* Fixed, ONLY C255 */
uint32_t pdmd; /* Fixed, ONLY X509 */
uint64_t orbt; /* Fixed, 0 */
uint64_t expy;
/* Keep the hole for compatibility with older builds of LSWS: */
struct old_evp_aead_ctx_st unused
#if __GNUC__
__attribute__((deprecated))
#endif
;
short scfg_len;
} SCFG_info_t;
} lsquic_session_cache_info_t;
struct lsquic_zero_rtt_storage
struct SCFG_st
{
uint32_t quic_version_tag;
uint32_t serializer_version;
uint32_t ver;
uint32_t aead;
uint32_t kexs;
uint32_t pdmd;
uint64_t orbt;
uint64_t expy;
uint64_t sstk_len;
uint64_t scfg_len;
uint64_t scfg_flag;
uint8_t sstk[STK_LENGTH];
uint8_t scfg[MAX_SCFG_LENGTH];
uint8_t sscid[SCID_LENGTH];
uint8_t spubs[MAX_SPUBS_LENGTH];
uint32_t cert_count;
/*
* uint32_t cert_len;
* uint8_t cert_data[0];
*/
SCFG_info_t info;
unsigned char scfg[]; /* whoile buffer */
};
typedef struct SCFG_st SCFG_t;
/* server side need to store STK with expired time */
#ifndef LSQUIC_KEEP_ENC_SESS_HISTORY
# ifndef NDEBUG
# define LSQUIC_KEEP_ENC_SESS_HISTORY 1
# else
# define LSQUIC_KEEP_ENC_SESS_HISTORY 0
# endif
#endif
#if LSQUIC_KEEP_ENC_SESS_HISTORY
#define ESHIST_BITS 7
#define ESHIST_MASK ((1 << ESHIST_BITS) - 1)
#define ESHIST_STR_SIZE ((1 << ESHIST_BITS) + 1)
#endif
struct enc_session_funcs
typedef struct lsquic_server_config
{
/* Global initialization: call once per implementation */
int (*esf_global_init)(int flags);
SCFG_t *lsc_scfg; /* This part is stored in SHM */
EVP_AEAD_CTX lsc_stk_ctx;
} lsquic_server_config_t;
/* Global cleanup: call once per implementation */
void (*esf_global_cleanup) (void);
/* Based on enum HandshakeFailureReason in Chromium */
enum hsk_failure_reason
{
HFR_HANDSHAKE_OK = 0,
#if LSQUIC_KEEP_ENC_SESS_HISTORY
/* Grab encryption session history */
void (*esf_get_hist) (const lsquic_enc_session_t *,
char buf[ESHIST_STR_SIZE]);
#endif
/* Invalid client nonce in CHLO: */
HFR_CLIENT_NONCE_UNKNOWN = 1, /* Default nonce failure */
HFR_CLIENT_NONCE_INVALID = 2, /* Incorrect nonce length */
HFR_CLIENT_NONCE_NOT_UNIQ = 3,
HFR_CLIENT_NONCE_INVALID_ORBIT = 4,
HFR_CLIENT_NONCE_INVALID_TIME = 5,
/* Destroy enc session */
void (*esf_destroy)(lsquic_enc_session_t *enc_session);
/* Invalid server nonce in CHLO: */
HFR_SERVER_NONCE_DECRYPTION = 8,
HFR_SERVER_NONCE_INVALID = 9,
HFR_SERVER_NONCE_NOT_UNIQUE = 10,
HFR_SERVER_NONCE_INVALID_TIME = 11,
HFR_SERVER_NONCE_REQUIRED = 20,
/* Return true if handshake has been completed */
int (*esf_is_hsk_done)(lsquic_enc_session_t *enc_session);
/* Encrypt buffer */
enum enc_level (*esf_encrypt)(lsquic_enc_session_t *enc_session,
enum lsquic_version, uint8_t path_id, uint64_t pack_num,
const unsigned char *header, size_t header_len,
const unsigned char *data, size_t data_len,
unsigned char *buf_out, size_t max_out_len, size_t *out_len,
int is_hello);
/** Decrypt buffer
*
* If decryption is successful, decryption level is returned. Otherwise,
* the return value is -1.
*/
enum enc_level (*esf_decrypt)(lsquic_enc_session_t *enc_session,
enum lsquic_version,
uint8_t path_id, uint64_t pack_num,
unsigned char *buf, size_t *header_len, size_t data_len,
unsigned char *diversification_nonce,
unsigned char *buf_out, size_t max_out_len, size_t *out_len);
/* Get value of setting specified by `tag' */
int (*esf_get_peer_setting) (const lsquic_enc_session_t *, uint32_t tag,
uint32_t *val);
/* Get value of peer option (that from COPT array) */
int (*esf_get_peer_option) (const lsquic_enc_session_t *enc_session,
uint32_t tag);
/* Create client session */
lsquic_enc_session_t *
(*esf_create_client) (const char *domain, lsquic_cid_t cid,
const struct lsquic_engine_public *,
const unsigned char *, size_t);
/* Generate connection ID */
lsquic_cid_t (*esf_generate_cid) (void);
/* -1 error, 0, OK, response in `buf' */
int
(*esf_gen_chlo) (lsquic_enc_session_t *, enum lsquic_version,
uint8_t *buf, size_t *len);
int
(*esf_handle_chlo_reply) (lsquic_enc_session_t *,
const uint8_t *data, int len);
size_t
(*esf_mem_used)(lsquic_enc_session_t *);
int
(*esf_verify_reset_token) (lsquic_enc_session_t *, const unsigned char *,
size_t);
int
(*esf_did_zero_rtt_succeed) (const lsquic_enc_session_t *);
int
(*esf_is_zero_rtt_enabled) (const lsquic_enc_session_t *);
c_cert_item_t *
(*esf_get_cert_item) (const lsquic_enc_session_t *);
struct stack_st_X509 *
(*esf_get_server_cert_chain) (lsquic_enc_session_t *);
ssize_t
(*esf_get_zero_rtt) (lsquic_enc_session_t *, enum lsquic_version,
void *, size_t);
HFR_CONFIG_INCHOATE_HELLO = 12, /* Missing SCID tag */
HFR_CONFIG_UNKNOWN_CONFIG = 13, /* Could not find server config SCID */
HFR_SRC_ADDR_TOKEN_INVALID = 14, /* Missing STK tag */
HFR_SRC_ADDR_TOKEN_DECRYPTION = 15,
HFR_SRC_ADDR_TOKEN_PARSE = 16,
HFR_SRC_ADDR_TOKEN_DIFFERENT_IP_ADDRESS = 17,
HFR_SRC_ADDR_TOKEN_CLOCK_SKEW = 18,
HFR_SRC_ADDR_TOKEN_EXPIRED = 19,
HFR_INVALID_EXPECTED_LEAF_CERTIFICATE = 21,
};
extern
#ifdef NDEBUG
const
#endif
struct enc_session_funcs lsquic_enc_session_gquic_1;
#define select_esf_by_ver(ver) \
(ver ? &lsquic_enc_session_gquic_1 : &lsquic_enc_session_gquic_1)
enum lsquic_version
lsquic_zero_rtt_version (const unsigned char *, size_t);

View File

@ -12,21 +12,9 @@
#include <vc_compat.h>
#endif
#include "lsquic_malo.h"
#include "lsquic_hash.h"
#include "lsquic_xxhash.h"
struct lsquic_hash_elem
{
TAILQ_ENTRY(lsquic_hash_elem)
qhe_next_bucket,
qhe_next_all;
const void *qhe_key_data;
unsigned qhe_key_len;
void *qhe_value;
unsigned qhe_hash_val;
};
TAILQ_HEAD(hels_head, lsquic_hash_elem);
#define N_BUCKETS(n_bits) (1U << (n_bits))
@ -36,7 +24,6 @@ struct lsquic_hash
{
struct hels_head *qh_buckets,
qh_all;
struct malo *qh_malo_els;
struct lsquic_hash_elem *qh_iter_next;
unsigned qh_count;
unsigned qh_nbits;
@ -48,7 +35,6 @@ lsquic_hash_create (void)
{
struct hels_head *buckets;
struct lsquic_hash *hash;
struct malo *malo;
unsigned nbits = 2;
unsigned i;
@ -63,21 +49,12 @@ lsquic_hash_create (void)
return NULL;
}
malo = lsquic_malo_create(sizeof(struct lsquic_hash_elem));
if (!malo)
{
free(hash);
free(buckets);
return NULL;
}
for (i = 0; i < N_BUCKETS(nbits); ++i)
TAILQ_INIT(&buckets[i]);
TAILQ_INIT(&hash->qh_all);
hash->qh_buckets = buckets;
hash->qh_nbits = nbits;
hash->qh_malo_els = malo;
hash->qh_iter_next = NULL;
hash->qh_count = 0;
return hash;
@ -87,7 +64,6 @@ lsquic_hash_create (void)
void
lsquic_hash_destroy (struct lsquic_hash *hash)
{
lsquic_malo_destroy(hash->qh_malo_els);
free(hash->qh_buckets);
free(hash);
}
@ -129,21 +105,16 @@ lsquic_hash_grow (struct lsquic_hash *hash)
struct lsquic_hash_elem *
lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
unsigned key_sz, void *data)
unsigned key_sz, void *value, struct lsquic_hash_elem *el)
{
unsigned buckno, hash_val;
struct lsquic_hash_elem *el;
el = lsquic_malo_get(hash->qh_malo_els);
if (!el)
if (el->qhe_flags & QHE_HASHED)
return NULL;
if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 &&
0 != lsquic_hash_grow(hash))
{
lsquic_malo_put(el);
return NULL;
}
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
@ -151,8 +122,9 @@ lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
el->qhe_key_data = key;
el->qhe_key_len = key_sz;
el->qhe_value = data;
el->qhe_value = value;
el->qhe_hash_val = hash_val;
el->qhe_flags |= QHE_HASHED;
++hash->qh_count;
return el;
}
@ -178,22 +150,18 @@ lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
}
void *
lsquic_hashelem_getdata (const struct lsquic_hash_elem *el)
{
return el->qhe_value;
}
void
lsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el)
{
unsigned buckno;
assert(el->qhe_flags & QHE_HASHED);
buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val);
if (hash->qh_iter_next == el)
hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket);
TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all);
lsquic_malo_put(el);
el->qhe_flags &= ~QHE_HASHED;
--hash->qh_count;
}
@ -235,6 +203,5 @@ size_t
lsquic_hash_mem_used (const struct lsquic_hash *hash)
{
return sizeof(*hash)
+ N_BUCKETS(hash->qh_nbits) * sizeof(hash->qh_buckets[0])
+ lsquic_malo_mem_used(hash->qh_malo_els);
+ N_BUCKETS(hash->qh_nbits) * sizeof(hash->qh_buckets[0]);
}

View File

@ -7,7 +7,20 @@
#define LSQUIC_HASH_H
struct lsquic_hash;
struct lsquic_hash_elem;
struct lsquic_hash_elem
{
TAILQ_ENTRY(lsquic_hash_elem)
qhe_next_bucket,
qhe_next_all;
const void *qhe_key_data;
void *qhe_value;
unsigned qhe_key_len;
unsigned qhe_hash_val;
enum {
QHE_HASHED = 1 << 0,
} qhe_flags;
};
struct lsquic_hash *
lsquic_hash_create (void);
@ -17,13 +30,12 @@ lsquic_hash_destroy (struct lsquic_hash *);
struct lsquic_hash_elem *
lsquic_hash_insert (struct lsquic_hash *, const void *key, unsigned key_sz,
void *data);
void *value, struct lsquic_hash_elem *);
struct lsquic_hash_elem *
lsquic_hash_find (struct lsquic_hash *, const void *key, unsigned key_sz);
void *
lsquic_hashelem_getdata (const struct lsquic_hash_elem *);
#define lsquic_hashelem_getdata(el) ((el)->qhe_value)
void
lsquic_hash_erase (struct lsquic_hash *, struct lsquic_hash_elem *);

View File

@ -0,0 +1,221 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_hcsi_reader.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HCSI_READER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(reader->hr_conn)
#include "lsquic_logger.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
void
lsquic_hcsi_reader_init (struct hcsi_reader *reader,
struct lsquic_conn *conn, const struct hcsi_callbacks *callbacks,
void *ctx)
{
memset(reader, 0, sizeof(*reader));
reader->hr_state = HR_READ_FRAME_BEGIN;
reader->hr_conn = conn;
reader->hr_cb = callbacks;
reader->hr_ctx = ctx;
LSQ_DEBUG("initialized");
}
int
lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
size_t bufsz)
{
const unsigned char *p = buf;
const unsigned char *const end = p + bufsz;
const unsigned char *orig_p;
enum h3_prio_frame_read_status prio_status;
uint64_t len;
int s;
while (p < end)
{
switch (reader->hr_state)
{
case HR_READ_FRAME_BEGIN:
reader->hr_u.vint2_state.vr2s_state = 0;
reader->hr_state = HR_READ_FRAME_CONTINUE;
/* fall-through */
case HR_READ_FRAME_CONTINUE:
s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
if (s < 0)
break;
reader->hr_frame_type = reader->hr_u.vint2_state.vr2s_one;
reader->hr_frame_length = reader->hr_u.vint2_state.vr2s_two;
switch (reader->hr_frame_type)
{
case HQFT_SETTINGS:
if (reader->hr_frame_length)
{
reader->hr_state = HR_READ_SETTING_BEGIN;
reader->hr_nread = 0;
}
else
{
reader->hr_cb->on_settings_frame(reader->hr_ctx);
reader->hr_state = HR_READ_FRAME_BEGIN;
}
break;
case HQFT_PRIORITY:
reader->hr_state = HR_READ_PRIORITY_BEGIN;
break;
case HQFT_GOAWAY:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_CANCEL_PUSH:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_MAX_PUSH_ID:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_DATA:
case HQFT_HEADERS:
case HQFT_PUSH_PROMISE:
reader->hr_cb->on_unexpected_frame(reader->hr_ctx,
reader->hr_frame_type);
return -1;
default:
if (!(reader->hr_frame_type >= 0xB &&
(reader->hr_frame_type - 0xB) % 0x1F == 0))
LSQ_INFO("unknown frame type 0x%"PRIX64" -- skipping",
reader->hr_frame_type);
reader->hr_state = HR_SKIPPING;
LSQ_DEBUG("unknown frame 0x%"PRIX64": will skip %"PRIu64" bytes",
reader->hr_frame_type, reader->hr_frame_length);
break;
}
break;
case HR_READ_VARINT:
reader->hr_u.vint_state.pos = 0;
reader->hr_state = HR_READ_VARINT_CONTINUE;
reader->hr_nread = 0;
/* fall-through */
case HR_READ_VARINT_CONTINUE:
orig_p = p;
s = lsquic_varint_read_nb(&p, end, &reader->hr_u.vint_state);
reader->hr_nread += p - orig_p;
if (0 == s)
{
if (reader->hr_nread != reader->hr_frame_length)
{
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
HEC_MALFORMED_FRAME + reader->hr_frame_type,
"Frame length does not match actual payload length");
reader->hr_state = HR_ERROR;
return -1;
}
switch (reader->hr_frame_type)
{
case HQFT_GOAWAY:
reader->hr_cb->on_goaway(reader->hr_ctx,
reader->hr_u.vint_state.val);
break;
case HQFT_CANCEL_PUSH:
reader->hr_cb->on_cancel_push(reader->hr_ctx,
reader->hr_u.vint_state.val);
break;
case HQFT_MAX_PUSH_ID:
reader->hr_cb->on_max_push_id(reader->hr_ctx,
reader->hr_u.vint_state.val);
break;
default:
assert(0);
}
reader->hr_state = HR_READ_FRAME_BEGIN;
break;
}
else
{
assert(p == end);
return 0;
}
case HR_SKIPPING:
len = MIN((uintptr_t) (end - p), reader->hr_frame_length);
p += len;
reader->hr_frame_length -= len;
if (0 == reader->hr_frame_length)
reader->hr_state = HR_READ_FRAME_BEGIN;
break;
case HR_READ_SETTING_BEGIN:
reader->hr_u.vint2_state.vr2s_state = 0;
reader->hr_state = HR_READ_SETTING_CONTINUE;
/* fall-through */
case HR_READ_SETTING_CONTINUE:
orig_p = p;
s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
reader->hr_nread += p - orig_p;
if (reader->hr_nread > reader->hr_frame_length)
{
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
HEC_MALFORMED_FRAME + HQFT_SETTINGS,
"SETTING frame contents too long");
reader->hr_state = HR_ERROR;
return -1;
}
if (s < 0)
break;
reader->hr_cb->on_setting(reader->hr_ctx,
reader->hr_u.vint2_state.vr2s_one,
reader->hr_u.vint2_state.vr2s_two);
if (reader->hr_nread >= reader->hr_frame_length)
{
reader->hr_state = HR_READ_FRAME_BEGIN;
reader->hr_cb->on_settings_frame(reader->hr_ctx);
}
else
reader->hr_state = HR_READ_SETTING_BEGIN;
break;
case HR_READ_PRIORITY_BEGIN:
reader->hr_u.prio.h3pfrs_state = 0;
reader->hr_nread = 0;
reader->hr_state = HR_READ_PRIORITY_CONTINUE;
/* fall-through */
case HR_READ_PRIORITY_CONTINUE:
orig_p = p;
prio_status = lsquic_h3_prio_frame_read(&p, end - p,
&reader->hr_u.prio);
reader->hr_nread += p - orig_p;
if (prio_status == H3PFR_STATUS_DONE)
{
if (reader->hr_nread != reader->hr_frame_length)
{
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
HEC_MALFORMED_FRAME + HQFT_PRIORITY, "PRIORITY frame "
"contents size does not match frame length");
reader->hr_state = HR_ERROR;
return -1;
}
reader->hr_state = HR_READ_FRAME_BEGIN;
reader->hr_cb->on_priority(reader->hr_ctx,
&reader->hr_u.prio.h3pfrs_prio);
}
break;
default:
assert(0);
/* fall-through */
case HR_ERROR:
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,61 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hcsi_reader.h -- HTTP Control Stream Incoming (HCSI) reader
*/
#ifndef LSQUIC_HCSI_READER_H
#define LSQUIC_HCSI_READER_H 1
struct lsquic_conn;
struct hcsi_callbacks
{
void (*on_priority)(void *ctx, const struct hq_priority *);
void (*on_cancel_push)(void *ctx, uint64_t push_id);
void (*on_max_push_id)(void *ctx, uint64_t push_id);
/* Gets called at the *end* of the SETTING frame */
void (*on_settings_frame)(void *ctx);
void (*on_setting)(void *ctx, uint64_t setting_id, uint64_t value);
void (*on_goaway)(void *ctx, uint64_t stream_id);
void (*on_unexpected_frame)(void *ctx, uint64_t frame_type);
};
struct hcsi_reader
{
enum {
HR_READ_FRAME_BEGIN,
HR_READ_FRAME_CONTINUE,
HR_SKIPPING,
HR_READ_SETTING_BEGIN,
HR_READ_SETTING_CONTINUE,
HR_READ_PRIORITY_BEGIN,
HR_READ_PRIORITY_CONTINUE,
HR_READ_VARINT,
HR_READ_VARINT_CONTINUE,
HR_ERROR,
} hr_state;
struct lsquic_conn *hr_conn;
uint64_t hr_frame_type;
uint64_t hr_frame_length;
union
{
struct varint_read_state vint_state;
struct varint_read2_state vint2_state;
struct h3_prio_frame_read_state prio;
} hr_u;
const struct hcsi_callbacks *hr_cb;
void *hr_ctx;
unsigned hr_nread; /* Used for PRIORITY and SETTINGS frames */
};
void
lsquic_hcsi_reader_init (struct hcsi_reader *, struct lsquic_conn *,
const struct hcsi_callbacks *, void *cb_ctx);
int
lsquic_hcsi_reader_feed (struct hcsi_reader *, const void *buf, size_t bufsz);
#endif

View File

@ -0,0 +1,367 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hcso_writer.c - write to outgoing HTTP Control Stream
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_stream.h"
#include "lsquic_frab_list.h"
#include "lsquic_varint.h"
#include "lsquic_byteswap.h"
#include "lsquic_hcso_writer.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HCSO_WRITER
#define LSQUIC_LOG_CONN_ID \
lsquic_conn_log_cid(lsquic_stream_conn(writer->how_stream))
#include "lsquic_logger.h"
static int
hcso_write_type (struct hcso_writer *writer)
{
int s;
#ifndef NDEBUG
if (writer->how_flags & HOW_RAND_VARINT)
{
s = rand() & 3;
LSQ_DEBUG("writing %d-byte stream type", 1 << s);
}
else
#endif
s = 0;
switch (s)
{
case 0:
return lsquic_frab_list_write(&writer->how_fral,
(unsigned char []) { HQUST_CONTROL }, 1);
case 1:
return lsquic_frab_list_write(&writer->how_fral,
(unsigned char []) { 0x40, HQUST_CONTROL }, 2);
case 2:
return lsquic_frab_list_write(&writer->how_fral,
(unsigned char []) { 0x80, 0x00, 0x00, HQUST_CONTROL }, 4);
default:
return lsquic_frab_list_write(&writer->how_fral,
(unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
HQUST_CONTROL }, 8);
}
}
static lsquic_stream_ctx_t *
hcso_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
{
struct hcso_writer *writer = stream_if_ctx;
writer->how_stream = stream;
lsquic_frab_list_init(&writer->how_fral, 0x100, NULL, NULL, NULL);
#ifndef NDEBUG
const char *env = getenv("LSQUIC_RND_VARINT_LEN");
if (env && atoi(env))
{
writer->how_flags |= HOW_RAND_VARINT;
LSQ_INFO("will randomize varints");
if (0 == (rand() & 3))
{
writer->how_flags |= HOW_CHOP_STREAM;
LSQ_INFO("will chop beginning of stream into tiny STREAM frames");
}
}
#endif
if (0 != hcso_write_type(writer))
{
LSQ_INFO("cannot write to frab list");
/* TODO: abort connection */
}
LSQ_DEBUG("create HTTP Control Stream Writer");
lsquic_stream_wantwrite(stream, 1);
return stream_if_ctx;
}
static unsigned
hcso_setting_type2bits (struct hcso_writer *writer, unsigned setting)
{
unsigned bits = vint_val2bits(setting);
#ifndef NDEBUG
unsigned max_bits;
if (writer->how_flags & HOW_RAND_VARINT)
{
max_bits = rand() & 3;
if (max_bits > bits)
bits = max_bits;
LSQ_DEBUG("writing out HTTP/3 setting %u as %d-byte varint",
setting, 1 << bits);
}
#endif
return bits;
}
int
lsquic_hcso_write_settings (struct hcso_writer *writer,
const struct lsquic_engine_settings *settings,
int is_server)
{
unsigned char *p;
unsigned bits;
int was_empty;
#ifdef NDEBUG
const unsigned frame_size_len = 1;
#else
/* Need to use two bytes for frame length, as randomization may require
* more than 63 bytes.
*/
const unsigned frame_size_len = 2;
#endif
unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len
/* There are maximum four settings that need to be written out and
* each value can be encoded in maximum 8 bytes:
*/
+ 4 * (
#ifdef NDEBUG
1 /* Each setting needs 1-byte varint number, */
#else
8 /* but it can be up to 8 bytes when randomized */
#endif
+ 8) ];
p = buf;
*p++ = HQFT_SETTINGS;
p += frame_size_len;
if (is_server)
if (settings->es_h3_placeholders != HQ_DF_NUM_PLACEHOLDERS)
{
/* Write out SETTINGS_NUM_PLACEHOLDERS */
bits = hcso_setting_type2bits(writer, HQSID_NUM_PLACEHOLDERS);
vint_write(p, HQSID_NUM_PLACEHOLDERS, bits, 1 << bits);
p += 1 << bits;
bits = vint_val2bits(settings->es_h3_placeholders);
vint_write(p, settings->es_h3_placeholders, bits, 1 << bits);
p += 1 << bits;
}
if (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
{
/* Write out SETTINGS_MAX_HEADER_LIST_SIZE */
bits = hcso_setting_type2bits(writer, HQSID_MAX_HEADER_LIST_SIZE);
vint_write(p, HQSID_MAX_HEADER_LIST_SIZE, bits, 1 << bits);
p += 1 << bits;
bits = vint_val2bits(settings->es_max_header_list_size);
vint_write(p, settings->es_max_header_list_size, bits, 1 << bits);
p += 1 << bits;
}
if (settings->es_qpack_dec_max_size != HQ_DF_QPACK_MAX_TABLE_CAPACITY)
{
/* Write out SETTINGS_QPACK_MAX_TABLE_CAPACITY */
bits = hcso_setting_type2bits(writer, HQSID_QPACK_MAX_TABLE_CAPACITY);
vint_write(p, HQSID_QPACK_MAX_TABLE_CAPACITY, bits, 1 << bits);
p += 1 << bits;
bits = vint_val2bits(settings->es_qpack_dec_max_size);
vint_write(p, settings->es_qpack_dec_max_size, bits, 1 << bits);
p += 1 << bits;
}
if (settings->es_qpack_dec_max_blocked != HQ_DF_QPACK_BLOCKED_STREAMS)
{
/* Write out SETTINGS_QPACK_BLOCKED_STREAMS */
bits = hcso_setting_type2bits(writer, HQSID_QPACK_BLOCKED_STREAMS);
vint_write(p, HQSID_QPACK_BLOCKED_STREAMS, bits, 1 << bits);
p += 1 << bits;
bits = vint_val2bits(settings->es_qpack_dec_max_size);
vint_write(p, settings->es_qpack_dec_max_blocked, bits, 1 << bits);
p += 1 << bits;
}
#ifdef NDEBUG
buf[1] = p - buf - 2;
#else
vint_write(buf + 1, p - buf - 3, 1, 2);
#endif
was_empty = lsquic_frab_list_empty(&writer->how_fral);
if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
{
LSQ_INFO("cannot write SETTINGS frame to frab list");
return -1;
}
if (was_empty)
lsquic_stream_wantwrite(writer->how_stream, 1);
LSQ_DEBUG("generated %u-byte SETTINGS frame", (unsigned) (p - buf));
return 0;
}
static const char *
hqft2str (enum hq_frame_type type)
{
switch (type)
{
case HQFT_PUSH_PROMISE: return "PUSH_PROMISE";
case HQFT_MAX_PUSH_ID: return "MAX_PUSH_ID";
case HQFT_CANCEL_PUSH: return "CANCEL_PUSH";
case HQFT_GOAWAY: return "GOAWAY";
default: return "<unknown>";
}
}
int
hcso_write_number_frame (struct hcso_writer *writer,
enum hq_frame_type type, uint64_t value)
{
unsigned char *p;
unsigned bits;
int was_empty;
unsigned char buf[1 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */ ];
p = buf;
*p++ = type;
bits = vint_val2bits(value);
*p++ = 1 << bits;
vint_write(p, value, bits, 1 << bits);
p += 1 << bits;
was_empty = lsquic_frab_list_empty(&writer->how_fral);
if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
{
LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
return -1;
}
if (was_empty)
lsquic_stream_wantwrite(writer->how_stream, 1);
LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
hqft2str(type));
return 0;
}
int
lsquic_hcso_write_goaway (struct hcso_writer *writer,
lsquic_stream_id_t stream_id)
{
return hcso_write_number_frame(writer, HQFT_GOAWAY, stream_id);
}
int
lsquic_hcso_write_max_push_id (struct hcso_writer *writer, uint64_t max_push_id)
{
return hcso_write_number_frame(writer, HQFT_MAX_PUSH_ID, max_push_id);
}
int
lsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id)
{
return hcso_write_number_frame(writer, HQFT_CANCEL_PUSH, push_id);
}
#ifndef NDEBUG
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static size_t
one_byte_limit_read (void *ctx, void *buf, size_t bufsz)
{
return lsquic_frab_list_read(ctx, buf, MIN(bufsz, 1));
}
static size_t
one_byte_limit_size (void *ctx)
{
size_t size;
size = lsquic_frab_list_size(ctx);
return MIN(size, 1);
}
#endif
static void
hcso_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
{
struct hcso_writer *const writer = (void *) ctx;
struct lsquic_reader reader = {
.lsqr_read = lsquic_frab_list_read,
.lsqr_size = lsquic_frab_list_size,
.lsqr_ctx = &writer->how_fral
};
ssize_t nw;
#ifndef NDEBUG
if (stream->tosend_off < 8 && (writer->how_flags & HOW_CHOP_STREAM))
{
reader.lsqr_read = one_byte_limit_read;
reader.lsqr_size = one_byte_limit_size;
}
#endif
nw = lsquic_stream_writef(stream, &reader);
if (nw >= 0)
{
LSQ_DEBUG("wrote %zd bytes to stream", nw);
(void) lsquic_stream_flush(stream);
if (lsquic_frab_list_empty(&writer->how_fral))
lsquic_stream_wantwrite(stream, 0);
}
else
{
/* TODO: abort connection */
LSQ_WARN("cannot write to stream: %s", strerror(errno));
lsquic_stream_wantwrite(stream, 0);
}
}
static void
hcso_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
{
struct hcso_writer *writer = (void *) ctx;
LSQ_DEBUG("close HTTP Control Stream Writer");
lsquic_frab_list_cleanup(&writer->how_fral);
writer->how_stream = NULL;
}
static void
hcso_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
{
assert(0);
}
static const struct lsquic_stream_if hcso_if =
{
.on_new_stream = hcso_on_new,
.on_read = hcso_on_read,
.on_write = hcso_on_write,
.on_close = hcso_on_close,
};
const struct lsquic_stream_if *const lsquic_hcso_writer_if = &hcso_if;

View File

@ -0,0 +1,39 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hcso_writer.h
*/
#ifndef LSQUIC_HCSO_WRITER_H
#define LSQUIC_HCSO_WRITER_H 1
struct lsquic_engine_settings;
struct lsquic_stream;
struct hcso_writer
{
struct lsquic_stream *how_stream;
struct frab_list how_fral;
#ifndef NDEBUG
enum {
HOW_RAND_VARINT = 1 << 0,
HOW_CHOP_STREAM = 1 << 1,
} how_flags;
#endif
};
int
lsquic_hcso_write_settings (struct hcso_writer *,
const struct lsquic_engine_settings *, int);
int
lsquic_hcso_write_goaway (struct hcso_writer *, lsquic_stream_id_t);
int
lsquic_hcso_write_max_push_id (struct hcso_writer *, uint64_t max_push_id);
int
lsquic_hcso_write_cancel_push (struct hcso_writer *, uint64_t push_id);
extern const struct lsquic_stream_if *const lsquic_hcso_writer_if;
#endif

View File

@ -2,8 +2,6 @@
#ifndef LSQUIC_HEADERS_H
#define LSQUIC_HEADERS_H 1
#include <stdint.h>
/* When ea_hsi_if is not specified, the headers are converted to a C string
* that contains HTTP/1.x-like header structure.
*/
@ -23,8 +21,8 @@ struct http1x_headers
*/
struct uncompressed_headers
{
uint32_t uh_stream_id;
uint32_t uh_oth_stream_id; /* For HEADERS frame, the ID of the
lsquic_stream_id_t uh_stream_id;
lsquic_stream_id_t uh_oth_stream_id; /* For HEADERS frame, the ID of the
* stream that this stream depends
* on. (Zero means unset.) For
* PUSH_PROMISE, the promised stream

View File

@ -5,6 +5,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@ -29,7 +30,8 @@
#define MAX_HEADER_TABLE_SIZE (512 * 1024)
#define LSQUIC_LOGGER_MODULE LSQLM_HEADERS
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(lsquic_stream_conn(hs->hs_stream))
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
lsquic_stream_conn(hs->hs_stream))
#include "lsquic_logger.h"
static const struct frame_reader_callbacks *frame_callbacks_ptr;
@ -85,6 +87,7 @@ headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
LSQ_WARN("could not initialize HPACK encoder: %s", strerror(errno));
return NULL;
}
(void) lshpack_enc_use_hist(&hs->hs_henc, 1);
hs->hs_flags |= HS_HENC_INITED;
hs->hs_stream = stream;
LSQ_DEBUG("stream created");
@ -103,7 +106,7 @@ headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
return NULL;
}
hs->hs_fw = lsquic_frame_writer_new(&hs->hs_enpub->enp_mm, stream, 0,
&hs->hs_henc, lsquic_stream_write,
&hs->hs_henc, lsquic_stream_writef,
#if LSQUIC_CONN_STATS
hs->hs_conn_stats,
#endif
@ -161,8 +164,8 @@ headers_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
int
lsquic_headers_stream_send_headers (struct headers_stream *hs,
uint32_t stream_id, const struct lsquic_http_headers *headers, int eos,
unsigned weight)
lsquic_stream_id_t stream_id, const struct lsquic_http_headers *headers,
int eos, unsigned weight)
{
LSQ_DEBUG("received compressed headers to send");
int s;
@ -181,7 +184,8 @@ lsquic_headers_stream_send_headers (struct headers_stream *hs,
int
lsquic_headers_stream_send_priority (struct headers_stream *hs,
uint32_t stream_id, int exclusive, uint32_t dep_stream_id, unsigned weight)
lsquic_stream_id_t stream_id, int exclusive,
lsquic_stream_id_t dep_stream_id, unsigned weight)
{
LSQ_DEBUG("received priority to send");
int s;
@ -272,8 +276,8 @@ headers_on_push_promise (void *ctx, struct uncompressed_headers *uh)
static void
headers_on_priority (void *ctx, uint32_t stream_id, int exclusive,
uint32_t dep_stream_id, unsigned weight)
headers_on_priority (void *ctx, lsquic_stream_id_t stream_id, int exclusive,
lsquic_stream_id_t dep_stream_id, unsigned weight)
{
struct headers_stream *hs = ctx;
hs->hs_callbacks->hsc_on_priority(hs->hs_cb_ctx, stream_id, exclusive,
@ -282,7 +286,8 @@ headers_on_priority (void *ctx, uint32_t stream_id, int exclusive,
static void
headers_on_error (void *ctx, uint32_t stream_id, enum frame_reader_error err)
headers_on_error (void *ctx, lsquic_stream_id_t stream_id,
enum frame_reader_error err)
{
struct headers_stream *hs = ctx;
switch (err)
@ -290,6 +295,7 @@ headers_on_error (void *ctx, uint32_t stream_id, enum frame_reader_error err)
case FR_ERR_DUPLICATE_PSEH:
case FR_ERR_INCOMPL_REQ_PSEH:
case FR_ERR_UNNEC_REQ_PSEH:
case FR_ERR_BAD_REQ_HEADER:
case FR_ERR_INCOMPL_RESP_PSEH:
case FR_ERR_UNNEC_RESP_PSEH:
case FR_ERR_UNKNOWN_PSEH:
@ -299,7 +305,8 @@ headers_on_error (void *ctx, uint32_t stream_id, enum frame_reader_error err)
case FR_ERR_DECOMPRESS:
case FR_ERR_HEADERS_TOO_LARGE:
case FR_ERR_SELF_DEP_STREAM:
LSQ_INFO("error %u is a stream error (stream %u)", err, stream_id);
LSQ_INFO("error %u is a stream error (stream %"PRIu64")", err,
stream_id);
hs->hs_callbacks->hsc_on_stream_error(hs->hs_cb_ctx, stream_id);
break;
case FR_ERR_INVALID_FRAME_SIZE:
@ -308,7 +315,8 @@ headers_on_error (void *ctx, uint32_t stream_id, enum frame_reader_error err)
case FR_ERR_ZERO_STREAM_ID:
case FR_ERR_NOMEM:
case FR_ERR_EXPECTED_CONTIN:
LSQ_INFO("error %u is a connection error (stream %u)", err, stream_id);
LSQ_INFO("error %u is a connection error (stream %"PRIu64")", err,
stream_id);
hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
break;
}
@ -376,10 +384,12 @@ headers_on_settings (void *ctx, uint16_t setting_id, uint32_t setting_value)
int
lsquic_headers_stream_push_promise (struct headers_stream *hs,
uint32_t stream_id, uint32_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers)
lsquic_stream_id_t stream_id64, lsquic_stream_id_t promised_stream_id64,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *headers)
{
uint32_t stream_id = stream_id64;
uint32_t promised_stream_id = promised_stream_id64;
int s;
LSQ_DEBUG("promising stream %u in response to stream %u",
promised_stream_id, stream_id);

View File

@ -33,9 +33,9 @@ struct headers_stream_callbacks
void (*hsc_on_enable_push) (void *hs_cb_ctx, int enable_push);
void (*hsc_on_push_promise)
(void *frame_cb_ctx, struct uncompressed_headers *);
void (*hsc_on_priority) (void *hs_cb_ctx, uint32_t stream_id,
int exclusive, uint32_t dep_stream_id, unsigned weight);
void (*hsc_on_stream_error) (void *hs_cb_ctx, uint32_t stream_id);
void (*hsc_on_priority) (void *hs_cb_ctx, lsquic_stream_id_t stream_id,
int exclusive, lsquic_stream_id_t dep_stream_id, unsigned weight);
void (*hsc_on_stream_error) (void *hs_cb_ctx, lsquic_stream_id_t stream_id);
void (*hsc_on_conn_error) (void *hs_cb_ctx);
};
@ -53,19 +53,20 @@ lsquic_headers_stream_destroy (struct headers_stream *);
int
lsquic_headers_stream_send_headers (struct headers_stream *hs,
uint32_t stream_id,
lsquic_stream_id_t stream_id,
const struct lsquic_http_headers *, int eos,
unsigned weight);
int
lsquic_headers_stream_push_promise (struct headers_stream *hs,
uint32_t stream_id, uint32_t promised_stream_id,
lsquic_stream_id_t stream_id, lsquic_stream_id_t promised_stream_id,
const struct iovec *path, const struct iovec *host,
const struct lsquic_http_headers *);
int
lsquic_headers_stream_send_priority (struct headers_stream *hs,
uint32_t stream_id, int exclusive, uint32_t dep_stream_id, unsigned weight);
lsquic_stream_id_t stream_id, int exclusive,
lsquic_stream_id_t dep_stream_id, unsigned weight);
int
lsquic_headers_stream_send_settings (struct headers_stream *hs,

View File

@ -0,0 +1,40 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <openssl/hkdf.h>
#include "lsquic_hkdf.h"
/* [draft-ietf-quic-tls-17] Section 5 */
void
lsquic_qhkdf_expand (const EVP_MD *md, const unsigned char *secret,
unsigned secret_len, const char *label, uint8_t label_len,
unsigned char *out, uint16_t out_len)
{
#ifndef NDEBUG
int s;
#endif
unsigned char info[ 2 + 1 + 6 + label_len + 1];
info[0] = out_len >> 8;
info[1] = out_len;
info[2] = label_len + 6;
info[3] = 't';
info[4] = 'l';
info[5] = 's';
info[6] = '1';
info[7] = '3';
info[8] = ' ';
memcpy(info + 9, label, label_len);
info[9 + label_len] = 0;
#ifndef NDEBUG
s =
#else
(void)
#endif
HKDF_expand(out, out_len, md, secret, secret_len, info, sizeof(info));
assert(s);
}

View File

@ -0,0 +1,21 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HKDF_H
#define LSQUIC_HKDF_H 1
/* [draft-ietf-quic-tls-22] Section 5.2 */
#define HSK_SALT_BUF "\x7f\xbc\xdb\x0e\x7c\x66\xbb\xe9\x19\x3a" \
"\x96\xcd\x21\x51\x9e\xbd\x7a\x02\x64\x4a"
#define HSK_SALT ((unsigned char *) HSK_SALT_BUF)
#define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1)
#define CLIENT_LABEL "client in"
#define CLIENT_LABEL_SZ (sizeof(CLIENT_LABEL) - 1)
#define SERVER_LABEL "server in"
#define SERVER_LABEL_SZ (sizeof(SERVER_LABEL) - 1)
void
lsquic_qhkdf_expand (const struct env_md_st *, const unsigned char *secret,
unsigned secret_len, const char *label, uint8_t label_len,
unsigned char *out, uint16_t out_len);
#endif

94
src/liblsquic/lsquic_hq.c Normal file
View File

@ -0,0 +1,94 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <stddef.h>
#include "lsquic_types.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
const char *const lsquic_h3det2str[] =
{
[H3DET_REQ_STREAM] = "request stream",
[H3DET_PUSH_STREAM] = "push stream",
[H3DET_PLACEHOLDER] = "placeholder",
[H3DET_ROOT] = "root of the tree",
};
const char *const lsquic_h3pet2str[] =
{
[H3PET_REQ_STREAM] = "request stream",
[H3PET_PUSH_STREAM] = "push stream",
[H3PET_PLACEHOLDER] = "placeholder",
[H3PET_CUR_STREAM] = "current stream",
};
enum h3_prio_frame_read_status
lsquic_h3_prio_frame_read (const unsigned char **bufp, size_t bufsz,
struct h3_prio_frame_read_state *state)
{
const unsigned char *p = *bufp;
const unsigned char *const end = p + bufsz;
int s;
while (p < end)
{
switch (state->h3pfrs_state)
{
case H3PFRS_STATE_TYPE:
state->h3pfrs_prio.hqp_prio_type = (p[0] >> HQ_PT_SHIFT) & 3;
state->h3pfrs_prio.hqp_dep_type = (p[0] >> HQ_DT_SHIFT) & 3;
++p;
if (state->h3pfrs_prio.hqp_prio_type == H3PET_CUR_STREAM
&& state->h3pfrs_prio.hqp_dep_type == H3DET_ROOT)
state->h3pfrs_state = H3PFRS_STATE_WEIGHT;
else
{
state->h3pfrs_flags = 0;
state->h3pfrs_state = H3PFRS_STATE_VINT_BEGIN;
}
break;
case H3PFRS_STATE_VINT_BEGIN:
state->h3pfrs_vint.pos = 0;
state->h3pfrs_state = H3PFRS_STATE_VINT_CONTINUE;
/* fall-through */
case H3PFRS_STATE_VINT_CONTINUE:
s = lsquic_varint_read_nb(&p, end, &state->h3pfrs_vint);
if (0 == s)
{
if (state->h3pfrs_prio.hqp_prio_type == H3PET_CUR_STREAM
|| (state->h3pfrs_flags & H3PFRS_FLAG_HAVE_PRIO_ID))
{
state->h3pfrs_prio.hqp_dep_id = state->h3pfrs_vint.val;
state->h3pfrs_state = H3PFRS_STATE_WEIGHT;
}
else
{
state->h3pfrs_prio.hqp_prio_id = state->h3pfrs_vint.val;
state->h3pfrs_flags |= H3PFRS_FLAG_HAVE_PRIO_ID;
if (state->h3pfrs_prio.hqp_dep_type == H3DET_ROOT)
state->h3pfrs_state = H3PFRS_STATE_WEIGHT;
else
state->h3pfrs_state = H3PFRS_STATE_VINT_BEGIN;
}
break;
}
else
{
assert(p == end);
*bufp = p;
return H3PFR_STATUS_NEED;
}
case H3PFRS_STATE_WEIGHT:
state->h3pfrs_prio.hqp_weight = *p++;
*bufp = p;
return H3PFR_STATUS_DONE;
default:
assert(0);
return H3PFR_STATUS_DONE;
}
}
*bufp = p;
return H3PFR_STATUS_NEED;
}

152
src/liblsquic/lsquic_hq.h Normal file
View File

@ -0,0 +1,152 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hq.h -- HTTP over QUIC (HQ) types
*/
#ifndef LSQUIC_HQ_H
#define LSQUIC_HQ_H 1
/* [draft-ietf-quic-http-15] Section 4 */
enum hq_frame_type
{
HQFT_DATA = 0,
HQFT_HEADERS = 1,
HQFT_PRIORITY = 2,
HQFT_CANCEL_PUSH = 3,
HQFT_SETTINGS = 4,
HQFT_PUSH_PROMISE = 5,
HQFT_GOAWAY = 7,
HQFT_MAX_PUSH_ID = 0xD,
HQFT_DUPLICATE_PUSH = 0xE,
/* This frame is made up and its type is never written to stream.
* Nevertheless, just to be on the safe side, give it a value as
* described in [draft-ietf-quic-http-20] Section 4.2.10.
*/
HQFT_PUSH_PREAMBLE = 0x1F * 3 + 0x21,
};
enum h3_prio_el_type
{
H3PET_REQ_STREAM = 0,
H3PET_PUSH_STREAM = 1,
H3PET_PLACEHOLDER = 2,
H3PET_CUR_STREAM = 3,
};
enum h3_dep_el_type
{
H3DET_REQ_STREAM = 0,
H3DET_PUSH_STREAM = 1,
H3DET_PLACEHOLDER = 2,
H3DET_ROOT = 3,
};
#define HQ_PT_SHIFT 6
#define HQ_DT_SHIFT 4
enum hq_setting_id
{
HQSID_QPACK_MAX_TABLE_CAPACITY = 1,
HQSID_MAX_HEADER_LIST_SIZE = 6,
HQSID_QPACK_BLOCKED_STREAMS = 7,
HQSID_NUM_PLACEHOLDERS = 9,
};
/* As of 12/18/2018: */
#define HQ_DF_QPACK_MAX_TABLE_CAPACITY 0
#define HQ_DF_NUM_PLACEHOLDERS 0
#define HQ_DF_MAX_HEADER_LIST_SIZE 0
#define HQ_DF_QPACK_BLOCKED_STREAMS 0
struct hq_priority
{
lsquic_stream_id_t hqp_prio_id;
lsquic_stream_id_t hqp_dep_id;
enum h3_prio_el_type hqp_prio_type:8;
enum h3_dep_el_type hqp_dep_type:8;
uint8_t hqp_weight;
};
#define HQP_WEIGHT(p) ((p)->hqp_weight + 1)
/* [draft-ietf-quic-http-19] Section 10.6,
* [draft-ietf-quic-qpack-07] Section 8.2
*/
enum hq_uni_stream_type
{
HQUST_CONTROL = 0,
HQUST_PUSH = 1,
HQUST_QPACK_ENC = 2,
HQUST_QPACK_DEC = 3,
};
extern const char *const lsquic_h3det2str[];
extern const char *const lsquic_h3pet2str[];
/* [draft-ietf-quic-http-22] Section 8.1 and
* [draft-ietf-quic-qpack-08], Section 8.3
*/
enum http_error_code
{
HEC_NO_ERROR = 0x00,
HEC_GENERAL_PROTOCOL_ERROR = 0x01,
/* Error code 0x2 is reserved and has no meaning */
HEC_INTERNAL_ERROR = 0x03,
/* Error code 0x4 is reserved and has no meaning */
HEC_REQUEST_CANCELLED = 0x05,
HEC_INCOMPLETE_REQUEST = 0x06,
HEC_CONNECT_ERROR = 0x07,
HEC_EXCESSIVE_LOAD = 0x08,
HEC_VERSION_FALLBACK = 0x09,
HEC_WRONG_STREAM = 0x0A,
HEC_ID_ERROR = 0x0B,
/* Error code 0xC is reserved and has no meaning */
HEC_STREAM_CREATION_ERROR = 0x0D,
/* Error code 0xE is reserved and has no meaning */
HEC_CLOSED_CRITICAL_STREAM = 0x0F,
/* Error code 0x10 is reserved and has no meaning */
HEC_EARLY_RESPONSE = 0x0011,
HEC_MISSING_SETTINGS = 0x0012,
HEC_UNEXPECTED_FRAME = 0x0013,
HEC_REQUEST_REJECTED = 0x14,
HEC_SETTINGS_ERROR = 0x00FF,
HEC_MALFORMED_FRAME = 0x0100, /* add frame type */
HEC_QPACK_DECOMPRESSION_FAILED = 0x200,
HEC_QPACK_ENCODER_STREAM_ERROR = 0x201,
HEC_QPACK_DECODER_STREAM_ERROR = 0x202,
};
struct h3_prio_frame_read_state
{
struct varint_read_state h3pfrs_vint;
struct hq_priority h3pfrs_prio;
enum {
H3PFRS_STATE_TYPE = 0,
H3PFRS_STATE_VINT_BEGIN,
H3PFRS_STATE_VINT_CONTINUE,
H3PFRS_STATE_WEIGHT,
} h3pfrs_state;
enum {
H3PFRS_FLAG_HAVE_PRIO_ID = 1 << 0,
} h3pfrs_flags;
};
enum h3_prio_frame_read_status
{
H3PFR_STATUS_DONE,
H3PFR_STATUS_NEED,
};
/* When first called, h3pfrs_state should be set to 0 */
enum h3_prio_frame_read_status
lsquic_h3_prio_frame_read (const unsigned char **, size_t,
struct h3_prio_frame_read_state *);
#endif

View File

@ -0,0 +1,163 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hspack_valid.c -- Handshake packet validator.
*
* We want to eliminate invalid packets as soon as we read them in and not
* feed them to lsquic engine if we can avoid it. The handshake packet
* possesses several characteristics which make it possible to detect
* garbage packets.
*/
#include <assert.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_ietf.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_version.h"
#include "lsquic_parse_common.h"
#define SMALLEST_GQUIC_OVERHEAD \
1 /* Type */ \
+ GQUIC_CID_LEN \
+ sizeof(lsquic_ver_tag_t) \
+ 1 /* Packet number */ \
+ 1 /* Stream frame */ \
+ 1 /* Stream ID */ \
+ 2 /* Data length */ \
+ 12 /* IV */
/* Note that we ignore nonce: even if the flag is set, we know that Chrome
* does not actually include the 32-byte nonce.
*/
static int
is_valid_gquic_hs_packet (const unsigned char *buf, size_t bufsz,
lsquic_ver_tag_t *tag)
{
if (bufsz > GQUIC_MAX_PACKET_SZ ||
/* Data: HPACKed :method GET :path / is 2 bytes */
bufsz < SMALLEST_GQUIC_OVERHEAD + 2 ||
/* Check maximum packet number: */
buf[1 + GQUIC_CID_LEN + sizeof(lsquic_ver_tag_t)] > 64 ||
/* From [draft-hamilton-quic-transport-protocol-01]:
* 0x80 is currently unused, and must be set to 0.
* 0x40 = MULTIPATH. This bit is reserved for multipath use.
*
* 0x30 = Packet number length. We expect these bits to be
* unset.
*
* The reference implementation checks that two high bits are not
* set if version flag is not set or if the version is the same.
* For our purposes, all GQUIC version we support so far have these
* bits set to zero.
*
* Incoming handshake packets must have both connection ID and
* version bits set.
*
* Nonce flag is ignored: Chrome sets it erronesously, but it may
* not be true (a) in the future or (b) in other clients.
*/
((buf[0] ^ (
/* These should be unset: */
(~(0x80|0x40|0x30|PACKET_PUBLIC_FLAGS_RST))
&
/* While these should be set: */
(PACKET_PUBLIC_FLAGS_VERSION|
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)
)) & /* Ignore this bit: */ ~PACKET_PUBLIC_FLAGS_NONCE)
)
{
return 0;
}
memcpy(tag, buf + 1 + 8, sizeof(*tag));
return 1;
}
int
lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
const unsigned char *buf, size_t bufsz)
{
lsquic_ver_tag_t tag;
int is_valid;
if (bufsz < 1)
return 0;
switch (buf[0] & 0xF8)
{
/* Xs vary, Gs are iGnored: */
/* 1X11 XGGG: Q046 long header */
case 0x80|0x40|0x20|0x10|0x08:
case 0x80|0x00|0x20|0x10|0x08:
case 0x80|0x40|0x20|0x10|0x00:
case 0x80|0x00|0x20|0x10|0x00:
is_valid = lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
break;
/* 1X00 XGGG: ID-22 long header */
case 0x80|0x40|0x00|0x00|0x08:
case 0x80|0x00|0x00|0x00|0x08:
case 0x80|0x40|0x00|0x00|0x00:
case 0x80|0x00|0x00|0x00|0x00:
/* 1X01 XGGG: ID-22 long header */
case 0x80|0x40|0x00|0x10|0x08:
case 0x80|0x00|0x00|0x10|0x08:
case 0x80|0x40|0x00|0x10|0x00:
case 0x80|0x00|0x00|0x10|0x00:
/* 1X10 XGGG: ID-22 long header */
case 0x80|0x40|0x20|0x00|0x08:
case 0x80|0x00|0x20|0x00|0x08:
case 0x80|0x40|0x20|0x00|0x00:
case 0x80|0x00|0x20|0x00|0x00:
is_valid = lsquic_is_valid_ietf_v1_or_Q046_hs_packet(buf, bufsz, &tag);
break;
/* 01XX XGGG: ID-22 short header */
case 0x00|0x40|0x00|0x00|0x00:
case 0x00|0x40|0x00|0x00|0x08:
case 0x00|0x40|0x00|0x10|0x00:
case 0x00|0x40|0x00|0x10|0x08:
case 0x00|0x40|0x20|0x00|0x00:
case 0x00|0x40|0x20|0x00|0x08:
case 0x00|0x40|0x20|0x10|0x00:
case 0x00|0x40|0x20|0x10|0x08:
is_valid = 0;
break;
/* 00XX 0GGG: Q046 short header */
case 0x00|0x00|0x00|0x00|0x00:
case 0x00|0x00|0x00|0x10|0x00:
case 0x00|0x00|0x20|0x00|0x00:
case 0x00|0x00|0x20|0x10|0x00:
is_valid = 0;
break;
/* 00XX 1GGG: GQUIC */
case 0x00|0x00|0x00|0x00|0x08:
case 0x00|0x00|0x00|0x10|0x08:
case 0x00|0x00|0x20|0x00|0x08:
case 0x00|0x00|0x20|0x10|0x08:
is_valid = is_valid_gquic_hs_packet(buf, bufsz, &tag);
break;
default: /* gcc thinks this is possible?! */
assert(0);
is_valid = 0;
break;
}
if (is_valid)
{
return 1;
}
else
return 0;
}

View File

@ -11,7 +11,7 @@
#include "lshpack.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HTTP1X
#define LSQUIC_LOG_CONN_ID hwc->hwc_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(hwc->hwc_conn)
#include "lsquic_logger.h"
enum pseudo_header
@ -36,7 +36,7 @@ enum pseudo_header
struct header_writer_ctx
{
lsquic_cid_t hwc_cid;
const struct lsquic_conn *hwc_conn;
char *buf;
char *cookie_val;
unsigned cookie_sz, cookie_nalloc;
@ -75,7 +75,7 @@ h1h_create_header_set (void *ctx, int is_push_promise)
if (is_push_promise)
hwc->hwc_flags |= HWC_PUSH_PROMISE;
hwc->max_headers_sz = hcc->max_headers_sz;
hwc->hwc_cid = hcc->cid;
hwc->hwc_conn = hcc->conn;
return &hwc->hwc_h1h;
}

View File

@ -3,14 +3,17 @@
#define LSQUIC_HTTP1X_IF_H 1
struct lsquic_hset_if;
struct lsquic_conn;
struct http1x_ctor_ctx
{
lsquic_cid_t cid; /* Used for logging */
const struct lsquic_conn *conn; /* Used for logging */
unsigned max_headers_sz;
int is_server;
};
extern const struct lsquic_hset_if *const lsquic_http1x_if;
#define MAX_HTTP1X_HEADERS_SIZE (64 * 1024)
#endif

View File

@ -0,0 +1,28 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_IETF_H
#define LSQUIC_IETF_H 1
/* Things specific to the IETF version of QUIC that do not fit anywhere else */
/* [draft-ietf-quic-transport-18] Section 22.3 */
enum trans_error_code
{
TEC_NO_ERROR = 0x0,
TEC_INTERNAL_ERROR = 0x1,
TEC_SERVER_BUSY = 0x2,
TEC_FLOW_CONTROL_ERROR = 0x3,
TEC_STREAM_LIMIT_ERROR = 0x4,
TEC_STREAM_STATE_ERROR = 0x5,
TEC_FINAL_SIZE_ERROR = 0x6,
TEC_FRAME_ENCODING_ERROR = 0x7,
TEC_TRANSPORT_PARAMETER_ERROR = 0x8,
TEC_VERSION_NEGOTIATION_ERROR = 0x9,
TEC_PROTOCOL_VIOLATION = 0xA,
TEC_INVALID_MIGRATION = 0xC,
TEC_CRYPTO_BUFFER_EXCEEDED = 0xD,
};
/* Must be at least two */
#define MAX_IETF_CONN_DCIDS 8
#endif

View File

@ -19,4 +19,13 @@ struct lsquic_packno_range {
lsquic_packno_t low, high;
};
/* RFC 3168 */
enum ecn
{
ECN_NOT_ECT = 0,
ECN_ECT1 = 1,
ECN_ECT0 = 2,
ECN_CE = 3,
};
#endif

View File

@ -16,43 +16,34 @@
#include <time.h>
#define LSQUIC_LOGGER_MODULE LSQLM_LOGGER /* Quis custodiet ipsos custodes? */
#include "lsquic_logger.h"
#include "lsquic_byteswap.h"
#include "lsquic.h"
#include "lsquic_logger.h"
/* The switch to big-endian format in GQUIC also resulted in Chrome swapping
* the CID in its log. We do the same thing in our log messages so that the
* CIDs are easy to match. The exception is Q035, which is the last little-
* endian GQUIC version this library supports.
*/
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define DISP_CID(cid) bswap_64(cid)
#else
#define DISP_CID(cid) (cid)
#endif
#define MAX_LINE_LEN 8192
#define FORMAT_PROBLEM(lb, len, max) (((ssize_t)lb < 0) || ((ssize_t)lb + (ssize_t)len >= (ssize_t)max))
/* TODO: display GQUIC CIDs in Chrome-compatible format */
static enum lsquic_logger_timestamp_style g_llts = LLTS_NONE;
static int
null_vprintf (void *ctx, const char *fmt, va_list ap)
null_log_buf (void *ctx, const char *buf, size_t len)
{
return 0;
}
static int
file_vprintf (void *ctx, const char *fmt, va_list ap)
file_log_buf (void *ctx, const char *buf, size_t len)
{
return vfprintf((FILE *) ctx, fmt, ap);
return (int)fwrite(buf, sizeof(char), len, (FILE *) ctx);
}
static const struct lsquic_logger_if file_logger_if = {
.vprintf = file_vprintf,
.log_buf = file_log_buf,
};
static const struct lsquic_logger_if null_logger_if = {
.vprintf = null_vprintf,
.log_buf = null_log_buf,
};
static void *logger_ctx = NULL;
@ -74,18 +65,31 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_CRYPTO] = LSQ_LOG_WARN,
[LSQLM_HANDSHAKE] = LSQ_LOG_WARN,
[LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
[LSQLM_BBR] = LSQ_LOG_WARN,
[LSQLM_CUBIC] = LSQ_LOG_WARN,
[LSQLM_HEADERS] = LSQ_LOG_WARN,
[LSQLM_FRAME_READER]= LSQ_LOG_WARN,
[LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
[LSQLM_CONN_HASH] = LSQ_LOG_WARN,
[LSQLM_MINI_CONN] = LSQ_LOG_WARN,
[LSQLM_TOKGEN] = LSQ_LOG_WARN,
[LSQLM_ENG_HIST] = LSQ_LOG_WARN,
[LSQLM_SPI] = LSQ_LOG_WARN,
[LSQLM_DI] = LSQ_LOG_WARN,
[LSQLM_PRQ] = LSQ_LOG_WARN,
[LSQLM_PACER] = LSQ_LOG_WARN,
[LSQLM_MIN_HEAP] = LSQ_LOG_WARN,
[LSQLM_HTTP1X] = LSQ_LOG_WARN,
[LSQLM_QLOG] = LSQ_LOG_WARN,
[LSQLM_TRAPA] = LSQ_LOG_WARN,
[LSQLM_PURGA] = LSQ_LOG_WARN,
[LSQLM_HCSI_READER] = LSQ_LOG_WARN,
[LSQLM_HCSO_WRITER] = LSQ_LOG_WARN,
[LSQLM_QENC_HDL] = LSQ_LOG_WARN,
[LSQLM_QDEC_HDL] = LSQ_LOG_WARN,
[LSQLM_QPACK_ENC] = LSQ_LOG_WARN,
[LSQLM_QPACK_DEC] = LSQ_LOG_WARN,
[LSQLM_PRIO] = LSQ_LOG_WARN,
[LSQLM_BW_SAMPLER] = LSQ_LOG_WARN,
};
const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
@ -104,18 +108,31 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_CRYPTO] = "crypto",
[LSQLM_HANDSHAKE] = "handshake",
[LSQLM_HSK_ADAPTER] = "hsk-adapter",
[LSQLM_BBR] = "bbr",
[LSQLM_CUBIC] = "cubic",
[LSQLM_HEADERS] = "headers",
[LSQLM_FRAME_READER]= "frame-reader",
[LSQLM_FRAME_WRITER]= "frame-writer",
[LSQLM_CONN_HASH] = "conn-hash",
[LSQLM_MINI_CONN] = "mini-conn",
[LSQLM_TOKGEN] = "tokgen",
[LSQLM_ENG_HIST] = "eng-hist",
[LSQLM_SPI] = "spi",
[LSQLM_DI] = "di",
[LSQLM_PRQ] = "prq",
[LSQLM_PACER] = "pacer",
[LSQLM_MIN_HEAP] = "min-heap",
[LSQLM_HTTP1X] = "http1x",
[LSQLM_QLOG] = "qlog",
[LSQLM_TRAPA] = "trapa",
[LSQLM_PURGA] = "purga",
[LSQLM_HCSI_READER] = "hcsi-reader",
[LSQLM_HCSO_WRITER] = "hcso-writer",
[LSQLM_QENC_HDL] = "qenc-hdl",
[LSQLM_QDEC_HDL] = "qdec-hdl",
[LSQLM_QPACK_ENC] = "qpack-enc",
[LSQLM_QPACK_DEC] = "qpack-dec",
[LSQLM_PRIO] = "prio",
[LSQLM_BW_SAMPLER] = "bw-sampler",
};
const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
@ -130,16 +147,6 @@ const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
};
static void
lsquic_printf (const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
va_end(ap);
}
#ifdef WIN32
#define DELTA_EPOCH_IN_TICKS 116444736000000000Ui64
struct timezone
@ -180,16 +187,16 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
return 0;
}
#endif
static void
print_timestamp (void)
static size_t
print_timestamp (char *buf, size_t max)
{
struct tm tm;
struct timeval tv;
size_t len = 0;
gettimeofday(&tv, NULL);
#ifdef WIN32
{
@ -204,43 +211,66 @@ print_timestamp (void)
localtime_r(&tv.tv_sec, &tm);
#endif
if (g_llts == LLTS_YYYYMMDD_HHMMSSUS)
lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%06d ",
len = snprintf(buf, max, "%04d-%02d-%02d %02d:%02d:%02d.%06d ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec));
else if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
lsquic_printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ",
len = snprintf(buf, max, "%04d-%02d-%02d %02d:%02d:%02d.%03d ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
else if (g_llts == LLTS_HHMMSSMS)
lsquic_printf("%02d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) (tv.tv_usec / 1000));
len = snprintf(buf, max, "%02d:%02d:%02d.%03d ",
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
else if (g_llts == LLTS_HHMMSSUS)
lsquic_printf("%02d:%02d:%02d.%06d ", tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) tv.tv_usec);
len = snprintf(buf, max, "%02d:%02d:%02d.%06d ",
tm.tm_hour, tm.tm_min, tm.tm_sec, (int) tv.tv_usec);
else if (g_llts == LLTS_CHROMELIKE)
lsquic_printf("%02d%02d/%02d%02d%02d.%06d ", tm.tm_mon + 1,
tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, (int) tv.tv_usec);
len = snprintf(buf, max, "%02d%02d/%02d%02d%02d.%06d ",
tm.tm_mon + 1, tm.tm_mday,tm.tm_hour, tm.tm_min,
tm.tm_sec, (int) tv.tv_usec);
return len;
}
void
lsquic_logger_log3 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
uint64_t conn_id, uint32_t stream_id, const char *fmt, ...)
const lsquic_cid_t *conn_id, lsquic_stream_id_t stream_id,
const char *fmt, ...)
{
const int saved_errno = errno;
char cidbuf_[MAX_CID_LEN * 2 + 1];
size_t len = 0;
size_t lb;
size_t max = MAX_LINE_LEN;
char buf[MAX_LINE_LEN];
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] [QUIC:%"PRIu64"-%"PRIu32"] %s: ",
lsq_loglevel2str[log_level], DISP_CID(conn_id), stream_id,
lsqlm_to_str[module]);
{
lb = print_timestamp(buf, max);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
}
lb = snprintf(buf + len, max - len, "[%s] [QUIC:%"CID_FMT"-%"PRIu64"] %s: ",
lsq_loglevel2str[log_level], CID_BITS(conn_id),
stream_id, lsqlm_to_str[module]);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
lb = vsnprintf(buf + len, max - len, fmt, ap);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_end(ap);
lsquic_printf("\n");
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
logger_if->log_buf(logger_ctx, buf, len);
end:
errno = saved_errno;
}
@ -248,20 +278,41 @@ lsquic_logger_log3 (enum lsq_log_level log_level,
void
lsquic_logger_log2 (enum lsq_log_level log_level,
enum lsquic_logger_module module,
uint64_t conn_id, const char *fmt, ...)
const struct lsquic_cid *conn_id, const char *fmt, ...)
{
const int saved_errno = errno;
char cidbuf_[MAX_CID_LEN * 2 + 1];
size_t len = 0;
size_t lb;
size_t max = MAX_LINE_LEN;
char buf[MAX_LINE_LEN];
if (g_llts != LLTS_NONE)
print_timestamp();
{
lb = print_timestamp(buf, max);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
}
lsquic_printf("[%s] [QUIC:%"PRIu64"] %s: ",
lsq_loglevel2str[log_level], DISP_CID(conn_id), lsqlm_to_str[module]);
lb = snprintf(buf + len, max - len, "[%s] [QUIC:%"CID_FMT"] %s: ",
lsq_loglevel2str[log_level], CID_BITS(conn_id), lsqlm_to_str[module]);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
lb = vsnprintf(buf + len, max - len, fmt, ap);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_end(ap);
lsquic_printf("\n");
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
logger_if->log_buf(logger_ctx, buf, len);
end:
errno = saved_errno;
}
@ -272,17 +323,36 @@ lsquic_logger_log1 (enum lsq_log_level log_level,
const char *fmt, ...)
{
const int saved_errno = errno;
size_t len = 0;
size_t lb;
size_t max = MAX_LINE_LEN;
char buf[MAX_LINE_LEN];
if (g_llts != LLTS_NONE)
print_timestamp();
lsquic_printf("[%s] %s: ", lsq_loglevel2str[log_level],
{
lb = print_timestamp(buf, max);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
}
lb = snprintf(buf + len, max - len, "[%s] %s: ", lsq_loglevel2str[log_level],
lsqlm_to_str[module]);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
lb = vsnprintf(buf + len, max - len, fmt, ap);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_end(ap);
lsquic_printf("\n");
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
logger_if->log_buf(logger_ctx, buf, len);
end:
errno = saved_errno;
}
@ -291,16 +361,36 @@ void
lsquic_logger_log0 (enum lsq_log_level log_level, const char *fmt, ...)
{
const int saved_errno = errno;
size_t len = 0;
size_t lb;
size_t max = MAX_LINE_LEN;
char buf[MAX_LINE_LEN];
if (g_llts != LLTS_NONE)
print_timestamp();
{
lb = print_timestamp(buf, max);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
}
lsquic_printf("[%s] ", lsq_loglevel2str[log_level]);
lb = snprintf(buf + len, max - len, "[%s] ", lsq_loglevel2str[log_level]);
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
va_list ap;
va_start(ap, fmt);
logger_if->vprintf(logger_ctx, fmt, ap);
lb = vsnprintf(buf + len, max - len, fmt, ap);
va_end(ap);
lsquic_printf("\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
lb = snprintf(buf + len, max - len, "\n");
if (FORMAT_PROBLEM(lb, len, max))
goto end;
len += lb;
logger_if->log_buf(logger_ctx, buf, len);
end:
errno = saved_errno;
}
@ -406,3 +496,19 @@ lsquic_set_log_level (const char *level_str)
else
return -1;
}
/* `out' must be at least MAX_CID_LEN * 2 + 1 characters long */
void
lsquic_cid2str (const lsquic_cid_t *cid, char *out)
{
static const char hex[] = "0123456789ABCDEF";
int i;
for (i = 0; i < (int) cid->len; ++i)
{
*out++ = hex[ cid->idbuf[i] >> 4 ];
*out++ = hex[ cid->idbuf[i] & 0xF ];
}
*out = '\0';
}

View File

@ -61,18 +61,31 @@ enum lsquic_logger_module {
LSQLM_CRYPTO,
LSQLM_HANDSHAKE,
LSQLM_HSK_ADAPTER,
LSQLM_BBR,
LSQLM_CUBIC,
LSQLM_HEADERS,
LSQLM_FRAME_WRITER,
LSQLM_FRAME_READER,
LSQLM_CONN_HASH,
LSQLM_MINI_CONN,
LSQLM_TOKGEN,
LSQLM_ENG_HIST,
LSQLM_SPI,
LSQLM_DI,
LSQLM_PRQ,
LSQLM_PACER,
LSQLM_MIN_HEAP,
LSQLM_HTTP1X,
LSQLM_QLOG,
LSQLM_TRAPA,
LSQLM_PURGA,
LSQLM_HCSI_READER,
LSQLM_HCSO_WRITER,
LSQLM_QENC_HDL,
LSQLM_QDEC_HDL,
LSQLM_QPACK_ENC,
LSQLM_QPACK_DEC,
LSQLM_PRIO,
LSQLM_BW_SAMPLER,
N_LSQUIC_LOGGER_MODULES
};
@ -89,6 +102,8 @@ extern const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS];
#define LSQ_LOG_ENABLED(level) LSQ_LOG_ENABLED_EXT(level, LSQUIC_LOGGER_MODULE)
struct lsquic_cid;
/* The functions that perform actual logging are void. This is an
* optimization. In majority of cases the calls will succeed; even if
* they fail, there is nothing (at least, nothing simple) to be done to
@ -108,8 +123,8 @@ extern const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS];
void
lsquic_logger_log3 (enum lsq_log_level, enum lsquic_logger_module,
uint64_t conn_id, uint32_t stream_id,
const char *format, ...)
const struct lsquic_cid *conn_id,
lsquic_stream_id_t stream_id, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 5, 6)))
#endif
@ -117,12 +132,13 @@ lsquic_logger_log3 (enum lsq_log_level, enum lsquic_logger_module,
# define LSQ_LOG3(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log3(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, LSQUIC_LOG_STREAM_ID, __VA_ARGS__); \
LSQUIC_LOG_CONN_ID, LSQUIC_LOG_STREAM_ID, __VA_ARGS__); \
} while (0)
void
lsquic_logger_log2 (enum lsq_log_level, enum lsquic_logger_module,
uint64_t conn_id, const char *format, ...)
const struct lsquic_cid *conn_id, const char *format, ...)
#if __GNUC__
__attribute__((format(printf, 4, 5)))
#endif
@ -130,7 +146,15 @@ lsquic_logger_log2 (enum lsq_log_level, enum lsquic_logger_module,
# define LSQ_LOG2(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log2(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, __VA_ARGS__); \
LSQUIC_LOG_CONN_ID, __VA_ARGS__); \
} while (0)
# define LSQ_LOG2C(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
{ \
char cidbuf_[MAX_CID_LEN * 2 + 1]; \
lsquic_logger_log2(level, LSQUIC_LOGGER_MODULE, \
LSQUIC_LOG_CONN_ID, __VA_ARGS__); \
} \
} while (0)
void
@ -144,6 +168,13 @@ lsquic_logger_log1 (enum lsq_log_level, enum lsquic_logger_module,
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log1(level, LSQUIC_LOGGER_MODULE, __VA_ARGS__); \
} while (0)
# define LSQ_LOG1C(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
{ \
char cidbuf_[MAX_CID_LEN * 2 + 1]; \
lsquic_logger_log1(level, LSQUIC_LOGGER_MODULE, __VA_ARGS__); \
} \
} while (0)
void
lsquic_logger_log0 (enum lsq_log_level, const char *format, ...)
@ -155,6 +186,13 @@ lsquic_logger_log0 (enum lsq_log_level, const char *format, ...)
if (LSQ_LOG_ENABLED(level)) \
lsquic_logger_log0(level, __VA_ARGS__); \
} while (0)
# define LSQ_LOG0C(level, ...) do { \
if (LSQ_LOG_ENABLED(level)) \
{ \
char cidbuf_[MAX_CID_LEN * 2 + 1]; \
lsquic_logger_log0(level, __VA_ARGS__); \
} \
} while (0)
#if defined(LSQUIC_LOGGER_MODULE)
#if defined(LSQUIC_LOG_CONN_ID)
@ -162,12 +200,15 @@ lsquic_logger_log0 (enum lsq_log_level, const char *format, ...)
# define LSQ_LOG LSQ_LOG3
#else
# define LSQ_LOG LSQ_LOG2
# define LSQ_LOGC LSQ_LOG2C
#endif
#else
# define LSQ_LOG LSQ_LOG1
# define LSQ_LOGC LSQ_LOG1C
#endif
#else
# define LSQ_LOG LSQ_LOG0
# define LSQ_LOGC LSQ_LOG0C
# define LSQUIC_LOGGER_MODULE LSQLM_NOMODULE
#endif
@ -180,6 +221,15 @@ lsquic_logger_log0 (enum lsq_log_level, const char *format, ...)
#define LSQ_INFO(...) LSQ_LOG(LSQ_LOG_INFO, __VA_ARGS__)
#define LSQ_EMERG(...) LSQ_LOG(LSQ_LOG_EMERG, __VA_ARGS__)
#define LSQ_DEBUGC(...) LSQ_LOGC(LSQ_LOG_DEBUG, __VA_ARGS__)
#define LSQ_WARNC(...) LSQ_LOGC(LSQ_LOG_WARN, __VA_ARGS__)
#define LSQ_ALERTC(...) LSQ_LOGC(LSQ_LOG_ALERT, __VA_ARGS__)
#define LSQ_CRITC(...) LSQ_LOGC(LSQ_LOG_CRIT, __VA_ARGS__)
#define LSQ_ERRORC(...) LSQ_LOGC(LSQ_LOG_ERROR, __VA_ARGS__)
#define LSQ_NOTICEC(...) LSQ_LOGC(LSQ_LOG_NOTICE, __VA_ARGS__)
#define LSQ_INFOC(...) LSQ_LOGC(LSQ_LOG_INFO, __VA_ARGS__)
#define LSQ_EMERGC(...) LSQ_LOGC(LSQ_LOG_EMERG, __VA_ARGS__)
/* Shorthand for printing to file streams using internal lsquic_logger_if
*/
void
@ -197,6 +247,17 @@ lsquic_str_to_log_level (const char *);
int
lsquic_logger_lopt (const char *optarg);
#define CID_FMT ".*s"
#define CID_BITS(cid) 2 * (int) (cid)->len, \
(lsquic_cid2str(cid, cidbuf_), cidbuf_)
void
lsquic_cid2str (const struct lsquic_cid *, char *out);
const struct lsquic_cid *
lsquic_conn_log_cid (const struct lsquic_conn *);
#ifdef __cplusplus
}
#endif

View File

@ -7,7 +7,8 @@
* does the following:
*
* 1. Allocations occur 4 KB at a time.
* 2. No division or multiplication operations are performed.
* 2. No division or multiplication operations are performed for
* appropriately sized objects. (More on this below.)
*
* (In recent testing, malo was about 2.7 times faster than malloc for
* 64-byte objects.)
@ -18,18 +19,17 @@
* 1. To free (put) an object, one does not need a pointer to the malo
* object. This makes this allocator easy to use.
* 2. A built-in iterator is provided to iterate over all allocated
* objects (with ability to safely release objects while iterator
* objects (with ability safely to release objects while iterator
* is active). This may be useful in some circumstances.
*
* To gain all these advantages, there are trade-offs:
*
* 1. There are two memory penalties:
* a. Per object overhead. To avoid division and multiplication,
* the object sizes is rounded up to the nearest power or two,
* starting with 64 bytes (minumum) and up to 2 KB (maximum).
* Thus, a 104-byte object will have a 24-byte overhead; a
* 130-byte object will have 126-byte overhead. This is
* something to keep in mind.
* a. Per object overhead. If an object is at least ROUNDUP_THRESH in
* size as the next power of two, the allocator uses that power of
* two value as the object size. This is done to avoid using
* division and multiplication. For example, a 104-byte object
* will have a 24-byte overhead.
* b. Per page overhead. Page links occupy some bytes in the
* page. To keep things fast, at least one slot per page is
* always occupied, independent of object size. Thus, for a
@ -56,17 +56,25 @@
#include "fiu-local.h"
#include "lsquic_malo.h"
#ifndef LSQUIC_USE_POOLS
#define LSQUIC_USE_POOLS 1
#endif
/* 64 slots in a 4KB page means that the smallest object is 64 bytes.
* The largest object is 2KB.
*/
#define MALO_MIN_NBITS 6
#define MALO_MAX_NBITS 11
#define ROUNDUP_THRESH 0.75f
/* A "free page" is a page with free slots available.
*/
#if LSQUIC_USE_POOLS
static unsigned find_free_slot (uint64_t slots);
static unsigned size_in_bits (size_t sz);
#endif
struct malo_page {
SLIST_ENTRY(malo_page) next_page;
@ -74,14 +82,25 @@ struct malo_page {
struct malo *malo;
uint64_t slots,
full_slot_mask;
unsigned nbits;
unsigned nbits; /* If pow is zero, stores object size */
unsigned initial_slot;
int pow; /* True if object is power of 2 */
};
typedef char malo_header_fits_in_one_slot
[(sizeof(struct malo_page) > (1 << MALO_MAX_NBITS)) ? -1 : 1];
[(sizeof(struct malo_page) > (1 << MALO_MIN_NBITS)) ? -1 : 1];
#if !LSQUIC_USE_POOLS
struct nopool_elem
{
TAILQ_ENTRY(nopool_elem) next;
struct malo *malo;
unsigned char data[0];
};
#endif
struct malo {
#if LSQUIC_USE_POOLS
struct malo_page page_header;
SLIST_HEAD(, malo_page) all_pages;
LIST_HEAD(, malo_page) free_pages;
@ -89,12 +108,23 @@ struct malo {
struct malo_page *cur_page;
unsigned next_slot;
} iter;
#else
/* List of all elements: used by the iterator */
TAILQ_HEAD(, nopool_elem) elems;
size_t obj_size;
struct nopool_elem *next_iter_elem;
#endif
};
struct malo *
lsquic_malo_create (size_t obj_size)
{
unsigned nbits = size_in_bits(obj_size);
#if LSQUIC_USE_POOLS
int pow, n_slots;
unsigned nbits;
obj_size = (obj_size + 7) & -8;
nbits = size_in_bits(obj_size);
if (nbits < MALO_MIN_NBITS)
nbits = MALO_MIN_NBITS;
else if (nbits > MALO_MAX_NBITS)
@ -103,6 +133,9 @@ lsquic_malo_create (size_t obj_size)
return NULL;
}
pow = obj_size <= (1 << MALO_MIN_NBITS)
|| (float) obj_size / (1 << nbits) > ROUNDUP_THRESH;
struct malo *malo;
if (0 != posix_memalign((void **) &malo, 0x1000, 0x1000))
return NULL;
@ -112,25 +145,52 @@ lsquic_malo_create (size_t obj_size)
malo->iter.cur_page = &malo->page_header;
malo->iter.next_slot = 0;
int n_slots = sizeof(*malo) / (1 << nbits)
if (pow)
n_slots = sizeof(*malo) / (1 << nbits)
+ ((sizeof(*malo) % (1 << nbits)) > 0);
else
n_slots = sizeof(*malo) / obj_size
+ ((sizeof(*malo) % obj_size) > 0);
struct malo_page *const page = &malo->page_header;
SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
page->malo = malo;
if (nbits == MALO_MIN_NBITS)
if (!pow)
page->full_slot_mask = (1ULL << (0x1000 / obj_size)) - 1;
else if (nbits == MALO_MIN_NBITS)
page->full_slot_mask = ~0ULL;
else
page->full_slot_mask = (1ULL << (1 << (12 - nbits))) - 1;
page->slots = (1ULL << n_slots) - 1;
page->nbits = nbits;
page->pow = pow;
page->nbits = pow ? nbits : obj_size;
page->initial_slot = n_slots;
return malo;
#else
struct malo *malo;
/* Use the same sizing mechanism as in the normal version */
if (obj_size < (1 << MALO_MIN_NBITS))
obj_size = 1 << MALO_MIN_NBITS;
else
obj_size = (obj_size + 7) & -8;
malo = malloc(sizeof(*malo));
if (malo)
{
TAILQ_INIT(&malo->elems);
malo->obj_size = obj_size;
return malo;
}
else
return NULL;
#endif
}
#if LSQUIC_USE_POOLS
static struct malo_page *
allocate_page (struct malo *malo)
{
@ -142,10 +202,12 @@ allocate_page (struct malo *malo)
page->slots = 1;
page->full_slot_mask = malo->page_header.full_slot_mask;
page->nbits = malo->page_header.nbits;
page->pow = malo->page_header.pow;
page->malo = malo;
page->initial_slot = 1;
return page;
}
#endif
#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
@ -154,6 +216,7 @@ allocate_page (struct malo *malo)
void *
lsquic_malo_get (struct malo *malo)
{
#if LSQUIC_USE_POOLS
fiu_do_on("malo/get", FAIL_NOMEM);
struct malo_page *page = LIST_FIRST(&malo->free_pages);
if (!page)
@ -166,7 +229,22 @@ lsquic_malo_get (struct malo *malo)
page->slots |= (1ULL << slot);
if (page->full_slot_mask == page->slots)
LIST_REMOVE(page, next_free_page);
return (char *) page + (slot << page->nbits);
if (page->pow)
return (char *) page + (slot << page->nbits);
else
return (char *) page + (slot * page->nbits);
#else
struct nopool_elem *el;
el = malloc(sizeof(*el) + malo->obj_size);
if (el)
{
TAILQ_INSERT_HEAD(&malo->elems, el, next);
el->malo = malo;
return el->data;
}
else
return NULL;
#endif
}
@ -174,18 +252,32 @@ lsquic_malo_get (struct malo *malo)
void
lsquic_malo_put (void *obj)
{
#if LSQUIC_USE_POOLS
uintptr_t page_addr = (uintptr_t) obj & ~((1 << 12) - 1);
struct malo_page *page = (void *) page_addr;
unsigned slot = ((uintptr_t) obj - page_addr) >> page->nbits;
unsigned slot;
if (page->pow)
slot = ((uintptr_t) obj - page_addr) >> page->nbits;
else
slot = ((uintptr_t) obj - page_addr) / page->nbits;
if (page->full_slot_mask == page->slots)
LIST_INSERT_HEAD(&page->malo->free_pages, page, next_free_page);
page->slots &= ~(1ULL << slot);
#else
struct nopool_elem *el;
el = (struct nopool_elem *) ((char *) obj - sizeof(*el));
if (el == el->malo->next_iter_elem)
el->malo->next_iter_elem = TAILQ_NEXT(el->malo->next_iter_elem, next);
TAILQ_REMOVE(&el->malo->elems, el, next);
free(el);
#endif
}
void
lsquic_malo_destroy (struct malo *malo)
{
#if LSQUIC_USE_POOLS
struct malo_page *page, *next;
page = SLIST_FIRST(&malo->all_pages);
while (page != &malo->page_header)
@ -203,6 +295,15 @@ lsquic_malo_destroy (struct malo *malo)
#else
_aligned_free(page);
#endif
#else
struct nopool_elem *el, *next_el;
for (el = TAILQ_FIRST(&malo->elems); el; el = next_el)
{
next_el = TAILQ_NEXT(el, next);
free(el);
}
free(malo);
#endif
}
@ -214,8 +315,12 @@ lsquic_malo_destroy (struct malo *malo)
void *
lsquic_malo_first (struct malo *malo)
{
#if LSQUIC_USE_POOLS
malo->iter.cur_page = SLIST_FIRST(&malo->all_pages);
malo->iter.next_slot = malo->iter.cur_page->initial_slot;
#else
malo->next_iter_elem = TAILQ_FIRST(&malo->elems);
#endif
return lsquic_malo_next(malo);
}
@ -223,13 +328,17 @@ lsquic_malo_first (struct malo *malo)
void *
lsquic_malo_next (struct malo *malo)
{
#if LSQUIC_USE_POOLS
struct malo_page *page;
unsigned max_slot, slot;
page = malo->iter.cur_page;
if (page)
{
max_slot = 1 << (12 - page->nbits); /* Same for all pages */
if (page->pow)
max_slot = 1 << (12 - page->nbits); /* Same for all pages */
else
max_slot = 0x1000 / page->nbits;
slot = malo->iter.next_slot;
while (1)
{
@ -239,7 +348,13 @@ lsquic_malo_next (struct malo *malo)
{
malo->iter.cur_page = page;
malo->iter.next_slot = slot + 1;
return (char *) page + (slot << page->nbits);
if (page->pow)
return (char *) page + (slot << page->nbits);
else
{
assert(slot * (page->nbits + 1) < 0x1000);
return (char *) page + (slot * page->nbits);
}
}
}
page = SLIST_NEXT(page, next_page);
@ -254,14 +369,27 @@ lsquic_malo_next (struct malo *malo)
}
return NULL;
#else
struct nopool_elem *retval;
if (malo->next_iter_elem)
{
retval = malo->next_iter_elem;
malo->next_iter_elem = TAILQ_NEXT(malo->next_iter_elem, next);
return retval->data;
}
else
return NULL;
#endif
}
#if LSQUIC_USE_POOLS
static unsigned
size_in_bits (size_t sz)
{
#if __GNUC__
unsigned clz = __builtin_clz(sz - 1);
unsigned clz = sz > 1 ? __builtin_clz(sz - 1) : 31;
return 32 - clz;
#elif defined(WIN32)
unsigned char s;
@ -306,11 +434,13 @@ find_free_slot (uint64_t slots)
return n;
#endif
}
#endif
size_t
lsquic_malo_mem_used (const struct malo *malo)
{
#if LSQUIC_USE_POOLS
const struct malo_page *page;
size_t size;
@ -319,4 +449,7 @@ lsquic_malo_mem_used (const struct malo *malo)
size += sizeof(*page);
return size;
#else
return 0;
#endif
}

View File

@ -6,6 +6,10 @@
#ifndef LSQUIC_MALO_H
#define LSQUIC_MALO_H 1
#ifndef LSQUIC_USE_POOLS
#define LSQUIC_USE_POOLS 1
#endif
struct malo;
/* Create a malo allocator for objects of size `obj_size'. */
@ -24,6 +28,10 @@ lsquic_malo_put (void *obj);
void
lsquic_malo_destroy (struct malo *);
/* This iterator is slow. It is only used in unit tests for verification.
*
* If you to iterate over all elements allocated in a pool, keep track yourself.
*/
/* The iterator is built-in. Usage:
* void *obj;
* for (obj = lsquic_malo_first(obj); obj; lsquic_malo_next(obj))

View File

@ -8,6 +8,7 @@
#include "lsquic_min_heap.h"
#define LSQUIC_LOGGER_MODULE LSQLM_MIN_HEAP
#include "lsquic_types.h"
#include "lsquic_logger.h"
#define MHE_PARENT(i) ((i - 1) / 2)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_mini_conn.h -- Mini-connection
*
* Before a connection is established, the server keeps a "mini" connection
* object where it keeps track of stream 1 offsets and so on.
*/
#ifndef LSQUIC_MINI_CONN_H
#define LSQUIC_MINI_CONN_H
#include <stdint.h>
#include <sys/queue.h>
#define MAX_MINI_CONN_LIFESPAN_IN_USEC \
((1 << (sizeof(((struct mini_conn *) 0)->mc_largest_recv) * 8)) - 1)
struct lsquic_packet_in;
struct lsquic_packet_out;
struct lsquic_engine_public;
#ifndef LSQUIC_KEEP_MINICONN_HISTORY
# ifndef NDEBUG
# define LSQUIC_KEEP_MINICONN_HISTORY 0 /* XXX */
# else
# define LSQUIC_KEEP_MINICONN_HISTORY 0
# endif
#endif
#if LSQUIC_KEEP_MINICONN_HISTORY
#define MCHIST_BITS 4
#define MCHIST_MASK ((1 << MCHIST_BITS) - 1)
typedef unsigned char mchist_idx_t;
enum miniconn_history_event
{
MCHE_EMPTY = '\0',
MCHE_PLUS = '+',
MCHE_HANDLE_1RTT = '1',
MCHE_HANDLE_2RTT = '2',
MCHE_PACKET2LARGE_IN = 'a',
MCHE_CONN_CLOSE = 'c',
MCHE_CREATED = 'C',
MCHE_2HSK_1STREAM = 'd',
MCHE_DUP_HSK = 'D',
MCHE_HANDLE_ERROR = 'e',
MCHE_EFRAME = 'f',
MCHE_UNDECR_DEFER = 'F',
MCHE_HANDLE_NOT_ENOUGH = 'g',
MCHE_NEW_HSK = 'H',
MCHE_INVALID_FRAME = 'I',
MCHE_DECRYPTED = 'K',
MCHE_PACKET_LOST = 'L',
MCHE_HELLO_TOO_MUCH = 'm',
MCHE_ENOMEM = 'M',
MCHE_NEW_PACKET_OUT = 'N',
MCHE_HELLO_HOLE = 'o',
MCHE_PACKET_DUP_IN = 'p',
MCHE_UNDECR_DROP = 'P',
MCHE_PRST_IN = 'R',
MCHE_HANDLE_SHLO = 's',
MCHE_NEW_ENC_SESS = 'S',
MCHE_PACKET_SENT = 'T',
MCHE_HAHDLE_UNKNOWN = 'u',
MCHE_UNSENT_ACKED = 'U',
MCHE_HANDLE_DELAYED = 'y',
MCHE_PACKET_DELAYED = 'Y',
MCHE_PACKET0_IN = 'z',
MCHE_OUT_OF_PACKNOS = '#',
};
#endif
#ifndef LSQUIC_RECORD_INORD_HIST
# if __GNUC__
# define LSQUIC_RECORD_INORD_HIST 0 /* XXX */
# else
# define LSQUIC_RECORD_INORD_HIST 0
# endif
#endif
typedef uint64_t mconn_packno_set_t;
#define MINICONN_MAX_PACKETS (sizeof(mconn_packno_set_t) * 8)
TAILQ_HEAD(head_packet_in, lsquic_packet_in);
struct mini_conn {
struct lsquic_conn mc_conn;
struct conn_cid_elem mc_cces[1];
struct head_packet_in mc_deferred,
mc_packets_in;
TAILQ_HEAD(, lsquic_packet_out)
mc_packets_out;
struct lsquic_engine_public
*mc_enpub;
lsquic_time_t mc_created;
struct lsquic_rtt_stats
mc_rtt_stats;
mconn_packno_set_t mc_received_packnos,
mc_sent_packnos,
mc_deferred_packnos, /* Informational */
mc_dropped_packnos, /* Informational */
mc_lost_packnos, /* Packets that were deemed lost */ /* Informational */
mc_acked_packnos;
#if LSQUIC_RECORD_INORD_HIST
unsigned long long mc_inord_hist[2]; /* Informational */
#endif
uint32_t mc_error_code; /* From CONNECTION_CLOSE frame */ /* Informational */
unsigned short mc_n_ticks; /* Number of times mini conn ticked. */ /* Informational */
unsigned short mc_read_off, /* Read offset for stream 1 */
mc_write_off;/* Write offset for stream 1 */
unsigned char mc_max_ack_packno,
/* XXX: mc_max_ack_packno is never set */
mc_cutoff,
mc_cur_packno;
unsigned char mc_hsk_count;
#if LSQUIC_RECORD_INORD_HIST
unsigned char mc_inord_idx;
#endif
/* mc_largest_recv is the timestamp of when packet with the largest
* number was received; it is necessary to generate ACK frames. 24
* bits holds about 16.5 seconds worth of microseconds, which is
* larger than the maximum amount of time a mini connection object
* is allowed to live. To get the timestamp, add this value to
* mc_created.
*/
unsigned char mc_largest_recv[3];
enum {
MC_HAVE_NEW_HSK = (1 << 0),
MC_PROMOTE = (1 << 1),
MC_HAVE_SHLO = (1 << 2),
MC_UNUSED_3 = (1 << 3),
MC_ERROR = (1 << 4),
MC_UNSENT_ACK = (1 << 5),
MC_GEN_ACK = (1 << 6),
MC_HSK_ERR = (1 << 7),
MC_OO_PACKNOS = (1 << 8),
MC_STOP_WAIT_ON = (1 << 9),
} mc_flags:16;
struct network_path mc_path;
#if LSQUIC_KEEP_MINICONN_HISTORY
mchist_idx_t mc_hist_idx;
unsigned char mc_hist_buf[1 << MCHIST_BITS];
#endif
};
lsquic_conn_t *
mini_conn_new (struct lsquic_engine_public *, const struct lsquic_packet_in *,
enum lsquic_version version);
/* Packet numbers start with 1. By subtracting 1, we can utilize the full
* length of the bitmask.
*/
#define MCONN_PACKET_MASK(packno) (1ULL << (packno - 1))
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_mini_conn_ietf.h -- Mini connection used by the IETF QUIC
*/
#ifndef LSQUIC_MINI_CONN_IETF_H
#define LSQUIC_MINI_CONN_IETF_H 1
struct lsquic_conn;
struct lsquic_engine_public;
struct lsquic_packet_in;
enum { MCSBIT_WANTREAD, MCSBIT_WANTWRITE, };
struct mini_crypto_stream
{
unsigned mcs_read_off;
unsigned mcs_write_off;
enum {
MCS_WANTREAD = 1 << MCSBIT_WANTREAD,
MCS_WANTWRITE = 1 << MCSBIT_WANTWRITE,
MCS_CREATED = 1 << 2,
} mcs_flags:8;
enum enc_level mcs_enc_level:8;
};
typedef uint64_t packno_set_t;
#define MAX_PACKETS ((sizeof(packno_set_t) * 8) - 1)
struct ietf_mini_conn
{
struct lsquic_conn imc_conn;
struct conn_cid_elem imc_cces[3];
struct lsquic_engine_public *imc_enpub;
lsquic_time_t imc_created;
enum {
IMC_ENC_SESS_INITED = 1 << 0,
IMC_QUEUED_ACK_INIT = 1 << 1,
IMC_QUEUED_ACK_HSK = IMC_QUEUED_ACK_INIT << PNS_HSK,
IMC_QUEUED_ACK_APP = IMC_QUEUED_ACK_INIT << PNS_APP,
IMC_ERROR = 1 << 4,
IMC_HSK_OK = 1 << 5,
IMC_HSK_FAILED = 1 << 6,
IMC_HAVE_TP = 1 << 7,
IMC_RETRY_MODE = 1 << 8,
IMC_RETRY_DONE = 1 << 9,
IMC_IGNORE_INIT = 1 << 10,
#define IMCBIT_PNS_BIT_SHIFT 11
IMC_MAX_PNS_BIT_0 = 1 << 11,
IMC_MAX_PNS_BIT_1 = 1 << 12,
IMC_TLS_ALERT = 1 << 13,
IMC_ABORT_ERROR = 1 << 14,
IMC_ABORT_ISAPP = 1 << 15,
IMC_BAD_TRANS_PARAMS = 1 << 16,
IMC_ADDR_VALIDATED = 1 << 17,
} imc_flags;
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
void *imc_stream_ps[N_ENC_LEVS];
struct {
struct stream_frame *frame; /* Latest frame - on stack - be careful. */
enum enc_level enc_level;
} imc_last_in;
TAILQ_HEAD(, lsquic_packet_in) imc_app_packets;
TAILQ_HEAD(, lsquic_packet_out) imc_packets_out;
packno_set_t imc_sent_packnos;
packno_set_t imc_recvd_packnos[N_PNS];
packno_set_t imc_acked_packnos[N_PNS];
lsquic_time_t imc_largest_recvd[N_PNS];
struct lsquic_rtt_stats imc_rtt_stats;
unsigned imc_error_code;
unsigned imc_bytes_in;
unsigned imc_bytes_out;
unsigned char imc_next_packno;
unsigned char imc_hsk_count;
uint8_t imc_ack_exp;
uint8_t imc_ecn_counts_in[N_PNS][4];
uint8_t imc_ecn_counts_out[N_PNS][4];
uint8_t imc_incoming_ecn;
uint8_t imc_tls_alert;
struct network_path imc_path;
};
struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
const struct lsquic_packet_in *,
enum lsquic_version, int is_ipv4, const struct lsquic_cid *);
#endif

View File

@ -0,0 +1,155 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Based on Google code released under BSD license here:
* https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
*/
/*
* Copyright 2017, Google Inc.
*
* Use of this source code is governed by the following BSD-style license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Kathleen Nichols' algorithm for tracking the minimum (or maximum)
* value of a data stream over some fixed time interval. (E.g.,
* the minimum RTT over the past five minutes.) It uses constant
* space and constant time per update yet almost always delivers
* the same minimum as an implementation that has to keep all the
* data in the window.
*
* The algorithm keeps track of the best, 2nd best & 3rd best min
* values, maintaining an invariant that the measurement time of
* the n'th best >= n-1'th best. It also makes sure that the three
* values are widely separated in the time window since that bounds
* the worse case error when that data is monotonically increasing
* over the window.
*
* Upon getting a new min, we can forget everything earlier because
* it has no value - the new min is <= everything else in the window
* by definition and it'samples the most recent. So we restart fresh on
* every new min and overwrites 2nd & 3rd choices. The same property
* holds for 2nd & 3rd best.
*/
#include <stdint.h>
#include <string.h>
#include "lsquic_minmax.h"
/* As time advances, update the 1st, 2nd, and 3rd choices. */
static void
minmax_subwin_update (struct minmax *minmax, const struct minmax_sample *sample)
{
uint64_t dt = sample->time - minmax->samples[0].time;
if (dt > minmax->window)
{
/*
* Passed entire window without a new sample so make 2nd
* choice the new sample & 3rd choice the new 2nd choice.
* we may have to iterate this since our 2nd choice
* may also be outside the window (we checked on entry
* that the third choice was in the window).
*/
minmax->samples[0] = minmax->samples[1];
minmax->samples[1] = minmax->samples[2];
minmax->samples[2] = *sample;
if (sample->time - minmax->samples[0].time > minmax->window) {
minmax->samples[0] = minmax->samples[1];
minmax->samples[1] = minmax->samples[2];
minmax->samples[2] = *sample;
}
}
else if (minmax->samples[1].time == minmax->samples[0].time
&& dt > minmax->window / 4)
{
/*
* We've passed a quarter of the window without a new sample
* so take a 2nd choice from the 2nd quarter of the window.
*/
minmax->samples[2] = minmax->samples[1] = *sample;
}
else if (minmax->samples[2].time == minmax->samples[1].time
&& dt > minmax->window / 2)
{
/*
* We've passed half the window without finding a new sample
* so take a 3rd choice from the last half of the window
*/
minmax->samples[2] = *sample;
}
}
/* Check if new measurement updates the 1st, 2nd or 3rd choice max. */
void
lsquic_minmax_update_max (struct minmax *minmax, uint64_t now, uint64_t meas)
{
struct minmax_sample sample = { .time = now, .value = meas };
if (minmax->samples[0].value == 0 /* uninitialized */
|| sample.value >= minmax->samples[0].value /* found new max? */
|| sample.time - minmax->samples[2].time > minmax->window) /* nothing left in window? */
{
minmax_reset(minmax, sample); /* forget earlier samples */
return;
}
if (sample.value >= minmax->samples[1].value)
minmax->samples[2] = minmax->samples[1] = sample;
else if (sample.value >= minmax->samples[2].value)
minmax->samples[2] = sample;
minmax_subwin_update(minmax, &sample);
}
/* Check if new measurement updates the 1st, 2nd or 3rd choice min. */
void
lsquic_minmax_update_min (struct minmax *minmax, uint64_t now, uint64_t meas)
{
struct minmax_sample sample = { .time = now, .value = meas };
if (minmax->samples[0].value == 0 /* uninitialized */
|| sample.value <= minmax->samples[0].value /* found new min? */
|| sample.time - minmax->samples[2].time > minmax->window) /* nothing left in window? */
{
minmax_reset(minmax, sample); /* forget earlier samples */
return;
}
if (sample.value <= minmax->samples[1].value)
minmax->samples[2] = minmax->samples[1] = sample;
else if (sample.value <= minmax->samples[2].value)
minmax->samples[2] = sample;
minmax_subwin_update(minmax, &sample);
}

View File

@ -0,0 +1,44 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_MINMAX_H
#define LSQUIC_MINMAX_H
/* Windowed min/max tracker by Kathleen Nichols.
*
* Based on Google code released under BSD license here:
* https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
*/
struct minmax_sample
{
uint64_t time;
uint64_t value;
};
struct minmax
{
uint64_t window;
struct minmax_sample samples[3];
};
#define minmax_get_idx(minmax_, idx_) ((minmax_)->samples[idx_].value)
#define minmax_get(minmax_) minmax_get_idx(minmax_, 0)
#define minmax_reset(minmax_, sample_) do { \
(minmax_)->samples[0] = (minmax_)->samples[1] \
= (minmax_)->samples[2] = (sample_); \
} while (0)
#define minmax_init(minmax_, window_) do { \
(minmax_)->window = (window_); \
minmax_reset(minmax_, ((struct minmax_sample) { 0, 0, })); \
} while (0)
void lsquic_minmax_update_min(struct minmax *, uint64_t now, uint64_t meas);
void lsquic_minmax_update_max(struct minmax *, uint64_t now, uint64_t meas);
#define minmax_upmin lsquic_minmax_update_min
#define minmax_upmax lsquic_minmax_update_max
#endif

View File

@ -14,22 +14,45 @@
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_sizes.h"
#include "lsquic_malo.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_rtt.h"
#include "lsquic_packet_common.h"
#include "lsquic_mini_conn.h"
#include "lsquic_enc_sess.h"
#include "lsquic_mini_conn_ietf.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_full_conn.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#ifndef LSQUIC_LOG_POOL_STATS
#define LSQUIC_LOG_POOL_STATS 0
#endif
#if LSQUIC_LOG_POOL_STATS
#include "lsquic_logger.h"
#endif
#ifndef LSQUIC_USE_POOLS
#define LSQUIC_USE_POOLS 1
#endif
#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
struct payload_buf
struct packet_in_buf
{
SLIST_ENTRY(payload_buf) next_pb;
SLIST_ENTRY(packet_in_buf) next_pib;
};
struct packet_out_buf
@ -51,21 +74,32 @@ struct sixteen_k_page
int
lsquic_mm_init (struct lsquic_mm *mm)
{
#if LSQUIC_USE_POOLS
int i;
#endif
mm->acki = malloc(sizeof(*mm->acki));
mm->malo.stream_frame = lsquic_malo_create(sizeof(struct stream_frame));
mm->malo.stream_rec_arr = lsquic_malo_create(sizeof(struct stream_rec_arr));
mm->malo.mini_conn = lsquic_malo_create(sizeof(struct mini_conn));
mm->malo.mini_conn_ietf = lsquic_malo_create(sizeof(struct ietf_mini_conn));
mm->malo.packet_in = lsquic_malo_create(sizeof(struct lsquic_packet_in));
mm->malo.packet_out = lsquic_malo_create(sizeof(struct lsquic_packet_out));
mm->malo.dcid_elem = lsquic_malo_create(sizeof(struct dcid_elem));
mm->malo.stream_hq_frame
= lsquic_malo_create(sizeof(struct stream_hq_frame));
#if LSQUIC_USE_POOLS
TAILQ_INIT(&mm->free_packets_in);
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
SLIST_INIT(&mm->packet_out_bufs[i]);
SLIST_INIT(&mm->payload_bufs);
for (i = 0; i < MM_N_IN_BUCKETS; ++i)
SLIST_INIT(&mm->packet_in_bufs[i]);
SLIST_INIT(&mm->four_k_pages);
SLIST_INIT(&mm->sixteen_k_pages);
if (mm->acki && mm->malo.stream_frame && mm->malo.stream_rec_arr &&
mm->malo.packet_in)
#endif
if (mm->acki && mm->malo.stream_frame && mm->malo.stream_rec_arr
&& mm->malo.mini_conn && mm->malo.mini_conn_ietf && mm->malo.packet_in
&& mm->malo.stream_hq_frame)
{
return 0;
}
@ -77,18 +111,25 @@ lsquic_mm_init (struct lsquic_mm *mm)
void
lsquic_mm_cleanup (struct lsquic_mm *mm)
{
#if LSQUIC_USE_POOLS
int i;
struct packet_out_buf *pob;
struct payload_buf *pb;
struct packet_in_buf *pib;
struct four_k_page *fkp;
struct sixteen_k_page *skp;
#endif
free(mm->acki);
lsquic_malo_destroy(mm->malo.stream_hq_frame);
lsquic_malo_destroy(mm->malo.dcid_elem);
lsquic_malo_destroy(mm->malo.packet_in);
lsquic_malo_destroy(mm->malo.packet_out);
lsquic_malo_destroy(mm->malo.stream_frame);
lsquic_malo_destroy(mm->malo.stream_rec_arr);
lsquic_malo_destroy(mm->malo.mini_conn);
lsquic_malo_destroy(mm->malo.mini_conn_ietf);
#if LSQUIC_USE_POOLS
for (i = 0; i < MM_N_OUT_BUCKETS; ++i)
while ((pob = SLIST_FIRST(&mm->packet_out_bufs[i])))
{
@ -96,11 +137,12 @@ lsquic_mm_cleanup (struct lsquic_mm *mm)
free(pob);
}
while ((pb = SLIST_FIRST(&mm->payload_bufs)))
{
SLIST_REMOVE_HEAD(&mm->payload_bufs, next_pb);
free(pb);
}
for (i = 0; i < MM_N_IN_BUCKETS; ++i)
while ((pib = SLIST_FIRST(&mm->packet_in_bufs[i])))
{
SLIST_REMOVE_HEAD(&mm->packet_in_bufs[i], next_pib);
free(pib);
}
while ((fkp = SLIST_FIRST(&mm->four_k_pages)))
{
@ -113,6 +155,56 @@ lsquic_mm_cleanup (struct lsquic_mm *mm)
SLIST_REMOVE_HEAD(&mm->sixteen_k_pages, next_skp);
free(skp);
}
#endif
}
#if LSQUIC_USE_POOLS
enum {
PACKET_IN_PAYLOAD_0 = 1370, /* common QUIC payload size upperbound */
PACKET_IN_PAYLOAD_1 = 4096, /* payload size middleground guess */
PACKET_IN_PAYLOAD_2 = 0xffff, /* UDP payload size upperbound */
};
static const unsigned packet_in_sizes[] = {
PACKET_IN_PAYLOAD_0,
PACKET_IN_PAYLOAD_1,
PACKET_IN_PAYLOAD_2,
};
static unsigned
packet_in_index (unsigned size)
{
unsigned idx = (size > PACKET_IN_PAYLOAD_0)
+ (size > PACKET_IN_PAYLOAD_1);
return idx;
}
#endif
void
lsquic_mm_put_packet_in (struct lsquic_mm *mm,
struct lsquic_packet_in *packet_in)
{
#if LSQUIC_USE_POOLS
unsigned idx;
struct packet_in_buf *pib;
assert(0 == packet_in->pi_refcnt);
if (packet_in->pi_flags & PI_OWN_DATA)
{
pib = (struct packet_in_buf *) packet_in->pi_data;
idx = packet_in_index(packet_in->pi_data_sz);
SLIST_INSERT_HEAD(&mm->packet_in_bufs[idx], pib, next_pib);
}
TAILQ_INSERT_HEAD(&mm->free_packets_in, packet_in, pi_next);
#else
if (packet_in->pi_flags & PI_OWN_DATA)
free(packet_in->pi_data);
lsquic_malo_put(packet_in);
#endif
}
@ -123,6 +215,7 @@ lsquic_mm_get_packet_in (struct lsquic_mm *mm)
fiu_do_on("mm/packet_in", FAIL_NOMEM);
#if LSQUIC_USE_POOLS
packet_in = TAILQ_FIRST(&mm->free_packets_in);
if (packet_in)
{
@ -130,6 +223,7 @@ lsquic_mm_get_packet_in (struct lsquic_mm *mm)
TAILQ_REMOVE(&mm->free_packets_in, packet_in, pi_next);
}
else
#endif
packet_in = lsquic_malo_get(mm->malo.packet_in);
if (packet_in)
@ -139,11 +233,14 @@ lsquic_mm_get_packet_in (struct lsquic_mm *mm)
}
#if LSQUIC_USE_POOLS
/* Based on commonly used MTUs, ordered from small to large: */
enum {
PACKET_OUT_PAYLOAD_0 = 1280 - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_1 = QUIC_MAX_IPv6_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_2 = QUIC_MAX_IPv4_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_0 = 1280 - GQUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_1 = GQUIC_MAX_IPv6_PACKET_SZ - GQUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_2 = GQUIC_MAX_IPv4_PACKET_SZ - GQUIC_MIN_PACKET_OVERHEAD,
PACKET_OUT_PAYLOAD_3 = 4096,
PACKET_OUT_PAYLOAD_4 = 0xffff,
};
@ -151,6 +248,8 @@ static const unsigned packet_out_sizes[] = {
PACKET_OUT_PAYLOAD_0,
PACKET_OUT_PAYLOAD_1,
PACKET_OUT_PAYLOAD_2,
PACKET_OUT_PAYLOAD_3,
PACKET_OUT_PAYLOAD_4,
};
@ -158,15 +257,120 @@ static unsigned
packet_out_index (unsigned size)
{
unsigned idx = (size > PACKET_OUT_PAYLOAD_0)
+ (size > PACKET_OUT_PAYLOAD_1);
+ (size > PACKET_OUT_PAYLOAD_1)
+ (size > PACKET_OUT_PAYLOAD_2)
+ (size > PACKET_OUT_PAYLOAD_3);
return idx;
}
#endif
#if LSQUIC_USE_POOLS
#define POOL_SAMPLE_PERIOD 1024
static void
poolst_sample_max (struct pool_stats *poolst)
{
#define ALPHA_SHIFT 3
#define BETA_SHIFT 2
unsigned diff;
if (poolst->ps_max_avg)
{
poolst->ps_max_var -= poolst->ps_max_var >> BETA_SHIFT;
if (poolst->ps_max_avg > poolst->ps_max)
diff = poolst->ps_max_avg - poolst->ps_max;
else
diff = poolst->ps_max - poolst->ps_max_avg;
poolst->ps_max_var += diff >> BETA_SHIFT;
poolst->ps_max_avg -= poolst->ps_max_avg >> ALPHA_SHIFT;
poolst->ps_max_avg += poolst->ps_max >> ALPHA_SHIFT;
}
else
{
/* First measurement */
poolst->ps_max_avg = poolst->ps_max;
poolst->ps_max_var = poolst->ps_max / 2;
}
poolst->ps_calls = 0;
poolst->ps_max = poolst->ps_objs_out;
#if LSQUIC_LOG_POOL_STATS
LSQ_DEBUG("new sample: max avg: %u; var: %u", poolst->ps_max_avg,
poolst->ps_max_var);
#endif
}
static void
poolst_allocated (struct pool_stats *poolst, unsigned new)
{
poolst->ps_objs_out += 1;
poolst->ps_objs_all += new;
if (poolst->ps_objs_out > poolst->ps_max)
poolst->ps_max = poolst->ps_objs_out;
++poolst->ps_calls;
if (0 == poolst->ps_calls % POOL_SAMPLE_PERIOD)
poolst_sample_max(poolst);
}
static void
poolst_freed (struct pool_stats *poolst)
{
--poolst->ps_objs_out;
++poolst->ps_calls;
if (0 == poolst->ps_calls % POOL_SAMPLE_PERIOD)
poolst_sample_max(poolst);
}
static int
poolst_has_new_sample (const struct pool_stats *poolst)
{
return poolst->ps_calls == 0;
}
/* If average maximum falls under 1/4 of all objects allocated, release
* half of the objects allocated.
*/
static void
maybe_shrink_packet_out_bufs (struct lsquic_mm *mm, unsigned idx)
{
struct pool_stats *poolst;
struct packet_out_buf *pob;
unsigned n_to_leave;
poolst = &mm->packet_out_bstats[idx];
if (poolst->ps_max_avg * 4 < poolst->ps_objs_all)
{
n_to_leave = poolst->ps_objs_all / 2;
while (poolst->ps_objs_all > n_to_leave
&& (pob = SLIST_FIRST(&mm->packet_out_bufs[idx])))
{
SLIST_REMOVE_HEAD(&mm->packet_out_bufs[idx], next_pob);
free(pob);
--poolst->ps_objs_all;
}
#if LSQUIC_LOG_POOL_STATS
LSQ_DEBUG("pool #%u; max avg %u; shrank from %u to %u objs",
idx, poolst->ps_max_avg, n_to_leave * 2, poolst->ps_objs_all);
#endif
}
#if LSQUIC_LOG_POOL_STATS
else
LSQ_DEBUG("pool #%u; max avg %u; objs: %u; won't shrink",
idx, poolst->ps_max_avg, poolst->ps_objs_all);
#endif
}
#endif
void
lsquic_mm_put_packet_out (struct lsquic_mm *mm,
struct lsquic_packet_out *packet_out)
{
#if LSQUIC_USE_POOLS
struct packet_out_buf *pob;
unsigned idx;
@ -174,6 +378,14 @@ lsquic_mm_put_packet_out (struct lsquic_mm *mm,
pob = (struct packet_out_buf *) packet_out->po_data;
idx = packet_out_index(packet_out->po_n_alloc);
SLIST_INSERT_HEAD(&mm->packet_out_bufs[idx], pob, next_pob);
poolst_freed(&mm->packet_out_bstats[idx]);
if (poolst_has_new_sample(&mm->packet_out_bstats[idx]))
maybe_shrink_packet_out_bufs(mm, idx);
if (packet_out->po_bwp_state)
lsquic_malo_put(packet_out->po_bwp_state);
#else
free(packet_out->po_data);
#endif
lsquic_malo_put(packet_out);
}
@ -184,9 +396,9 @@ lsquic_mm_get_packet_out (struct lsquic_mm *mm, struct malo *malo,
{
struct lsquic_packet_out *packet_out;
struct packet_out_buf *pob;
#if LSQUIC_USE_POOLS
unsigned idx;
assert(size <= QUIC_MAX_PAYLOAD_SZ);
#endif
fiu_do_on("mm/packet_out", FAIL_NOMEM);
@ -194,10 +406,14 @@ lsquic_mm_get_packet_out (struct lsquic_mm *mm, struct malo *malo,
if (!packet_out)
return NULL;
#if LSQUIC_USE_POOLS
idx = packet_out_index(size);
pob = SLIST_FIRST(&mm->packet_out_bufs[idx]);
if (pob)
{
SLIST_REMOVE_HEAD(&mm->packet_out_bufs[idx], next_pob);
poolst_allocated(&mm->packet_out_bstats[idx], 0);
}
else
{
pob = malloc(packet_out_sizes[idx]);
@ -206,7 +422,18 @@ lsquic_mm_get_packet_out (struct lsquic_mm *mm, struct malo *malo,
lsquic_malo_put(packet_out);
return NULL;
}
poolst_allocated(&mm->packet_out_bstats[idx], 1);
}
if (poolst_has_new_sample(&mm->packet_out_bstats[idx]))
maybe_shrink_packet_out_bufs(mm, idx);
#else
pob = malloc(size);
if (!pob)
{
lsquic_malo_put(packet_out);
return NULL;
}
#endif
memset(packet_out, 0, sizeof(*packet_out));
packet_out->po_n_alloc = size;
@ -217,29 +444,46 @@ lsquic_mm_get_packet_out (struct lsquic_mm *mm, struct malo *malo,
void *
lsquic_mm_get_1370 (struct lsquic_mm *mm)
lsquic_mm_get_packet_in_buf (struct lsquic_mm *mm, size_t size)
{
struct payload_buf *pb = SLIST_FIRST(&mm->payload_bufs);
fiu_do_on("mm/1370", FAIL_NOMEM);
if (pb)
SLIST_REMOVE_HEAD(&mm->payload_bufs, next_pb);
struct packet_in_buf *pib;
#if LSQUIC_USE_POOLS
unsigned idx;
idx = packet_in_index(size);
pib = SLIST_FIRST(&mm->packet_in_bufs[idx]);
fiu_do_on("mm/packet_in_buf", FAIL_NOMEM);
if (pib)
SLIST_REMOVE_HEAD(&mm->packet_in_bufs[idx], next_pib);
else
pb = malloc(1370);
return pb;
pib = malloc(packet_in_sizes[idx]);
#else
pib = malloc(size);
#endif
return pib;
}
void
lsquic_mm_put_1370 (struct lsquic_mm *mm, void *mem)
lsquic_mm_put_packet_in_buf (struct lsquic_mm *mm, void *mem, size_t size)
{
struct payload_buf *pb = mem;
SLIST_INSERT_HEAD(&mm->payload_bufs, pb, next_pb);
#if LSQUIC_USE_POOLS
unsigned idx;
struct packet_in_buf *pib;
pib = (struct packet_in_buf *) mem;
idx = packet_in_index(size);
SLIST_INSERT_HEAD(&mm->packet_in_bufs[idx], pib, next_pib);
#else
free(mem);
#endif
}
void *
lsquic_mm_get_4k (struct lsquic_mm *mm)
{
#if LSQUIC_USE_POOLS
struct four_k_page *fkp = SLIST_FIRST(&mm->four_k_pages);
fiu_do_on("mm/4k", FAIL_NOMEM);
if (fkp)
@ -247,20 +491,28 @@ lsquic_mm_get_4k (struct lsquic_mm *mm)
else
fkp = malloc(0x1000);
return fkp;
#else
return malloc(0x1000);
#endif
}
void
lsquic_mm_put_4k (struct lsquic_mm *mm, void *mem)
{
#if LSQUIC_USE_POOLS
struct four_k_page *fkp = mem;
SLIST_INSERT_HEAD(&mm->four_k_pages, fkp, next_fkp);
#else
free(mem);
#endif
}
void *
lsquic_mm_get_16k (struct lsquic_mm *mm)
{
#if LSQUIC_USE_POOLS
struct sixteen_k_page *skp = SLIST_FIRST(&mm->sixteen_k_pages);
fiu_do_on("mm/16k", FAIL_NOMEM);
if (skp)
@ -268,33 +520,30 @@ lsquic_mm_get_16k (struct lsquic_mm *mm)
else
skp = malloc(16 * 1024);
return skp;
#else
return malloc(16 * 1024);
#endif
}
void
lsquic_mm_put_16k (struct lsquic_mm *mm, void *mem)
{
#if LSQUIC_USE_POOLS
struct sixteen_k_page *skp = mem;
SLIST_INSERT_HEAD(&mm->sixteen_k_pages, skp, next_skp);
}
void
lsquic_mm_put_packet_in (struct lsquic_mm *mm,
struct lsquic_packet_in *packet_in)
{
assert(0 == packet_in->pi_refcnt);
if (packet_in->pi_flags & PI_OWN_DATA)
lsquic_mm_put_1370(mm, packet_in->pi_data);
TAILQ_INSERT_HEAD(&mm->free_packets_in, packet_in, pi_next);
#else
free(mem);
#endif
}
size_t
lsquic_mm_mem_used (const struct lsquic_mm *mm)
{
#if LSQUIC_USE_POOLS
const struct packet_out_buf *pob;
const struct payload_buf *pb;
const struct packet_in_buf *pib;
const struct four_k_page *fkp;
const struct sixteen_k_page *skp;
unsigned i;
@ -304,6 +553,8 @@ lsquic_mm_mem_used (const struct lsquic_mm *mm)
size += sizeof(*mm->acki);
size += lsquic_malo_mem_used(mm->malo.stream_frame);
size += lsquic_malo_mem_used(mm->malo.stream_rec_arr);
size += lsquic_malo_mem_used(mm->malo.mini_conn);
size += lsquic_malo_mem_used(mm->malo.mini_conn_ietf);
size += lsquic_malo_mem_used(mm->malo.packet_in);
size += lsquic_malo_mem_used(mm->malo.packet_out);
@ -311,8 +562,9 @@ lsquic_mm_mem_used (const struct lsquic_mm *mm)
SLIST_FOREACH(pob, &mm->packet_out_bufs[i], next_pob)
size += packet_out_sizes[i];
SLIST_FOREACH(pb, &mm->payload_bufs, next_pb)
size += 1370;
for (i = 0; i < MM_N_IN_BUCKETS; ++i)
SLIST_FOREACH(pib, &mm->packet_in_bufs[i], next_pib)
size += packet_in_sizes[i];
SLIST_FOREACH(fkp, &mm->four_k_pages, next_fkp)
size += 0x1000;
@ -321,4 +573,7 @@ lsquic_mm_mem_used (const struct lsquic_mm *mm)
size += 0x4000;
return size;
#else
return sizeof(*mm);
#endif
}

View File

@ -14,20 +14,38 @@ struct lsquic_packet_in;
struct lsquic_packet_out;
struct ack_info;
struct malo;
struct mini_conn;
#define MM_N_OUT_BUCKETS 3
struct pool_stats
{
unsigned ps_calls; /* Calls to get/put */
unsigned ps_max; /* Maximum during this sample period */
unsigned ps_max_avg, /* Average maximum value */
ps_max_var;
unsigned ps_objs_all; /* Number of objects owned by the pool */
unsigned ps_objs_out; /* Number of objects in use */
};
#define MM_N_OUT_BUCKETS 5
#define MM_N_IN_BUCKETS 3
struct lsquic_mm {
struct ack_info *acki;
struct {
struct malo *stream_frame; /* For struct stream_frame */
struct malo *stream_rec_arr;/* For struct stream_rec_arr */
struct malo *mini_conn; /* For struct mini_conn */
struct malo *mini_conn_ietf;/* For struct ietf_mini_conn */
struct malo *retry_conn; /* For struct retry_conn */
struct malo *packet_in; /* For struct lsquic_packet_in */
struct malo *packet_out; /* For struct lsquic_packet_out */
struct malo *dcid_elem; /* For struct dcid_elem */
struct malo *stream_hq_frame; /* For struct stream_hq_frame */
} malo;
TAILQ_HEAD(, lsquic_packet_in) free_packets_in;
SLIST_HEAD(, packet_out_buf) packet_out_bufs[MM_N_OUT_BUCKETS];
SLIST_HEAD(, payload_buf) payload_bufs;
struct pool_stats packet_out_bstats[MM_N_OUT_BUCKETS];
SLIST_HEAD(, packet_in_buf) packet_in_bufs[MM_N_IN_BUCKETS];
SLIST_HEAD(, four_k_page) four_k_pages;
SLIST_HEAD(, sixteen_k_page) sixteen_k_pages;
};
@ -58,10 +76,10 @@ void
lsquic_mm_put_packet_out (struct lsquic_mm *, struct lsquic_packet_out *);
void *
lsquic_mm_get_1370 (struct lsquic_mm *);
lsquic_mm_get_packet_in_buf (struct lsquic_mm *, size_t);
void
lsquic_mm_put_1370 (struct lsquic_mm *, void *);
lsquic_mm_put_packet_in_buf (struct lsquic_mm *, void *, size_t);
void *
lsquic_mm_get_4k (struct lsquic_mm *);

View File

@ -11,11 +11,12 @@
#include "lsquic_int_types.h"
#include "lsquic_pacer.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_out.h"
#include "lsquic_util.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PACER
#define LSQUIC_LOG_CONN_ID pacer->pa_cid
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(pacer->pa_conn)
#include "lsquic_logger.h"
#ifndef MAX
@ -24,11 +25,12 @@
void
pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned clock_granularity)
pacer_init (struct pacer *pacer, const struct lsquic_conn *conn,
unsigned clock_granularity)
{
memset(pacer, 0, sizeof(*pacer));
pacer->pa_burst_tokens = 10;
pacer->pa_cid = cid;
pacer->pa_conn = conn;
pacer->pa_clock_granularity = clock_granularity;
}
@ -52,6 +54,7 @@ pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
#ifndef NDEBUG
++pacer->pa_stats.n_scheduled;
#endif
++pacer->pa_n_scheduled;
if (n_in_flight == 0 && !in_recovery)
{
@ -90,7 +93,7 @@ pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
sched_time + delay);
LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
pacer->pa_next_sched - lsquic_time_now());
pacer->pa_next_sched - pacer->pa_now);
}
@ -123,8 +126,25 @@ pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
void
pacer_tick (struct pacer *pacer, lsquic_time_t now)
pacer_tick_in (struct pacer *pacer, lsquic_time_t now)
{
assert(now >= pacer->pa_now);
pacer->pa_now = now;
if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
pacer->pa_flags |= PA_DELAYED_ON_TICK_IN;
pacer->pa_n_scheduled = 0;
}
void
pacer_tick_out (struct pacer *pacer)
{
if ((pacer->pa_flags & PA_DELAYED_ON_TICK_IN)
&& pacer->pa_n_scheduled == 0
&& pacer->pa_now > pacer->pa_next_sched)
{
LSQ_DEBUG("tick passed without scheduled packets: reset delayed flag");
pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
}
pacer->pa_flags &= ~PA_DELAYED_ON_TICK_IN;
}

View File

@ -2,9 +2,12 @@
#ifndef LSQUIC_PACER_H
#define LSQUIC_PACER_H 1
struct lsquic_conn;
struct pacer
{
lsquic_cid_t pa_cid; /* Used for logging */
const struct lsquic_conn
*pa_conn; /* Used for logging */
lsquic_time_t pa_next_sched;
lsquic_time_t pa_last_delayed;
lsquic_time_t pa_now;
@ -14,8 +17,10 @@ struct pacer
unsigned pa_clock_granularity;
unsigned pa_burst_tokens;
unsigned pa_n_scheduled; /* Within single tick */
enum {
PA_LAST_SCHED_DELAYED = (1 << 0),
PA_DELAYED_ON_TICK_IN = (1 << 1),
} pa_flags;
#ifndef NDEBUG
struct {
@ -28,13 +33,17 @@ struct pacer
typedef lsquic_time_t (*tx_time_f)(void *ctx);
void
pacer_init (struct pacer *, lsquic_cid_t, unsigned clock_granularity);
pacer_init (struct pacer *, const struct lsquic_conn *,
unsigned clock_granularity);
void
pacer_cleanup (struct pacer *);
void
pacer_tick (struct pacer *, lsquic_time_t);
pacer_tick_in (struct pacer *, lsquic_time_t);
void
pacer_tick_out (struct pacer *);
int
pacer_can_schedule (struct pacer *, unsigned n_in_flight);

View File

@ -3,10 +3,14 @@
* lsquic_packet_common.c -- some common packet-related routines
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_logger.h"
#include "lsquic_enc_sess.h"
#include "lsquic_packet_common.h"
@ -43,52 +47,6 @@ lsquic_frame_types_to_str (char *buf, size_t bufsz,
}
enum packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight)
{
uint64_t delta;
unsigned bits;
delta = packno - least_unacked;
if (n_in_flight > delta)
delta = n_in_flight;
delta *= 4;
bits = (delta > (1ULL << 8))
+ (delta > (1ULL << 16))
+ (delta > (1ULL << 32));
return bits;
}
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
unsigned len,
lsquic_packno_t max_packno)
{
lsquic_packno_t candidates[3], epoch_delta;
int64_t diffs[3];
unsigned min;
epoch_delta = 1ULL << (len << 3);
candidates[1] = (max_packno & ~(epoch_delta - 1)) + cur_packno;
candidates[0] = candidates[1] - epoch_delta;
candidates[2] = candidates[1] + epoch_delta;
diffs[0] = llabs((int64_t) candidates[0] - (int64_t) max_packno);
diffs[1] = llabs((int64_t) candidates[1] - (int64_t) max_packno);
diffs[2] = llabs((int64_t) candidates[2] - (int64_t) max_packno);
min = diffs[1] < diffs[0];
if (diffs[2] < diffs[min])
min = 2;
return candidates[min];
}
const char *const lsquic_hety2str[] =
{
[HETY_NOT_SET] = "Short",
@ -98,3 +56,33 @@ const char *const lsquic_hety2str[] =
[HETY_HANDSHAKE] = "Handshake",
[HETY_0RTT] = "0-RTT",
};
/* [draft-ietf-quic-tls-14], Section 4 */
const enum packnum_space lsquic_hety2pns[] =
{
[HETY_NOT_SET] = PNS_APP,
[HETY_VERNEG] = 0,
[HETY_INITIAL] = PNS_INIT,
[HETY_RETRY] = 0,
[HETY_HANDSHAKE] = PNS_HSK,
[HETY_0RTT] = PNS_APP,
};
/* [draft-ietf-quic-tls-14], Section 4 */
const enum packnum_space lsquic_enclev2pns[] =
{
[ENC_LEV_CLEAR] = PNS_INIT,
[ENC_LEV_INIT] = PNS_HSK,
[ENC_LEV_EARLY] = PNS_APP,
[ENC_LEV_FORW] = PNS_APP,
};
const char *const lsquic_pns2str[] =
{
[PNS_INIT] = "Init PNS",
[PNS_HSK] = "Handshake PNS",
[PNS_APP] = "App PNS",
};

View File

@ -2,52 +2,64 @@
#ifndef LSQUIC_PACKET_COMMON_H
#define LSQUIC_PACKET_COMMON_H 1
#include <stdint.h>
#include "lsquic_int_types.h"
enum PACKET_PUBLIC_FLAGS
{
PACKET_PUBLIC_FLAGS_VERSION = 1,
PACKET_PUBLIC_FLAGS_RST = 2,
PACKET_PUBLIC_FLAGS_NONCE = 4,
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 8,
PACKET_PUBLIC_FLAGS_MULTIPATH = 1 << 6,
PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
};
/* The list of frames contains frames both in GQUIC and in IETF QUIC.
* They are marked as follows:
* G Applicable to GQUIC only
* I Applicable to IETF QUIC only
* B Applicable to both GQUIC and IETF QUIC.
*/
enum quic_frame_type
{
QUIC_FRAME_INVALID,
/*Special*/
QUIC_FRAME_STREAM,
QUIC_FRAME_ACK,
/*Regular*/
QUIC_FRAME_PADDING,
QUIC_FRAME_RST_STREAM,
QUIC_FRAME_CONNECTION_CLOSE,
QUIC_FRAME_GOAWAY,
QUIC_FRAME_WINDOW_UPDATE,
QUIC_FRAME_BLOCKED,
QUIC_FRAME_STOP_WAITING,
QUIC_FRAME_PING,
QUIC_FRAME_STREAM, /* B */
QUIC_FRAME_ACK, /* B */
QUIC_FRAME_PADDING, /* B */
QUIC_FRAME_RST_STREAM, /* B */
QUIC_FRAME_CONNECTION_CLOSE, /* B */
QUIC_FRAME_GOAWAY, /* G */
QUIC_FRAME_WINDOW_UPDATE, /* G */
QUIC_FRAME_BLOCKED, /* B */
QUIC_FRAME_STOP_WAITING, /* G */
QUIC_FRAME_PING, /* B */
QUIC_FRAME_MAX_DATA, /* I */
QUIC_FRAME_MAX_STREAM_DATA, /* I */
QUIC_FRAME_MAX_STREAMS, /* I */
QUIC_FRAME_STREAM_BLOCKED, /* I */
QUIC_FRAME_STREAMS_BLOCKED, /* I */
QUIC_FRAME_NEW_CONNECTION_ID, /* I */
QUIC_FRAME_STOP_SENDING, /* I */
QUIC_FRAME_PATH_CHALLENGE, /* I */
QUIC_FRAME_PATH_RESPONSE, /* I */
QUIC_FRAME_CRYPTO, /* I */
QUIC_FRAME_RETIRE_CONNECTION_ID,/* I */
QUIC_FRAME_NEW_TOKEN, /* I */
N_QUIC_FRAMES
};
enum quic_ft_bit {
QUIC_FTBIT_INVALID = 1 << QUIC_FRAME_INVALID,
QUIC_FTBIT_STREAM = 1 << QUIC_FRAME_STREAM,
QUIC_FTBIT_ACK = 1 << QUIC_FRAME_ACK,
QUIC_FTBIT_PADDING = 1 << QUIC_FRAME_PADDING,
QUIC_FTBIT_RST_STREAM = 1 << QUIC_FRAME_RST_STREAM,
QUIC_FTBIT_CONNECTION_CLOSE = 1 << QUIC_FRAME_CONNECTION_CLOSE,
QUIC_FTBIT_GOAWAY = 1 << QUIC_FRAME_GOAWAY,
QUIC_FTBIT_WINDOW_UPDATE = 1 << QUIC_FRAME_WINDOW_UPDATE,
QUIC_FTBIT_BLOCKED = 1 << QUIC_FRAME_BLOCKED,
QUIC_FTBIT_STOP_WAITING = 1 << QUIC_FRAME_STOP_WAITING,
QUIC_FTBIT_PING = 1 << QUIC_FRAME_PING,
QUIC_FTBIT_INVALID = 1 << QUIC_FRAME_INVALID,
QUIC_FTBIT_STREAM = 1 << QUIC_FRAME_STREAM,
QUIC_FTBIT_ACK = 1 << QUIC_FRAME_ACK,
QUIC_FTBIT_PADDING = 1 << QUIC_FRAME_PADDING,
QUIC_FTBIT_RST_STREAM = 1 << QUIC_FRAME_RST_STREAM,
QUIC_FTBIT_CONNECTION_CLOSE = 1 << QUIC_FRAME_CONNECTION_CLOSE,
QUIC_FTBIT_GOAWAY = 1 << QUIC_FRAME_GOAWAY,
QUIC_FTBIT_WINDOW_UPDATE = 1 << QUIC_FRAME_WINDOW_UPDATE,
QUIC_FTBIT_BLOCKED = 1 << QUIC_FRAME_BLOCKED,
QUIC_FTBIT_STOP_WAITING = 1 << QUIC_FRAME_STOP_WAITING,
QUIC_FTBIT_PING = 1 << QUIC_FRAME_PING,
QUIC_FTBIT_MAX_DATA = 1 << QUIC_FRAME_MAX_DATA,
QUIC_FTBIT_MAX_STREAM_DATA = 1 << QUIC_FRAME_MAX_STREAM_DATA,
QUIC_FTBIT_MAX_STREAMS = 1 << QUIC_FRAME_MAX_STREAMS,
QUIC_FTBIT_STREAM_BLOCKED = 1 << QUIC_FRAME_STREAM_BLOCKED,
QUIC_FTBIT_STREAMS_BLOCKED = 1 << QUIC_FRAME_STREAMS_BLOCKED,
QUIC_FTBIT_NEW_CONNECTION_ID = 1 << QUIC_FRAME_NEW_CONNECTION_ID,
QUIC_FTBIT_STOP_SENDING = 1 << QUIC_FRAME_STOP_SENDING,
QUIC_FTBIT_PATH_CHALLENGE = 1 << QUIC_FRAME_PATH_CHALLENGE,
QUIC_FTBIT_PATH_RESPONSE = 1 << QUIC_FRAME_PATH_RESPONSE,
QUIC_FTBIT_CRYPTO = 1 << QUIC_FRAME_CRYPTO,
QUIC_FTBIT_NEW_TOKEN = 1 << QUIC_FRAME_NEW_TOKEN,
QUIC_FTBIT_RETIRE_CONNECTION_ID = 1 << QUIC_FRAME_RETIRE_CONNECTION_ID,
};
static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
@ -62,6 +74,18 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
[QUIC_FRAME_BLOCKED] = "QUIC_FRAME_BLOCKED",
[QUIC_FRAME_STOP_WAITING] = "QUIC_FRAME_STOP_WAITING",
[QUIC_FRAME_PING] = "QUIC_FRAME_PING",
[QUIC_FRAME_MAX_DATA] = "QUIC_FRAME_MAX_DATA",
[QUIC_FRAME_MAX_STREAM_DATA] = "QUIC_FRAME_MAX_STREAM_DATA",
[QUIC_FRAME_MAX_STREAMS] = "QUIC_FRAME_MAX_STREAMS",
[QUIC_FRAME_STREAM_BLOCKED] = "QUIC_FRAME_STREAM_BLOCKED",
[QUIC_FRAME_STREAMS_BLOCKED] = "QUIC_FRAME_STREAMS_BLOCKED",
[QUIC_FRAME_NEW_CONNECTION_ID] = "QUIC_FRAME_NEW_CONNECTION_ID",
[QUIC_FRAME_STOP_SENDING] = "QUIC_FRAME_STOP_SENDING",
[QUIC_FRAME_PATH_CHALLENGE] = "QUIC_FRAME_PATH_CHALLENGE",
[QUIC_FRAME_PATH_RESPONSE] = "QUIC_FRAME_PATH_RESPONSE",
[QUIC_FRAME_CRYPTO] = "QUIC_FRAME_CRYPTO",
[QUIC_FRAME_NEW_TOKEN] = "QUIC_FRAME_NEW_TOKEN",
[QUIC_FRAME_RETIRE_CONNECTION_ID] = "QUIC_FRAME_RETIRE_CONNECTION_ID",
};
#define QUIC_FRAME_PRELEN (sizeof("QUIC_FRAME_"))
@ -73,92 +97,35 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
* never a part of any frame list bitmask (e.g. po_frame_types).
*/
#define lsquic_frame_types_str_sz \
QUIC_FRAME_SLEN(QUIC_FRAME_STREAM) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_ACK) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_PADDING) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_RST_STREAM) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_CONNECTION_CLOSE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_GOAWAY) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_WINDOW_UPDATE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_BLOCKED) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_STOP_WAITING) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_PING) + 1
QUIC_FRAME_SLEN(QUIC_FRAME_STREAM) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_ACK) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_PADDING) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_RST_STREAM) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_CONNECTION_CLOSE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_GOAWAY) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_WINDOW_UPDATE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_BLOCKED) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_STOP_WAITING) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_PING) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_MAX_DATA) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_MAX_STREAM_DATA) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_MAX_STREAMS) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_STREAM_BLOCKED) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_STREAMS_BLOCKED) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_NEW_CONNECTION_ID) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_STOP_SENDING) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_PATH_CHALLENGE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_PATH_RESPONSE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_CRYPTO) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_RETIRE_CONNECTION_ID) \
+ 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_NEW_TOKEN) + 1 + \
0
const char *
lsquic_frame_types_to_str (char *buf, size_t bufsz, enum quic_ft_bit);
#define QFRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_STOP_WAITING))
#define QFRAME_REGENERATE(frame_type) ((1 << (frame_type)) & QFRAME_REGEN_MASK)
#define QFRAME_ACKABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_PING) \
| (1 << QUIC_FRAME_BLOCKED) \
)
#define QFRAME_ACKABLE(frame_type) ((1 << (frame_type)) & QFRAME_ACKABLE_MASK)
#define QFRAME_RETRANSMITTABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_BLOCKED) \
| (1 << QUIC_FRAME_CONNECTION_CLOSE) \
)
#define QFRAME_RETRANSMITTABLE(frame_type) \
((1 << (frame_type)) & QFRAME_RETRANSMITTABLE_MASK)
#define GQUIC_MAX_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ + 4 /* Version */ \
+ 32 /* Nonce */ + 6 /* Packet Number */ )
#define GQUIC_MIN_PUBHDR_SZ (1 /* Type */ + 1 /* Packet number */)
#define GQUIC_IETF_LONG_HEADER_SIZE (1 /* Type */ + 4 /* Version */ \
+ 1 /* DCIL/SCIL */ + 8 /* CID */ + 4 /* Packet number */)
/* XXX Nonce? */
#define IQUIC_MAX_PUBHDR_SZ GQUIC_IETF_LONG_HEADER_SIZE
#define IQUIC_MIN_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ \
+ 1 /* Packet number */)
#define QUIC_MAX_PUBHDR_SZ (GQUIC_MAX_PUBHDR_SZ > IQUIC_MAX_PUBHDR_SZ \
? GQUIC_MAX_PUBHDR_SZ : IQUIC_MAX_PUBHDR_SZ)
#define QUIC_MIN_PUBHDR_SZ (GQUIC_MIN_PUBHDR_SZ < IQUIC_MIN_PUBHDR_SZ \
? GQUIC_MIN_PUBHDR_SZ : IQUIC_MIN_PUBHDR_SZ)
/* 12 bytes of FNV hash or encryption IV */
#define QUIC_PACKET_HASH_SZ 12
/* [draft-hamilton-quic-transport-protocol-01], Section 7 */
#define QUIC_MAX_IPv4_PACKET_SZ 1370
#define QUIC_MAX_IPv6_PACKET_SZ 1350
#define QUIC_MAX_PACKET_SZ (QUIC_MAX_IPv4_PACKET_SZ > \
QUIC_MAX_IPv6_PACKET_SZ ? QUIC_MAX_IPv4_PACKET_SZ : QUIC_MAX_IPv6_PACKET_SZ)
#define QUIC_MIN_PACKET_OVERHEAD (QUIC_PACKET_HASH_SZ + QUIC_MIN_PUBHDR_SZ)
#define QUIC_MAX_PAYLOAD_SZ (QUIC_MAX_PACKET_SZ - QUIC_MIN_PACKET_OVERHEAD)
#define QUIC_WUF_SZ 13 /* Type (1) + Stream ID (4) + Offset (8) */
#define QUIC_BLOCKED_FRAME_SZ 5 /* Type (1) + Stream ID (4) */
#define QUIC_RST_STREAM_SZ 17 /* Type (1) + Stream ID (4) + Offset (8) +
Error code (4) */
#define QUIC_GOAWAY_FRAME_SZ 11 /* Type (1) + Error code (4) + Stream ID (4) +
Reason phrase length (2) */
/* This value represents a different number of bytes used to encode the packet
* length based on whether GQUIC or IQUIC is used.
*/
@ -203,15 +170,58 @@ enum header_type
extern const char *const lsquic_hety2str[];
enum packno_bits
calc_packno_bits (lsquic_packno_t packno, lsquic_packno_t least_unacked,
uint64_t n_in_flight);
#define IQUIC_MAX_PACKNO ((1ULL << 62) - 1)
#define IQUIC_INVALID_PACKNO (IQUIC_MAX_PACKNO + 1)
#define gquic_packno_bits2len(b) (((b) << 1) + !(b))
/* IETF QUIC only: */
#define is_valid_packno(packno) ((packno) <= IQUIC_MAX_PACKNO)
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
unsigned packet_len,
lsquic_packno_t max_packno);
enum packnum_space
{
PNS_INIT,
PNS_HSK,
PNS_APP,
N_PNS
};
extern const enum packnum_space lsquic_hety2pns[];
extern const enum packnum_space lsquic_enclev2pns[];
extern const char *const lsquic_pns2str[];
#define ALL_IQUIC_FRAMES ( \
QUIC_FTBIT_STREAM \
| QUIC_FTBIT_ACK \
| QUIC_FTBIT_PADDING \
| QUIC_FTBIT_RST_STREAM \
| QUIC_FTBIT_CONNECTION_CLOSE \
| QUIC_FTBIT_BLOCKED \
| QUIC_FTBIT_PING \
| 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_RETIRE_CONNECTION_ID \
| QUIC_FTBIT_NEW_TOKEN \
| QUIC_FTBIT_CRYPTO )
/* [draft-ietf-quic-transport-20] Section 13.1.1 */
#define IQUIC_FRAME_ACKABLE_MASK ( \
ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_ACK|QUIC_FTBIT_PADDING))
/* [draft-ietf-quic-transport-20], Section 13.2 */
/* We bend some rules and retransmit BLOCKED, MAX_DATA, MAX_STREAM_DATA,
* MAX_STREAMS, STREAM_BLOCKED, and STREAMS_BLOCKED frames instead of
* regenerating them. This keeps the code simple(r).
*/
#define IQUIC_FRAME_RETX_MASK ( \
ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_PADDING|QUIC_FTBIT_PATH_RESPONSE \
|QUIC_FTBIT_PATH_CHALLENGE|QUIC_FTBIT_ACK))
extern const enum quic_ft_bit lsquic_legal_frames_by_level[];
#endif

View File

@ -0,0 +1,33 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#include <stdint.h>
#include <stdlib.h>
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
unsigned len,
lsquic_packno_t max_packno)
{
lsquic_packno_t candidates[3], epoch_delta;
int64_t diffs[3];
unsigned min;
epoch_delta = 1ULL << (len << 3);
candidates[1] = (max_packno & ~(epoch_delta - 1)) + cur_packno;
candidates[0] = candidates[1] - epoch_delta;
candidates[2] = candidates[1] + epoch_delta;
diffs[0] = llabs((int64_t) candidates[0] - (int64_t) max_packno);
diffs[1] = llabs((int64_t) candidates[1] - (int64_t) max_packno);
diffs[2] = llabs((int64_t) candidates[2] - (int64_t) max_packno);
min = diffs[1] < diffs[0];
if (diffs[2] < diffs[min])
min = 2;
return candidates[min];
}

View File

@ -0,0 +1,99 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PACKET_GQUIC_H
#define LSQUIC_PACKET_GQUIC_H 1
#include <stdint.h>
#include "lsquic_int_types.h"
enum PACKET_PUBLIC_FLAGS
{
PACKET_PUBLIC_FLAGS_VERSION = 1,
PACKET_PUBLIC_FLAGS_RST = 2,
PACKET_PUBLIC_FLAGS_NONCE = 4,
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 8,
PACKET_PUBLIC_FLAGS_MULTIPATH = 1 << 6,
PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
};
#define GQUIC_FRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_PATH_CHALLENGE) | (1 << QUIC_FRAME_PATH_RESPONSE) \
| (1 << QUIC_FRAME_STOP_WAITING))
#define GQUIC_FRAME_REGENERATE(frame_type) ((1 << (frame_type)) & GQUIC_FRAME_REGEN_MASK)
#define GQUIC_FRAME_ACKABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_PING) \
| (1 << QUIC_FRAME_BLOCKED) \
| (1 << QUIC_FRAME_CRYPTO) \
)
#define GQUIC_FRAME_ACKABLE(frame_type) ((1 << (frame_type)) & GQUIC_FRAME_ACKABLE_MASK)
#define GQUIC_FRAME_RETRANSMITTABLE_MASK ( \
(1 << QUIC_FRAME_STREAM) \
| (1 << QUIC_FRAME_RST_STREAM) \
| (1 << QUIC_FRAME_GOAWAY) \
| (1 << QUIC_FRAME_WINDOW_UPDATE) \
| (1 << QUIC_FRAME_BLOCKED) \
| (1 << QUIC_FRAME_CONNECTION_CLOSE) \
| (1 << QUIC_FRAME_CRYPTO) \
| (1 << QUIC_FRAME_PING) \
)
#define GQUIC_FRAME_RETRANSMITTABLE(frame_type) \
((1 << (frame_type)) & GQUIC_FRAME_RETRANSMITTABLE_MASK)
#define GQUIC_MAX_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ + 4 /* Version */ \
+ 32 /* Nonce */ + 6 /* Packet Number */ )
#define GQUIC_MIN_PUBHDR_SZ (1 /* Type */ + 1 /* Packet number */)
#define GQUIC_IETF_LONG_HEADER_SIZE (1 /* Type */ + 4 /* Version */ \
+ 1 /* DCIL/SCIL */ + 8 /* CID */ + 4 /* Packet number */)
/* XXX Nonce? */
#define IQUIC_MAX_PUBHDR_SZ GQUIC_IETF_LONG_HEADER_SIZE
#define IQUIC_MIN_PUBHDR_SZ (1 /* Type */ + 8 /* CID */ \
+ 1 /* Packet number */)
#define QUIC_MAX_PUBHDR_SZ (GQUIC_MAX_PUBHDR_SZ > IQUIC_MAX_PUBHDR_SZ \
? GQUIC_MAX_PUBHDR_SZ : IQUIC_MAX_PUBHDR_SZ)
#define QUIC_MIN_PUBHDR_SZ (GQUIC_MIN_PUBHDR_SZ < IQUIC_MIN_PUBHDR_SZ \
? GQUIC_MIN_PUBHDR_SZ : IQUIC_MIN_PUBHDR_SZ)
/* 12 bytes of FNV hash or encryption IV */
#define GQUIC_PACKET_HASH_SZ 12
/* [draft-hamilton-quic-transport-protocol-01], Section 7 */
#define GQUIC_MAX_IPv4_PACKET_SZ 1370
#define GQUIC_MAX_IPv6_PACKET_SZ 1350
#define GQUIC_MAX_PACKET_SZ (GQUIC_MAX_IPv4_PACKET_SZ > \
GQUIC_MAX_IPv6_PACKET_SZ ? GQUIC_MAX_IPv4_PACKET_SZ : GQUIC_MAX_IPv6_PACKET_SZ)
#define GQUIC_MIN_PACKET_OVERHEAD (GQUIC_PACKET_HASH_SZ + GQUIC_MIN_PUBHDR_SZ)
#define GQUIC_MAX_PAYLOAD_SZ (GQUIC_MAX_PACKET_SZ - GQUIC_MIN_PACKET_OVERHEAD)
#define GQUIC_WUF_SZ 13 /* Type (1) + Stream ID (4) + Offset (8) */
#define GQUIC_BLOCKED_FRAME_SZ 5 /* Type (1) + Stream ID (4) */
#define GQUIC_RST_STREAM_SZ 17 /* Type (1) + Stream ID (4) + Offset (8) +
Error code (4) */
#define GQUIC_GOAWAY_FRAME_SZ 11 /* Type (1) + Error code (4) + Stream ID (4) +
Reason phrase length (2) */
#define gquic_packno_bits2len(b) (((b) << 1) + !(b))
lsquic_packno_t
restore_packno (lsquic_packno_t cur_packno,
unsigned packet_len,
lsquic_packno_t max_packno);
#endif

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_PACKET_IETF_H
#define LSQUIC_PACKET_IETF_H 1
#define IQUIC_MAX_IPv4_PACKET_SZ 1252
#define IQUIC_MAX_IPv6_PACKET_SZ 1232
#define iquic_packno_bits2len(b) ((b) + 1)
/* [draft-ietf-quic-transport-22] Section 7.2:
"
When an Initial packet is sent by a client that has not previously
received an Initial or Retry packet from the server, it populates the
Destination Connection ID field with an unpredictable value. This
MUST be at least 8 bytes in length.
"
* Because the server always generates 8-byte CIDs, the DCID length cannot be
* smaller than 8 even if the client received an Initial or Retry packet from
* us.
*/
#define MIN_INITIAL_DCID_LEN 8
#endif

View File

@ -52,3 +52,12 @@ lsquic_packet_in_mem_used (const struct lsquic_packet_in *packet_in)
return size;
}
void
lsquic_scid_from_packet_in (const struct lsquic_packet_in *packet_in,
lsquic_cid_t *scid)
{
scid->len = packet_in->pi_scid_len;
memcpy(scid->idbuf, packet_in->pi_data + packet_in->pi_scid_off, scid->len);
}

View File

@ -6,7 +6,10 @@
#ifndef LSQUIC_PACKET_IN_H
#define LSQUIC_PACKET_IN_H 1
struct lsquic_packet_in;
struct lsquic_cid;
struct data_frame
{
@ -17,6 +20,7 @@ struct data_frame
signed char df_fin; /* FIN? */
};
typedef struct stream_frame
{
/* Stream frames are stored in a list inside stream. */
@ -31,47 +35,74 @@ typedef struct stream_frame
struct data_frame data_frame;
uint32_t stream_id; /* Parsed from packet */
lsquic_stream_id_t stream_id; /* Parsed from packet */
} stream_frame_t;
#define DF_OFF(frame) (frame)->data_frame.df_offset
#define DF_ROFF(frame) (DF_OFF(frame) + (frame)->data_frame.df_read_off)
#define DF_FIN(frame) (frame)->data_frame.df_fin
#define DF_SIZE(frame) (frame)->data_frame.df_size
#define DF_END(frame) (DF_OFF(frame) + DF_SIZE(frame))
typedef struct lsquic_packet_in
{
TAILQ_ENTRY(lsquic_packet_in) pi_next;
lsquic_time_t pi_received; /* Time received */
lsquic_cid_t pi_conn_id;
lsquic_cid_t pi_dcid;
#define pi_conn_id pi_dcid
lsquic_packno_t pi_packno;
enum quic_ft_bit pi_frame_types;
unsigned short pi_header_sz; /* Points to payload */
unsigned short pi_data_sz; /* Data plus header */
/* A packet may be referred to by one or more frames and packets_in
* list.
*/
unsigned short pi_refcnt;
enum quic_ft_bit pi_frame_types:16;
unsigned short pi_hsk_stream; /* Offset to handshake stream
* frame, only valid if
* PI_HSK_STREAM is set.
*/
unsigned char pi_quic_ver; /* Offset to QUIC version */
unsigned char pi_nonce; /* Offset to nonce */
enum {
PI_DECRYPTED = (1 << 0),
PI_OWN_DATA = (1 << 1), /* We own pi_data */
PI_CONN_ID = (1 << 2), /* pi_conn_id is set */
PI_HSK_STREAM = (1 << 3), /* Has handshake data (mini only) */
PI_FROM_MINI = (1 << 4), /* Handed off by mini connection */
#define PIBIT_ENC_LEV_SHIFT 5
PI_ENC_LEV_BIT_0= (1 << 5), /* Encodes encryption level */
PI_ENC_LEV_BIT_1= (1 << 6), /* (see enum enc_level). */
PI_GQUIC = (1 << 7),
#define PIBIT_BITS_SHIFT 8
PI_BITS_BIT_0 = (1 << 8),
PI_BITS_BIT_1 = (1 << 9),
PI_UNUSED_8 = (1 << 8), /* <-- hole, reuse me! */
#define PIBIT_ECN_SHIFT 9
PI_ECN_BIT_0 = (1 << 9),
PI_ECN_BIT_1 = (1 <<10),
#define PIBIT_SPIN_SHIFT 11
PI_SPIN_BIT = (1 <<11),
#define PIBIT_BITS_SHIFT 12
PI_BITS_BIT_0 = (1 <<12),
PI_BITS_BIT_1 = (1 <<13),
} pi_flags:16;
/* pi_token and pi_token_size are set in Initial and Retry packets */
unsigned short pi_token_size; /* Size of the token */
unsigned char pi_token; /* Offset to token */
/* pi_odcid and pi_odcid_len are only set in Retry packets */
unsigned char pi_odcid; /* Offset to Original DCID */
unsigned char pi_odcid_len; /* Size of ODCID */
unsigned char pi_scid_off; /* Offset to SCID */
unsigned char pi_scid_len; /* Size of SCID */
unsigned char pi_quic_ver; /* Offset to QUIC version */
unsigned char pi_nonce; /* Offset to nonce */
enum header_type pi_header_type:8;
unsigned char pi_path_id;
/* If PI_OWN_DATA flag is not set, `pi_data' points to user-supplied
* packet data, which is NOT TO BE MODIFIED.
*/
unsigned char *pi_data;
} lsquic_packet_in_t;
#define lsquic_packet_in_public_flags(p) ((p)->pi_data[0])
#define lsquic_packet_in_is_gquic_prst(p) \
@ -96,6 +127,23 @@ typedef struct lsquic_packet_in
#define lsquic_packet_in_enc_level(p) \
(((p)->pi_flags >> PIBIT_ENC_LEV_SHIFT) & 0x3)
#define lsquic_packet_in_ecn(p) \
(((p)->pi_flags >> PIBIT_ECN_SHIFT) & 0x3)
#define lsquic_packet_in_spin_bit(p) (((p)->pi_flags & PI_SPIN_BIT) > 0)
/* PATH_CHALLENGE, PATH_RESPONSE, NEW_CONNECTION_ID, and PADDING frames
* are "probing frames", and all other frames are "non-probing frames".
* A packet containing only probing frames is a "probing packet", and a
* packet containing any other frame is a "non-probing packet".
*
* [draft-ietf-quic-transport-20], Section 9.1
*/
#define lsquic_packet_in_non_probing(p) \
(!!((p)->pi_frame_types & ~(QUIC_FTBIT_PATH_CHALLENGE \
|QUIC_FTBIT_PATH_RESPONSE|QUIC_FTBIT_PADDING \
|QUIC_FTBIT_NEW_CONNECTION_ID)))
/* The version iterator is used on a version negotiation packet only.
* The iterator functions return 1 when next version is returned and
* 0 when there are no more versions.
@ -116,4 +164,8 @@ packet_in_ver_next (struct ver_iter *, lsquic_ver_tag_t *ver_tag);
size_t
lsquic_packet_in_mem_used (const struct lsquic_packet_in *);
void
lsquic_scid_from_packet_in (const struct lsquic_packet_in *,
struct lsquic_cid *);
#endif

Some files were not shown because too many files have changed in this diff Show More