deps: update ngtcp2 to 1.19.0

This commit is contained in:
targos 2025-12-23 13:56:15 +00:00 committed by github-actions[bot]
parent a385bb1d70
commit 51c6dbe85b
137 changed files with 5621 additions and 18524 deletions

View File

@ -627,7 +627,10 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
* @macro
*
* :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length
* of a token generated by `ngtcp2_crypto_generate_regular_token`.
* of a token generated by `ngtcp2_crypto_generate_regular_token`.
* `ngtcp2_crypto_generate_regular_token2` generates a token of length
* at most :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes + the
* length of the provided opaque data.
*/
#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \
(/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
@ -787,6 +790,77 @@ NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
size_t secretlen, const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_crypto_generate_regular_token2` generates a token in the
* buffer pointed by |token| that is sent with NEW_TOKEN frame. The
* buffer pointed by |token| must have at least
* :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` + |datalen| bytes long.
* The successfully generated token starts with
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length
* |secretlen| is a keying material to generate keys to encrypt the
* token. |remote_addr| of length |remote_addrlen| is an address of
* client. |ts| is the timestamp when the token is generated. |data|
* of length |datalen| is an opaque data embedded in the token.
* |datalen| must be less than or equal to 256.
*
* Calling this function with |datalen| = 0 is equivalent to calling
* `ngtcp2_crypto_generate_regular_token`.
*
* To get the opaque data after successful verification, use
* `ngtcp2_crypto_verify_regular_token2`.
* `ngtcp2_crypto_verify_regular_token` can verify the token with
* |datalen| > 0, but it discards the opaque data.
*
* This function returns the length of generated token if it succeeds,
* or -1.
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token2(
uint8_t *token, const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
const void *data, size_t datalen, ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_crypto_verify_regular_token2` verifies a regular token
* stored in the buffer pointed by |token| of length |tokenlen|.
* |secret| of length |secretlen| is a keying material to generate
* keys to decrypt the token. |remote_addr| of length
* |remote_addrlen| is an address of client. |timeout| is the period
* during which the token is valid. |ts| is the current timestamp.
* |data| is the pointer to the buffer of length at least
* |max_datalen| bytes. If the token is verified successfully, the
* opaque data embedded in the token is copied to the buffer pointed
* by |data|.
*
* If |tokenlen| is less than
* :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN`, this function returns
* :macro:`NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN`.
*
* If the length of opaque data is larger than |max_datalen|, the
* verification still succeeds, but nothing is written to the buffer
* pointed by |data|, and this function returns 0. In other words,
* the opaque data is discarded.
*
* This function returns the number of the opaque data written to the
* buffer pointed by |data| if it succeeds, or one of the following
* negative error codes:
*
* :macro:`NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN`
* A token is badly formatted; or verifying the integrity
* protection failed.
* :macro:`NGTCP2_CRYPTO_ERR_VERIFY_TOKEN`
* A token validity has expired.
* :macro:`NGTCP2_CRYPTO_ERR_INTERNAL`
* Internal error occurred.
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_verify_regular_token2(
void *data, size_t max_datalen, const uint8_t *token, size_t tokenlen,
const uint8_t *secret, size_t secretlen, const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
/**
* @function
*

View File

@ -41,70 +41,40 @@
#include "ngtcp2_macro.h"
#include "shared.h"
static int crypto_initialized;
#if defined(OPENSSL_NO_CHACHA) || defined(OPENSSL_NO_POLY1305)
# define NGTCP2_NO_CHACHA_POLY1305
#endif /* defined(OPENSSL_NO_CHACHA) || \
defined(OPENSSL_NO_POLY1305) */
static EVP_CIPHER *crypto_aes_128_gcm;
static EVP_CIPHER *crypto_aes_256_gcm;
static EVP_CIPHER *crypto_chacha20_poly1305;
static EVP_CIPHER *crypto_aes_128_ccm;
static EVP_CIPHER *crypto_aes_128_ctr;
static EVP_CIPHER *crypto_aes_256_ctr;
#ifndef NGTCP2_NO_CHACHA_POLY1305
static EVP_CIPHER *crypto_chacha20_poly1305;
static EVP_CIPHER *crypto_chacha20;
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
static EVP_MD *crypto_sha256;
static EVP_MD *crypto_sha384;
static EVP_KDF *crypto_hkdf;
int ngtcp2_crypto_ossl_init(void) {
/* We do not care whether the pre-fetch succeeds or not. If it
fails, it returns NULL, which is still the default value, and our
code should still work with it. */
crypto_aes_128_gcm = EVP_CIPHER_fetch(NULL, "AES-128-GCM", NULL);
if (crypto_aes_128_gcm == NULL) {
return -1;
}
crypto_aes_256_gcm = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL);
if (crypto_aes_256_gcm == NULL) {
return -1;
}
crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL);
if (crypto_chacha20_poly1305 == NULL) {
return -1;
}
crypto_aes_128_ccm = EVP_CIPHER_fetch(NULL, "AES-128-CCM", NULL);
if (crypto_aes_128_ccm == NULL) {
return -1;
}
crypto_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL);
if (crypto_aes_128_ctr == NULL) {
return -1;
}
crypto_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL);
if (crypto_aes_256_ctr == NULL) {
return -1;
}
#ifndef NGTCP2_NO_CHACHA_POLY1305
crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL);
crypto_chacha20 = EVP_CIPHER_fetch(NULL, "ChaCha20", NULL);
if (crypto_chacha20 == NULL) {
return -1;
}
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
crypto_sha256 = EVP_MD_fetch(NULL, "sha256", NULL);
if (crypto_sha256 == NULL) {
return -1;
}
crypto_sha384 = EVP_MD_fetch(NULL, "sha384", NULL);
if (crypto_sha384 == NULL) {
return -1;
}
crypto_hkdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
if (crypto_hkdf == NULL) {
return -1;
}
crypto_initialized = 1;
return 0;
}
@ -125,6 +95,7 @@ static const EVP_CIPHER *crypto_aead_aes_256_gcm(void) {
return EVP_aes_256_gcm();
}
#ifndef NGTCP2_NO_CHACHA_POLY1305
static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) {
if (crypto_chacha20_poly1305) {
return crypto_chacha20_poly1305;
@ -132,6 +103,7 @@ static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) {
return EVP_chacha20_poly1305();
}
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) {
if (crypto_aes_128_ccm) {
@ -157,6 +129,7 @@ static const EVP_CIPHER *crypto_cipher_aes_256_ctr(void) {
return EVP_aes_256_ctr();
}
#ifndef NGTCP2_NO_CHACHA_POLY1305
static const EVP_CIPHER *crypto_cipher_chacha20(void) {
if (crypto_chacha20) {
return crypto_chacha20;
@ -164,6 +137,7 @@ static const EVP_CIPHER *crypto_cipher_chacha20(void) {
return EVP_chacha20();
}
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
static const EVP_MD *crypto_md_sha256(void) {
if (crypto_sha256) {
@ -189,13 +163,21 @@ static EVP_KDF *crypto_kdf_hkdf(void) {
return EVP_KDF_fetch(NULL, "hkdf", NULL);
}
static void crypto_kdf_hkdf_free(EVP_KDF *kdf) {
if (kdf && crypto_hkdf != kdf) {
EVP_KDF_free(kdf);
}
}
static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) {
switch (EVP_CIPHER_nid(aead)) {
case NID_aes_128_gcm:
case NID_aes_256_gcm:
return EVP_GCM_TLS_TAG_LEN;
#ifndef NGTCP2_NO_CHACHA_POLY1305
case NID_chacha20_poly1305:
return EVP_CHACHAPOLY_TLS_TAG_LEN;
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case NID_aes_128_ccm:
return EVP_CCM_TLS_TAG_LEN;
default:
@ -239,8 +221,10 @@ static const EVP_CIPHER *crypto_cipher_id_get_aead(uint32_t cipher_id) {
return crypto_aead_aes_128_gcm();
case TLS1_3_CK_AES_256_GCM_SHA384:
return crypto_aead_aes_256_gcm();
#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return crypto_aead_chacha20_poly1305();
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return crypto_aead_aes_128_ccm();
default:
@ -253,8 +237,10 @@ static uint64_t crypto_cipher_id_get_aead_max_encryption(uint32_t cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_256_GCM_SHA384:
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
default:
@ -268,8 +254,10 @@ crypto_cipher_id_get_aead_max_decryption_failure(uint32_t cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_256_GCM_SHA384:
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
default:
@ -284,8 +272,10 @@ static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) {
return crypto_cipher_aes_128_ctr();
case TLS1_3_CK_AES_256_GCM_SHA384:
return crypto_cipher_aes_256_ctr();
#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return crypto_cipher_chacha20();
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
default:
return NULL;
}
@ -294,7 +284,9 @@ static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) {
static const EVP_MD *crypto_cipher_id_get_md(uint32_t cipher_id) {
switch (cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return crypto_md_sha256();
case TLS1_3_CK_AES_256_GCM_SHA384:
@ -308,7 +300,9 @@ static int supported_cipher_id(uint32_t cipher_id) {
switch (cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_256_GCM_SHA384:
#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return 1;
default:
@ -697,9 +691,7 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
};
int rv = 0;
if (!crypto_initialized) {
EVP_KDF_free(kdf);
}
crypto_kdf_hkdf_free(kdf);
if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) {
rv = -1;
@ -730,9 +722,7 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
};
int rv = 0;
if (!crypto_initialized) {
EVP_KDF_free(kdf);
}
crypto_kdf_hkdf_free(kdf);
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
rv = -1;
@ -763,9 +753,7 @@ int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
};
int rv = 0;
if (!crypto_initialized) {
EVP_KDF_free(kdf);
}
crypto_kdf_hkdf_free(kdf);
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
rv = -1;

View File

@ -461,7 +461,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
if (ngtcp2_conn_is_server(conn) &&
crypto_set_local_transport_params(conn, tls) != 0) {
goto fail;
/* Just return -1 because aead_ctx and hp_ctx are now owned by
conn. */
return -1;
}
break;
@ -1305,13 +1307,22 @@ static size_t crypto_generate_regular_token_aad(uint8_t *dest,
return addrlen;
}
/* NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN is the maximum length of
opaque data embedded in a regular token. */
#define NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN 256
/* NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN is the maximum length
of plaintext included in a regular token. */
#define NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN \
(sizeof(ngtcp2_tstamp) + NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN)
static const uint8_t regular_token_info_prefix[] = "regular_token";
ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
static ngtcp2_ssize crypto_generate_regular_token(
uint8_t *token, const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
ngtcp2_tstamp ts) {
uint8_t plaintext[sizeof(ngtcp2_tstamp)];
const void *data, size_t datalen, ngtcp2_tstamp ts) {
uint8_t plaintext[NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN];
uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
uint8_t key[16];
uint8_t iv[12];
@ -1328,9 +1339,18 @@ ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
int rv;
(void)remote_addrlen;
if (datalen > NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN) {
return -1;
}
memcpy(p, &ts_be, sizeof(ts_be));
p += sizeof(ts_be);
if (datalen) {
memcpy(p, data, datalen);
p += datalen;
}
plaintextlen = (size_t)(p - plaintext);
if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
@ -1378,13 +1398,11 @@ ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
return p - token;
}
int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen,
ngtcp2_duration timeout,
ngtcp2_tstamp ts) {
uint8_t plaintext[sizeof(ngtcp2_tstamp)];
static ngtcp2_ssize crypto_verify_regular_token(
void *data, size_t max_datalen, const uint8_t *token, size_t tokenlen,
const uint8_t *secret, size_t secretlen, const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
uint8_t plaintext[NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN];
uint8_t key[16];
uint8_t iv[12];
size_t keylen;
@ -1397,13 +1415,14 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
const uint8_t *rand_data;
const uint8_t *ciphertext;
size_t ciphertextlen;
size_t datalen;
int rv;
ngtcp2_tstamp gen_ts;
(void)remote_addrlen;
if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
if (tokenlen < NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) {
return -1;
return NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN;
}
rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
@ -1413,6 +1432,10 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
ngtcp2_crypto_aead_aes_128_gcm(&aead);
ngtcp2_crypto_md_sha256(&md);
if (ciphertextlen > sizeof(plaintext) + aead.max_overhead) {
return NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN;
}
keylen = ngtcp2_crypto_aead_keylen(&aead);
ivlen = ngtcp2_crypto_aead_noncelen(&aead);
@ -1423,13 +1446,13 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
regular_token_info_prefix,
sizeof(regular_token_info_prefix) - 1) != 0) {
return -1;
return NGTCP2_CRYPTO_ERR_INTERNAL;
}
aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
return -1;
return NGTCP2_CRYPTO_ERR_INTERNAL;
}
rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
@ -1438,19 +1461,74 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
if (rv != 0) {
return -1;
return NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN;
}
memcpy(&gen_ts, plaintext, sizeof(gen_ts));
gen_ts = ngtcp2_ntohl64(gen_ts);
if (gen_ts + timeout <= ts) {
return NGTCP2_CRYPTO_ERR_VERIFY_TOKEN;
}
if (max_datalen == 0) {
return 0;
}
datalen = ciphertextlen - aead.max_overhead - sizeof(gen_ts);
if (datalen > max_datalen) {
return 0;
}
memcpy(data, plaintext + sizeof(gen_ts), datalen);
return (ngtcp2_ssize)datalen;
}
ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
uint8_t *token, const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
ngtcp2_tstamp ts) {
return crypto_generate_regular_token(token, secret, secretlen, remote_addr,
remote_addrlen, NULL, 0, ts);
}
int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen,
ngtcp2_duration timeout,
ngtcp2_tstamp ts) {
ngtcp2_ssize datalen =
crypto_verify_regular_token(NULL, 0, token, tokenlen, secret, secretlen,
remote_addr, remote_addrlen, timeout, ts);
if (datalen < 0) {
return -1;
}
assert(0 == datalen);
return 0;
}
ngtcp2_ssize ngtcp2_crypto_generate_regular_token2(
uint8_t *token, const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
const void *data, size_t datalen, ngtcp2_tstamp ts) {
return crypto_generate_regular_token(token, secret, secretlen, remote_addr,
remote_addrlen, data, datalen, ts);
}
ngtcp2_ssize ngtcp2_crypto_verify_regular_token2(
void *data, size_t max_datalen, const uint8_t *token, size_t tokenlen,
const uint8_t *secret, size_t secretlen, const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
return crypto_verify_regular_token(data, max_datalen, token, tokenlen, secret,
secretlen, remote_addr, remote_addrlen,
timeout, ts);
}
ngtcp2_ssize ngtcp2_crypto_write_connection_close(
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,

View File

@ -62,10 +62,10 @@ namespace {
constexpr size_t max_preferred_versionslen = 4;
} // namespace
Config config{};
Config config;
Stream::Stream(const Request &req, int64_t stream_id)
: req(req), stream_id(stream_id), fd(-1) {}
: req{req}, stream_id{stream_id} {}
Stream::~Stream() {
if (fd != -1) {
@ -175,26 +175,16 @@ void siginthandler(struct ev_loop *loop, ev_signal *w, int revents) {
Client::Client(struct ev_loop *loop, uint32_t client_chosen_version,
uint32_t original_version)
: remote_addr_{},
loop_(loop),
httpconn_(nullptr),
addr_(nullptr),
port_(nullptr),
nstreams_done_(0),
nstreams_closed_(0),
nkey_update_(0),
client_chosen_version_(client_chosen_version),
original_version_(original_version),
early_data_(false),
handshake_confirmed_(false),
: loop_{loop},
client_chosen_version_{client_chosen_version},
original_version_{original_version},
no_gso_{
#ifdef UDP_SEGMENT
false
config.no_gso
#else // !defined(UDP_SEGMENT)
true
#endif // !defined(UDP_SEGMENT)
},
tx_{} {
} {
ev_io_init(&wev_, writecb, 0, EV_WRITE);
wev_.data = this;
ev_timer_init(&timer_, timeoutcb, 0., 0.);
@ -563,8 +553,7 @@ int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
} // namespace
void Client::set_remote_addr(const ngtcp2_addr &remote_addr) {
memcpy(&remote_addr_.su, remote_addr.addr, remote_addr.addrlen);
remote_addr_.len = remote_addr.addrlen;
remote_addr_.set(remote_addr.addr);
}
namespace {
@ -587,8 +576,10 @@ int select_preferred_address(ngtcp2_conn *conn, ngtcp2_path *dest,
return NGTCP2_ERR_CALLBACK_FAILURE;
}
ngtcp2_addr_copy_byte(&dest->local, &(*ep)->addr.su.sa, (*ep)->addr.len);
ngtcp2_addr_copy_byte(&dest->remote, &remote_addr.su.sa, remote_addr.len);
ngtcp2_addr_copy_byte(&dest->local, (*ep)->addr.as_sockaddr(),
(*ep)->addr.size());
ngtcp2_addr_copy_byte(&dest->remote, remote_addr.as_sockaddr(),
remote_addr.size());
dest->user_data = *ep;
return 0;
@ -821,16 +812,8 @@ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
params.grease_quic_bit = 1;
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&ep.addr.su.sa),
.addrlen = ep.addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(&remote_addr.su.sa),
.addrlen = remote_addr.len,
},
.local = as_ngtcp2_addr(ep.addr),
.remote = as_ngtcp2_addr(remote_addr),
.user_data = &ep,
};
auto rv =
@ -877,16 +860,11 @@ int Client::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data) {
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&ep.addr.su.sa),
.addrlen = ep.addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.local = as_ngtcp2_addr(ep.addr),
.remote{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.user_data = const_cast<Endpoint *>(&ep),
};
if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data.data(), data.size(),
@ -916,7 +894,7 @@ int Client::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
int Client::on_read(const Endpoint &ep) {
std::array<uint8_t, 64_k> buf;
sockaddr_union su;
sockaddr_storage ss;
size_t pktcnt = 0;
ngtcp2_pkt_info pi;
@ -928,14 +906,21 @@ int Client::on_read(const Endpoint &ep) {
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(int))];
msghdr msg{
.msg_name = &su,
.msg_name = &ss,
.msg_iov = &msg_iov,
.msg_iovlen = 1,
.msg_control = msg_ctrl,
};
for (;;) {
msg.msg_namelen = sizeof(su);
auto start = util::timestamp();
for (; pktcnt < MAX_RECV_PKTS;) {
if (util::recv_pkt_time_threshold_exceeded(
config.cc_algo == NGTCP2_CC_ALGO_BBR, start, pktcnt)) {
break;
}
msg.msg_namelen = sizeof(ss);
msg.msg_controllen = sizeof(msg_ctrl);
auto nread = recvmsg(ep.fd, &msg, 0);
@ -954,7 +939,7 @@ int Client::on_read(const Endpoint &ep) {
continue;
}
pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
pi.ecn = msghdr_get_ecn(&msg, ss.ss_family);
auto gso_size = msghdr_get_udp_gro(&msg);
if (gso_size == 0) {
gso_size = static_cast<size_t>(nread);
@ -968,9 +953,10 @@ int Client::on_read(const Endpoint &ep) {
++pktcnt;
if (!config.quiet) {
std::cerr << "Received packet: local="
<< util::straddr(&ep.addr.su.sa, ep.addr.len)
<< " remote=" << util::straddr(&su.sa, msg.msg_namelen)
std::cerr << "Received packet: local=" << util::straddr(ep.addr)
<< " remote="
<< util::straddr(reinterpret_cast<const sockaddr *>(&ss),
msg.msg_namelen)
<< " ecn=0x" << std::hex << static_cast<uint32_t>(pi.ecn)
<< std::dec << " " << datalen << " bytes" << std::endl;
}
@ -984,8 +970,8 @@ int Client::on_read(const Endpoint &ep) {
if (!config.quiet) {
std::cerr << "** Simulated incoming packet loss **" << std::endl;
}
} else if (feed_data(ep, &su.sa, msg.msg_namelen, &pi,
{data.data(), datalen}) != 0) {
} else if (feed_data(ep, reinterpret_cast<const sockaddr *>(&ss),
msg.msg_namelen, &pi, {data.data(), datalen}) != 0) {
return -1;
}
@ -995,10 +981,6 @@ int Client::on_read(const Endpoint &ep) {
break;
}
}
if (pktcnt >= 10) {
break;
}
}
if (should_exit()) {
@ -1157,18 +1139,21 @@ int Client::write_streams() {
ngtcp2_pkt_info pi;
size_t gso_size;
auto ts = util::timestamp();
auto txbuf = std::span{tx_.data};
auto txbuf = std::span{txbuf_};
auto buflen = util::clamp_buffer_size(conn_, txbuf.size(), config.gso_burst);
ngtcp2_path_storage_zero(&ps);
auto nwrite =
ngtcp2_conn_write_aggregate_pkt(conn_, &ps.path, &pi, txbuf.data(),
txbuf.size(), &gso_size, ::write_pkt, ts);
auto nwrite = ngtcp2_conn_write_aggregate_pkt2(
conn_, &ps.path, &pi, txbuf.data(), buflen, &gso_size, ::write_pkt,
config.gso_burst, ts);
if (nwrite < 0) {
disconnect();
return -1;
}
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
if (nwrite == 0) {
return 0;
}
@ -1223,8 +1208,7 @@ void Client::update_timer() {
#ifdef HAVE_LINUX_RTNETLINK_H
namespace {
int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
int family) {
int bind_addr(Address &local_addr, int fd, const InAddr &ia, int family) {
addrinfo hints{
.ai_flags = AI_PASSIVE,
.ai_family = family,
@ -1234,15 +1218,16 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
char *node;
std::array<char, NI_MAXHOST> nodebuf;
if (iau) {
if (inet_ntop(family, iau, nodebuf.data(), nodebuf.size()) == nullptr) {
if (in_addr_empty(ia)) {
node = nullptr;
} else {
if (inet_ntop(family, in_addr_get_ptr(ia), nodebuf.data(),
nodebuf.size()) == nullptr) {
std::cerr << "inet_ntop: " << strerror(errno) << std::endl;
return -1;
}
node = nodebuf.data();
} else {
node = nullptr;
}
if (auto rv = getaddrinfo(node, "0", &hints, &res); rv != 0) {
@ -1250,7 +1235,7 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
return -1;
}
auto res_d = defer(freeaddrinfo, res);
auto res_d = defer([res] { freeaddrinfo(res); });
for (rp = res; rp; rp = rp->ai_next) {
if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
@ -1263,13 +1248,14 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
return -1;
}
socklen_t len = sizeof(local_addr.su.storage);
if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
sockaddr_storage ss;
socklen_t len = sizeof(ss);
if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) == -1) {
std::cerr << "getsockname: " << strerror(errno) << std::endl;
return -1;
}
local_addr.len = len;
local_addr.ifindex = 0;
local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
return 0;
}
@ -1279,18 +1265,19 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
#ifndef HAVE_LINUX_RTNETLINK_H
namespace {
int connect_sock(Address &local_addr, int fd, const Address &remote_addr) {
if (connect(fd, &remote_addr.su.sa, remote_addr.len) != 0) {
if (connect(fd, remote_addr.as_sockaddr(), remote_addr.size()) != 0) {
std::cerr << "connect: " << strerror(errno) << std::endl;
return -1;
}
socklen_t len = sizeof(local_addr.su.storage);
if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
sockaddr_storage ss;
socklen_t len = sizeof(ss);
if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) == -1) {
std::cerr << "getsockname: " << strerror(errno) << std::endl;
return -1;
}
local_addr.len = len;
local_addr.ifindex = 0;
local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
return 0;
}
@ -1326,7 +1313,7 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
return -1;
}
auto res_d = defer(freeaddrinfo, res);
auto res_d = defer([res] { freeaddrinfo(res); });
int fd = -1;
@ -1344,9 +1331,7 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
return -1;
}
remote_addr.len = rp->ai_addrlen;
memcpy(&remote_addr.su, rp->ai_addr, rp->ai_addrlen);
remote_addr.ifindex = 0;
remote_addr.set(rp->ai_addr);
return fd;
}
@ -1354,9 +1339,9 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
#ifdef HAVE_LINUX_RTNETLINK_H
in_addr_union iau;
InAddr ia;
if (get_local_addr(iau, remote_addr) != 0) {
if (get_local_addr(ia, remote_addr) != 0) {
std::cerr << "Could not get local address for a selected preferred address"
<< std::endl;
return nullptr;
@ -1364,12 +1349,14 @@ std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
auto current_path = ngtcp2_conn_get_path(conn_);
auto current_ep = static_cast<Endpoint *>(current_path->user_data);
if (addreq(&current_ep->addr.su.sa, iau)) {
if (addreq(current_ep->addr, ia)) {
return current_ep;
}
#endif // defined(HAVE_LINUX_RTNETLINK_H)
auto fd = udp_sock(remote_addr.su.sa.sa_family);
auto family = remote_addr.family();
auto fd = udp_sock(family);
if (fd == -1) {
return nullptr;
}
@ -1377,7 +1364,7 @@ std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
Address local_addr;
#ifdef HAVE_LINUX_RTNETLINK_H
if (bind_addr(local_addr, fd, &iau, remote_addr.su.sa.sa_family) != 0) {
if (bind_addr(local_addr, fd, ia, family) != 0) {
close(fd);
return nullptr;
}
@ -1412,21 +1399,23 @@ int Client::change_local_addr() {
std::cerr << "Changing local address" << std::endl;
}
auto nfd = udp_sock(remote_addr_.su.sa.sa_family);
auto family = remote_addr_.family();
auto nfd = udp_sock(family);
if (nfd == -1) {
return -1;
}
#ifdef HAVE_LINUX_RTNETLINK_H
in_addr_union iau;
InAddr ia;
if (get_local_addr(iau, remote_addr_) != 0) {
if (get_local_addr(ia, remote_addr_) != 0) {
std::cerr << "Could not get local address" << std::endl;
close(nfd);
return -1;
}
if (bind_addr(local_addr, nfd, &iau, remote_addr_.su.sa.sa_family) != 0) {
if (bind_addr(local_addr, nfd, ia, family) != 0) {
close(nfd);
return -1;
}
@ -1438,8 +1427,8 @@ int Client::change_local_addr() {
#endif // !defined(HAVE_LINUX_RTNETLINK_H)
if (!config.quiet) {
std::cerr << "Local address is now "
<< util::straddr(&local_addr.su.sa, local_addr.len) << std::endl;
std::cerr << "Local address is now " << util::straddr(local_addr)
<< std::endl;
}
endpoints_.emplace_back();
@ -1450,8 +1439,7 @@ int Client::change_local_addr() {
ev_io_init(&ep.rev, readcb, nfd, EV_READ);
ep.rev.data = &ep;
ngtcp2_addr addr;
ngtcp2_addr_init(&addr, &local_addr.su.sa, local_addr.len);
auto addr = as_ngtcp2_addr(local_addr);
if (config.nat_rebinding) {
ngtcp2_conn_set_local_addr(conn_, &addr);
@ -1459,11 +1447,7 @@ int Client::change_local_addr() {
} else {
auto path = ngtcp2_path{
.local = addr,
.remote =
{
.addr = const_cast<sockaddr *>(&remote_addr_.su.sa),
.addrlen = remote_addr_.len,
},
.remote = as_ngtcp2_addr(remote_addr_),
.user_data = &ep,
};
if (auto rv = ngtcp2_conn_initiate_immediate_migration(conn_, &path,
@ -1672,8 +1656,7 @@ Client::send_packet(const Endpoint &ep, const ngtcp2_addr &remote_addr,
assert(static_cast<size_t>(nwrite) == data.size());
if (!config.quiet) {
std::cerr << "Sent packet: local="
<< util::straddr(&ep.addr.su.sa, ep.addr.len) << " remote="
std::cerr << "Sent packet: local=" << util::straddr(ep.addr) << " remote="
<< util::straddr(remote_addr.addr, remote_addr.addrlen)
<< " ecn=0x" << std::hex << ecn << std::dec << " " << nwrite
<< " bytes" << std::endl;
@ -1691,11 +1674,10 @@ void Client::on_send_blocked(const ngtcp2_path &path, unsigned int ecn,
auto &p = tx_.blocked;
memcpy(&p.remote_addr.su, path.remote.addr, path.remote.addrlen);
p.remote_addr.set(path.remote.addr);
auto &ep = *static_cast<Endpoint *>(path.user_data);
p.remote_addr.len = path.remote.addrlen;
p.endpoint = &ep;
p.ecn = ecn;
p.data = data;
@ -1723,13 +1705,8 @@ int Client::send_blocked_packet() {
auto &p = tx_.blocked;
ngtcp2_addr remote_addr{
.addr = &p.remote_addr.su.sa,
.addrlen = p.remote_addr.len,
};
auto [rest, rv] =
send_packet(*p.endpoint, remote_addr, p.ecn, p.data, p.gso_size);
auto [rest, rv] = send_packet(*p.endpoint, as_ngtcp2_addr(p.remote_addr),
p.ecn, p.data, p.gso_size);
if (rv != 0) {
assert(NETWORK_ERR_SEND_BLOCKED == rv);
@ -1785,6 +1762,7 @@ int Client::on_stream_close(int64_t stream_id, uint64_t app_error_code) {
auto rv = nghttp3_conn_close_stream(httpconn_, stream_id, app_error_code);
switch (rv) {
case 0:
http_stream_close(stream_id, app_error_code);
break;
case NGHTTP3_ERR_STREAM_NOT_FOUND:
// We have to handle the case when stream opened but no data is
@ -1924,11 +1902,11 @@ int Client::submit_http_request(const Stream *stream) {
int Client::recv_stream_data(uint32_t flags, int64_t stream_id,
std::span<const uint8_t> data) {
auto nconsumed =
nghttp3_conn_read_stream(httpconn_, stream_id, data.data(), data.size(),
flags & NGTCP2_STREAM_DATA_FLAG_FIN);
auto nconsumed = nghttp3_conn_read_stream2(
httpconn_, stream_id, data.data(), data.size(),
flags & NGTCP2_STREAM_DATA_FLAG_FIN, ngtcp2_conn_get_timestamp(conn_));
if (nconsumed < 0) {
std::cerr << "nghttp3_conn_read_stream: "
std::cerr << "nghttp3_conn_read_stream2: "
<< nghttp3_strerror(static_cast<int>(nconsumed)) << std::endl;
ngtcp2_ccerr_set_application_error(
&last_error_,
@ -1964,15 +1942,13 @@ int Client::select_preferred_address(Address &selected_addr,
if (!paddr->ipv4_present) {
return -1;
}
selected_addr.su.in = paddr->ipv4;
selected_addr.len = sizeof(paddr->ipv4);
selected_addr.skaddr.emplace<sockaddr_in>(paddr->ipv4);
break;
case AF_INET6:
if (!paddr->ipv6_present) {
return -1;
}
selected_addr.su.in6 = paddr->ipv6;
selected_addr.len = sizeof(paddr->ipv6);
selected_addr.skaddr.emplace<sockaddr_in6>(paddr->ipv6);
break;
default:
return -1;
@ -1980,8 +1956,8 @@ int Client::select_preferred_address(Address &selected_addr,
if (!config.quiet) {
char host[NI_MAXHOST], service[NI_MAXSERV];
if (auto rv = getnameinfo(&selected_addr.su.sa, selected_addr.len, host,
sizeof(host), service, sizeof(service),
if (auto rv = getnameinfo(selected_addr.as_sockaddr(), selected_addr.size(),
host, sizeof(host), service, sizeof(service),
NI_NUMERICHOST | NI_NUMERICSERV);
rv != 0) {
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
@ -2149,41 +2125,31 @@ int Client::reset_stream(int64_t stream_id, uint64_t app_error_code) {
return 0;
}
namespace {
int http_stream_close(nghttp3_conn *conn, int64_t stream_id,
uint64_t app_error_code, void *conn_user_data,
void *stream_user_data) {
auto c = static_cast<Client *>(conn_user_data);
if (c->http_stream_close(stream_id, app_error_code) != 0) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
int Client::http_stream_close(int64_t stream_id, uint64_t app_error_code) {
if (ngtcp2_is_bidi_stream(stream_id)) {
assert(ngtcp2_conn_is_local_stream(conn_, stream_id));
++nstreams_closed_;
} else {
assert(!ngtcp2_conn_is_local_stream(conn_, stream_id));
ngtcp2_conn_extend_max_streams_uni(conn_, 1);
void Client::http_stream_close(int64_t stream_id, uint64_t app_error_code) {
if (!ngtcp2_is_bidi_stream(stream_id)) {
return;
}
if (auto it = streams_.find(stream_id); it != std::ranges::end(streams_)) {
if (!config.quiet) {
std::cerr << "HTTP stream " << stream_id << " closed with error code "
<< app_error_code << std::endl;
}
streams_.erase(it);
assert(ngtcp2_conn_is_local_stream(conn_, stream_id));
++nstreams_closed_;
auto it = streams_.find(stream_id);
if (it == std::ranges::end(streams_)) {
return;
}
return 0;
if (!config.quiet) {
std::cerr << "HTTP stream " << stream_id << " closed with error code "
<< app_error_code << std::endl;
}
streams_.erase(it);
}
namespace {
int http_recv_settings(nghttp3_conn *conn, const nghttp3_settings *settings,
int http_recv_settings(nghttp3_conn *conn,
const nghttp3_proto_settings *settings,
void *conn_user_data) {
if (!config.quiet) {
debug::print_http_settings(settings);
@ -2226,7 +2192,6 @@ int Client::setup_httpconn() {
}
nghttp3_callbacks callbacks{
.stream_close = ::http_stream_close,
.recv_data = ::http_recv_data,
.deferred_consume = ::http_deferred_consume,
.begin_headers = ::http_begin_headers,
@ -2237,10 +2202,10 @@ int Client::setup_httpconn() {
.end_trailers = ::http_end_trailers,
.stop_sending = ::http_stop_sending,
.reset_stream = ::http_reset_stream,
.recv_settings = ::http_recv_settings,
.recv_origin = ::http_recv_origin,
.end_origin = ::http_end_origin,
.rand = rand_bytes,
.recv_settings2 = ::http_recv_settings,
};
nghttp3_settings settings;
nghttp3_settings_default(&settings);
@ -2329,15 +2294,15 @@ int run(Client &c, const char *addr, const char *port,
}
#ifdef HAVE_LINUX_RTNETLINK_H
in_addr_union iau;
InAddr ia;
if (get_local_addr(iau, remote_addr) != 0) {
if (get_local_addr(ia, remote_addr) != 0) {
std::cerr << "Could not get local address" << std::endl;
close(fd);
return -1;
}
if (bind_addr(local_addr, fd, &iau, remote_addr.su.sa.sa_family) != 0) {
if (bind_addr(local_addr, fd, ia, remote_addr.family()) != 0) {
close(fd);
return -1;
}
@ -2436,35 +2401,11 @@ void print_usage() {
}
} // namespace
namespace {
void config_set_default(Config &config) {
config = Config{
.tx_loss_prob = 0.,
.rx_loss_prob = 0.,
.fd = -1,
.ciphers = util::crypto_default_ciphers(),
.groups = util::crypto_default_groups(),
.version = NGTCP2_PROTO_VER_V1,
.timeout = 30 * NGTCP2_SECONDS,
.http_method = "GET"sv,
.max_data = 24_m,
.max_stream_data_bidi_local = 16_m,
.max_stream_data_uni = 16_m,
.max_streams_uni = 100,
.cc_algo = NGTCP2_CC_ALGO_CUBIC,
.initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT,
.handshake_timeout = UINT64_MAX,
.ack_thresh = 2,
.initial_pkt_num = UINT32_MAX,
};
}
} // namespace
namespace {
void print_help() {
print_usage();
config_set_default(config);
Config config;
std::cout << R"(
<HOST> Remote server host (DNS name or IP address). In case of
@ -2677,6 +2618,15 @@ Options:
the handshake fails with ech_required alert, ECH retry
configs, if provided by server, will be written to
<PATH>.
--no-gso Disables GSO.
--show-stat Print the connection statistics when the connection is
closed.
--gso-burst=<N>
The maximum number of packets to aggregate for GSO. If
GSO is disabled, this is the maximum number of packets
to send per an event loop in a single connection. It
defaults to 0, which means it is not limited by the
configuration.
-h, --help Display this help and exit.
---
@ -2697,7 +2647,6 @@ Options:
} // namespace
int main(int argc, char **argv) {
config_set_default(config);
char *data_path = nullptr;
const char *private_key_file = nullptr;
const char *cert_file = nullptr;
@ -2762,6 +2711,9 @@ int main(int argc, char **argv) {
{"initial-pkt-num", required_argument, &flag, 42},
{"pmtud-probes", required_argument, &flag, 43},
{"ech-config-list-file", required_argument, &flag, 44},
{"no-gso", no_argument, &flag, 45},
{"show-stat", no_argument, &flag, 46},
{"gso-burst", required_argument, &flag, 47},
{},
};
@ -3092,9 +3044,9 @@ int main(int argc, char **argv) {
if (auto n = util::parse_uint_iec(optarg); !n) {
std::cerr << "max-udp-payload-size: invalid argument" << std::endl;
exit(EXIT_FAILURE);
} else if (*n > 64_k) {
std::cerr << "max-udp-payload-size: must not exceed 65536"
<< std::endl;
} else if (*n > NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE) {
std::cerr << "max-udp-payload-size: must not exceed "
<< NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE << std::endl;
exit(EXIT_FAILURE);
} else if (*n == 0) {
std::cerr << "max-udp-payload-size: must not be 0" << std::endl;
@ -3212,10 +3164,12 @@ int main(int argc, char **argv) {
if (auto n = util::parse_uint_iec(s); !n) {
std::cerr << "pmtud-probes: invalid argument" << std::endl;
exit(EXIT_FAILURE);
} else if (*n <= 1200 || *n >= 64_k) {
std::cerr
<< "pmtud-probes: must be in range [1201, 65535], inclusive."
<< std::endl;
} else if (*n <= NGTCP2_MAX_UDP_PAYLOAD_SIZE ||
*n > NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE) {
std::cerr << "pmtud-probes: must be in range ["
<< NGTCP2_MAX_UDP_PAYLOAD_SIZE + 1 << ", "
<< NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE << "], inclusive."
<< std::endl;
exit(EXIT_FAILURE);
} else {
config.pmtud_probes.push_back(static_cast<uint16_t>(*n));
@ -3227,11 +3181,37 @@ int main(int argc, char **argv) {
// --ech-config-list-file
config.ech_config_list_file = optarg;
break;
case 45:
// --no-gso
config.no_gso = true;
break;
case 46:
// --show-stat
config.show_stat = true;
break;
case 47: {
// --gso-burst
auto n = util::parse_uint(optarg);
if (!n) {
std::cerr << "gso-burst: invalid argument" << std::endl;
exit(EXIT_FAILURE);
}
if (*n > 64) {
std::cerr << "gso-burst: must be in range [0, 64], inclusive."
<< std::endl;
exit(EXIT_FAILURE);
}
config.gso_burst = static_cast<size_t>(*n);
break;
}
}
break;
default:
break;
};
}
}
if (argc - optind < 2) {
@ -3327,7 +3307,7 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
auto ev_loop_d = defer(ev_loop_destroy, EV_DEFAULT);
auto ev_loop_d = defer([] { ev_loop_destroy(EV_DEFAULT); });
auto keylog_filename = getenv("SSLKEYLOGFILE");
if (keylog_filename) {

View File

@ -59,7 +59,7 @@ struct Stream {
Request req;
int64_t stream_id;
int fd;
int fd{-1};
};
class Client;
@ -67,8 +67,8 @@ class Client;
struct Endpoint {
Address addr;
ev_io rev;
Client *client;
int fd;
Client *client{};
int fd{};
};
class Client : public ClientBase {
@ -133,7 +133,7 @@ public:
int extend_max_stream_data(int64_t stream_id, uint64_t max_data);
int stop_sending(int64_t stream_id, uint64_t app_error_code);
int reset_stream(int64_t stream_id, uint64_t app_error_code);
int http_stream_close(int64_t stream_id, uint64_t app_error_code);
void http_stream_close(int64_t stream_id, uint64_t app_error_code);
void on_send_blocked(const ngtcp2_path &path, unsigned int ecn,
std::span<const uint8_t> data, size_t gso_size);
@ -161,24 +161,24 @@ private:
struct ev_loop *loop_;
std::unordered_map<int64_t, std::unique_ptr<Stream>> streams_;
std::vector<uint32_t> offered_versions_;
nghttp3_conn *httpconn_;
nghttp3_conn *httpconn_{};
// addr_ is the server host address.
const char *addr_;
const char *addr_{};
// port_ is the server port.
const char *port_;
const char *port_{};
// nstreams_done_ is the number of streams opened.
size_t nstreams_done_;
size_t nstreams_done_{};
// nstreams_closed_ is the number of streams get closed.
size_t nstreams_closed_;
size_t nstreams_closed_{};
// nkey_update_ is the number of key update occurred.
size_t nkey_update_;
size_t nkey_update_{};
uint32_t client_chosen_version_;
uint32_t original_version_;
// early_data_ is true if client attempts to do 0RTT data transfer.
bool early_data_;
bool early_data_{};
// handshake_confirmed_ gets true after handshake has been
// confirmed.
bool handshake_confirmed_;
bool handshake_confirmed_{};
bool no_gso_;
struct {
@ -191,8 +191,8 @@ private:
std::span<const uint8_t> data;
size_t gso_size;
} blocked;
std::array<uint8_t, 64_k> data;
} tx_;
} tx_{};
std::array<uint8_t, 64_k> txbuf_;
};
#endif // !defined(CLIENT_H)

View File

@ -43,16 +43,16 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) {
return c->conn();
}
ClientBase::ClientBase()
: conn_ref_{get_conn, this},
qlog_(nullptr),
conn_(nullptr),
ticket_received_(false) {
ClientBase::ClientBase() : conn_ref_{get_conn, this} {
ngtcp2_ccerr_default(&last_error_);
}
ClientBase::~ClientBase() {
if (conn_) {
if (config.show_stat) {
debug::print_conn_info(conn_);
}
ngtcp2_conn_del(conn_);
}

View File

@ -40,6 +40,7 @@
#include "tls_client_session.h"
#include "network.h"
#include "shared.h"
#include "util.h"
using namespace ngtcp2;
@ -50,55 +51,55 @@ struct Request {
};
struct Config {
ngtcp2_cid dcid;
ngtcp2_cid scid;
bool scid_present;
ngtcp2_cid dcid{};
ngtcp2_cid scid{};
bool scid_present{};
// tx_loss_prob is probability of losing outgoing packet.
double tx_loss_prob;
double tx_loss_prob{};
// rx_loss_prob is probability of losing incoming packet.
double rx_loss_prob;
double rx_loss_prob{};
// fd is a file descriptor to read input for streams.
int fd;
int fd{-1};
// ciphers is the list of enabled ciphers.
const char *ciphers;
const char *ciphers{util::crypto_default_ciphers()};
// groups is the list of supported groups.
const char *groups;
const char *groups{util::crypto_default_groups()};
// nstreams is the number of streams to open.
size_t nstreams;
size_t nstreams{};
// data is the pointer to memory region which maps file denoted by
// fd.
uint8_t *data;
uint8_t *data{};
// datalen is the length of file denoted by fd.
size_t datalen;
size_t datalen{};
// version is a QUIC version to use.
uint32_t version;
uint32_t version{NGTCP2_PROTO_VER_V1};
// quiet suppresses the output normally shown except for the error
// messages.
bool quiet;
bool quiet{};
// timeout is an idle timeout for QUIC connection.
ngtcp2_duration timeout;
ngtcp2_duration timeout{30 * NGTCP2_SECONDS};
// session_file is a path to a file to write, and read TLS session.
const char *session_file;
const char *session_file{};
// tp_file is a path to a file to write, and read QUIC transport
// parameters.
const char *tp_file;
const char *tp_file{};
// show_secret is true if transport secrets should be printed out.
bool show_secret;
bool show_secret{};
// change_local_addr is the duration after which client changes
// local address.
ngtcp2_duration change_local_addr;
ngtcp2_duration change_local_addr{};
// key_update is the duration after which client initiates key
// update.
ngtcp2_duration key_update;
ngtcp2_duration key_update{};
// delay_stream is the duration after which client sends the first
// 1-RTT stream.
ngtcp2_duration delay_stream;
ngtcp2_duration delay_stream{};
// nat_rebinding is true if simulated NAT rebinding is enabled.
bool nat_rebinding;
bool nat_rebinding{};
// no_preferred_addr is true if client do not follow preferred
// address offered by server.
bool no_preferred_addr;
std::string_view http_method;
bool no_preferred_addr{};
std::string_view http_method{"GET"sv};
// download is a path to a directory where a downloaded file is
// saved. If it is empty, no file is saved.
std::string_view download;
@ -106,55 +107,55 @@ struct Config {
std::vector<Request> requests;
// no_quic_dump is true if hexdump of QUIC STREAM and CRYPTO data
// should be disabled.
bool no_quic_dump;
bool no_quic_dump{};
// no_http_dump is true if hexdump of HTTP response body should be
// disabled.
bool no_http_dump;
bool no_http_dump{};
// qlog_file is the path to write qlog.
std::string_view qlog_file;
// qlog_dir is the path to directory where qlog is stored. qlog_dir
// and qlog_file are mutually exclusive.
std::string_view qlog_dir;
// max_data is the initial connection-level flow control window.
uint64_t max_data;
uint64_t max_data{24_m};
// max_stream_data_bidi_local is the initial stream-level flow
// control window for a bidirectional stream that the local endpoint
// initiates.
uint64_t max_stream_data_bidi_local;
uint64_t max_stream_data_bidi_local{16_m};
// max_stream_data_bidi_remote is the initial stream-level flow
// control window for a bidirectional stream that the remote
// endpoint initiates.
uint64_t max_stream_data_bidi_remote;
uint64_t max_stream_data_bidi_remote{};
// max_stream_data_uni is the initial stream-level flow control
// window for a unidirectional stream.
uint64_t max_stream_data_uni;
uint64_t max_stream_data_uni{16_m};
// max_streams_bidi is the number of the concurrent bidirectional
// streams.
uint64_t max_streams_bidi;
uint64_t max_streams_bidi{};
// max_streams_uni is the number of the concurrent unidirectional
// streams.
uint64_t max_streams_uni;
uint64_t max_streams_uni{100};
// max_window is the maximum connection-level flow control window
// size if auto-tuning is enabled.
uint64_t max_window;
uint64_t max_window{};
// max_stream_window is the maximum stream-level flow control window
// size if auto-tuning is enabled.
uint64_t max_stream_window;
uint64_t max_stream_window{};
// exit_on_first_stream_close is the flag that if it is true, client
// exits when a first HTTP stream gets closed. It is not
// necessarily the same time when the underlying QUIC stream closes
// due to the QPACK synchronization.
bool exit_on_first_stream_close;
bool exit_on_first_stream_close{};
// exit_on_all_streams_close is the flag that if it is true, client
// exits when all HTTP streams get closed.
bool exit_on_all_streams_close;
bool exit_on_all_streams_close{};
// disable_early_data disables early data.
bool disable_early_data;
bool disable_early_data{};
// static_secret is used to derive keying materials for Stateless
// Retry token.
std::array<uint8_t, 32> static_secret;
// cc_algo is the congestion controller algorithm.
ngtcp2_cc_algo cc_algo;
ngtcp2_cc_algo cc_algo{NGTCP2_CC_ALGO_CUBIC};
// token_file is a path to file to read or write token from
// NEW_TOKEN frame.
std::string_view token_file;
@ -162,13 +163,13 @@ struct Config {
// remote host.
std::string_view sni;
// initial_rtt is an initial RTT.
ngtcp2_duration initial_rtt;
ngtcp2_duration initial_rtt{NGTCP2_DEFAULT_INITIAL_RTT};
// max_udp_payload_size is the maximum UDP payload size that client
// transmits.
size_t max_udp_payload_size;
size_t max_udp_payload_size{};
// handshake_timeout is the period of time before giving up QUIC
// connection establishment.
ngtcp2_duration handshake_timeout;
ngtcp2_duration handshake_timeout{UINT64_MAX};
// preferred_versions includes QUIC versions in the order of
// preference. Client uses this field to select a version from the
// version set offered in Version Negotiation packet.
@ -178,24 +179,32 @@ struct Config {
// transport_parameter.
std::vector<uint32_t> available_versions;
// no_pmtud disables Path MTU Discovery.
bool no_pmtud;
bool no_pmtud{};
// ack_thresh is the minimum number of the received ACK eliciting
// packets that triggers immediate acknowledgement.
size_t ack_thresh;
size_t ack_thresh{2};
// wait_for_ticket, if true, waits for a ticket to be received
// before exiting on exit_on_first_stream_close or
// exit_on_all_streams_close.
bool wait_for_ticket;
bool wait_for_ticket{};
// initial_pkt_num is the initial packet number for each packet
// number space. If it is set to UINT32_MAX, it is chosen randomly.
uint32_t initial_pkt_num;
uint32_t initial_pkt_num{UINT32_MAX};
// pmtud_probes is the array of UDP datagram payload size to probes.
std::vector<uint16_t> pmtud_probes;
// ech_config_list contains ECHConfigList.
std::vector<uint8_t> ech_config_list;
// ech_config_list_file is a path to a file to read and write
// ECHConfigList.
const char *ech_config_list_file;
const char *ech_config_list_file{};
// no_gso disables GSO.
bool no_gso{};
// show_stat, if true, displays the connection statistics when the
// connection is closed.
bool show_stat{};
// gso_burst is the number of packets to aggregate in GSO. 0 means
// it is not limited by the configuration.
size_t gso_burst{};
};
class ClientBase {
@ -218,10 +227,10 @@ public:
protected:
ngtcp2_crypto_conn_ref conn_ref_;
TLSClientSession tls_session_;
FILE *qlog_;
ngtcp2_conn *conn_;
FILE *qlog_{};
ngtcp2_conn *conn_{};
ngtcp2_ccerr last_error_;
bool ticket_received_;
bool ticket_received_{};
};
void qlog_write_cb(void *user_data, uint32_t flags, const void *data,

View File

@ -283,7 +283,7 @@ void print_http_response_headers(int64_t stream_id, const nghttp3_nv *nva,
}
}
void print_http_settings(const nghttp3_settings *settings) {
void print_http_settings(const nghttp3_proto_settings *settings) {
fprintf(outfile,
"http: remote settings\n"
"http: SETTINGS_MAX_FIELD_SECTION_SIZE=%" PRIu64 "\n"
@ -317,6 +317,29 @@ std::string_view secret_title(ngtcp2_encryption_level level) {
}
}
void print_conn_info(ngtcp2_conn *conn) {
ngtcp2_conn_info cinfo;
ngtcp2_conn_get_conn_info(conn, &cinfo);
std::cout << "# Connection Statistics (see ngtcp2_conn_info for details)\n"
"min_rtt="
<< util::format_durationf(cinfo.min_rtt) << '\n'
<< "smoothed_rtt=" << util::format_durationf(cinfo.smoothed_rtt)
<< '\n'
<< "rttvar=" << util::format_durationf(cinfo.rttvar) << '\n'
<< "cwnd=" << cinfo.cwnd << '\n'
<< "ssthresh=" << cinfo.ssthresh << '\n'
<< "pkt_sent=" << cinfo.pkt_sent << '\n'
<< "bytes_sent=" << cinfo.bytes_sent << '\n'
<< "pkt_recv=" << cinfo.pkt_recv << '\n'
<< "bytes_recv=" << cinfo.bytes_recv << '\n'
<< "pkt_lost=" << cinfo.pkt_lost << '\n'
<< "bytes_lost=" << cinfo.bytes_lost << '\n'
<< "ping_recv=" << cinfo.ping_recv << '\n'
<< "pkt_discarded=" << cinfo.pkt_discarded << std::endl;
}
} // namespace debug
} // namespace ngtcp2

View File

@ -116,7 +116,7 @@ void print_http_request_headers(int64_t stream_id, const nghttp3_nv *nva,
void print_http_response_headers(int64_t stream_id, const nghttp3_nv *nva,
size_t nvlen);
void print_http_settings(const nghttp3_settings *settings);
void print_http_settings(const nghttp3_proto_settings *settings);
void print_http_origin(const uint8_t *origin, size_t originlen);
@ -124,6 +124,8 @@ void print_http_end_origin();
std::string_view secret_title(ngtcp2_encryption_level level);
void print_conn_info(ngtcp2_conn *conn);
} // namespace debug
} // namespace ngtcp2

View File

@ -32,11 +32,17 @@
// include test cases' include files here
#include "util_test.h"
#include "siphash_test.h"
#ifdef WITH_EXAMPLE_WOLFSSL
# include "sim_test.h"
#endif // defined(WITH_EXAMPLE_WOLFSSL)
int main(int argc, char *argv[]) {
const MunitSuite suites[] = {
ngtcp2::util_suite,
ngtcp2::siphash_suite,
#ifdef WITH_EXAMPLE_WOLFSSL
ngtcp2::sim_suite,
#endif // defined(WITH_EXAMPLE_WOLFSSL)
{},
};
const MunitSuite suite = {

View File

@ -62,10 +62,10 @@ namespace {
constexpr size_t max_preferred_versionslen = 4;
} // namespace
Config config{};
Config config;
Stream::Stream(const Request &req, int64_t stream_id)
: req(req), stream_id(stream_id), fd(-1) {
: req{req}, stream_id{stream_id} {
nghttp3_buf_init(&reqbuf);
}
@ -177,25 +177,16 @@ void siginthandler(struct ev_loop *loop, ev_signal *w, int revents) {
Client::Client(struct ev_loop *loop, uint32_t client_chosen_version,
uint32_t original_version)
: remote_addr_{},
loop_(loop),
addr_(nullptr),
port_(nullptr),
nstreams_done_(0),
nstreams_closed_(0),
nkey_update_(0),
client_chosen_version_(client_chosen_version),
original_version_(original_version),
early_data_(false),
handshake_confirmed_(false),
: loop_{loop},
client_chosen_version_{client_chosen_version},
original_version_{original_version},
no_gso_{
#ifdef UDP_SEGMENT
false
config.no_gso
#else // !defined(UDP_SEGMENT)
true
#endif // !defined(UDP_SEGMENT)
},
tx_{} {
} {
ev_io_init(&wev_, writecb, 0, EV_WRITE);
wev_.data = this;
ev_timer_init(&timer_, timeoutcb, 0., 0.);
@ -515,8 +506,7 @@ int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
} // namespace
void Client::set_remote_addr(const ngtcp2_addr &remote_addr) {
memcpy(&remote_addr_.su, remote_addr.addr, remote_addr.addrlen);
remote_addr_.len = remote_addr.addrlen;
remote_addr_.set(remote_addr.addr);
}
namespace {
@ -539,8 +529,10 @@ int select_preferred_address(ngtcp2_conn *conn, ngtcp2_path *dest,
return NGTCP2_ERR_CALLBACK_FAILURE;
}
ngtcp2_addr_copy_byte(&dest->local, &(*ep)->addr.su.sa, (*ep)->addr.len);
ngtcp2_addr_copy_byte(&dest->remote, &remote_addr.su.sa, remote_addr.len);
ngtcp2_addr_copy_byte(&dest->local, (*ep)->addr.as_sockaddr(),
(*ep)->addr.size());
ngtcp2_addr_copy_byte(&dest->remote, remote_addr.as_sockaddr(),
remote_addr.size());
dest->user_data = *ep;
return 0;
@ -750,16 +742,8 @@ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
params.grease_quic_bit = 1;
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&ep.addr.su.sa),
.addrlen = ep.addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(&remote_addr.su.sa),
.addrlen = remote_addr.len,
},
.local = as_ngtcp2_addr(ep.addr),
.remote = as_ngtcp2_addr(remote_addr),
.user_data = &ep,
};
auto rv =
@ -806,16 +790,11 @@ int Client::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data) {
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&ep.addr.su.sa),
.addrlen = ep.addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.local = as_ngtcp2_addr(ep.addr),
.remote{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.user_data = const_cast<Endpoint *>(&ep),
};
if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data.data(), data.size(),
@ -845,7 +824,7 @@ int Client::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
int Client::on_read(const Endpoint &ep) {
std::array<uint8_t, 64_k> buf;
sockaddr_union su;
sockaddr_storage ss;
size_t pktcnt = 0;
ngtcp2_pkt_info pi;
@ -857,14 +836,21 @@ int Client::on_read(const Endpoint &ep) {
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(int))];
msghdr msg{
.msg_name = &su,
.msg_name = &ss,
.msg_iov = &msg_iov,
.msg_iovlen = 1,
.msg_control = msg_ctrl,
};
for (;;) {
msg.msg_namelen = sizeof(su);
auto start = util::timestamp();
for (; pktcnt < MAX_RECV_PKTS;) {
if (util::recv_pkt_time_threshold_exceeded(
config.cc_algo == NGTCP2_CC_ALGO_BBR, start, pktcnt)) {
break;
}
msg.msg_namelen = sizeof(ss);
msg.msg_controllen = sizeof(msg_ctrl);
auto nread = recvmsg(ep.fd, &msg, 0);
@ -883,7 +869,7 @@ int Client::on_read(const Endpoint &ep) {
continue;
}
pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
pi.ecn = msghdr_get_ecn(&msg, ss.ss_family);
auto gso_size = msghdr_get_udp_gro(&msg);
if (gso_size == 0) {
gso_size = static_cast<size_t>(nread);
@ -897,9 +883,10 @@ int Client::on_read(const Endpoint &ep) {
++pktcnt;
if (!config.quiet) {
std::cerr << "Received packet: local="
<< util::straddr(&ep.addr.su.sa, ep.addr.len)
<< " remote=" << util::straddr(&su.sa, msg.msg_namelen)
std::cerr << "Received packet: local=" << util::straddr(ep.addr)
<< " remote="
<< util::straddr(reinterpret_cast<const sockaddr *>(&ss),
msg.msg_namelen)
<< " ecn=0x" << std::hex << static_cast<uint32_t>(pi.ecn)
<< std::dec << " " << datalen << " bytes" << std::endl;
}
@ -913,8 +900,8 @@ int Client::on_read(const Endpoint &ep) {
if (!config.quiet) {
std::cerr << "** Simulated incoming packet loss **" << std::endl;
}
} else if (feed_data(ep, &su.sa, msg.msg_namelen, &pi,
{data.data(), datalen}) != 0) {
} else if (feed_data(ep, reinterpret_cast<const sockaddr *>(&ss),
msg.msg_namelen, &pi, {data.data(), datalen}) != 0) {
return -1;
}
@ -924,10 +911,6 @@ int Client::on_read(const Endpoint &ep) {
break;
}
}
if (pktcnt >= 10) {
break;
}
}
if (should_exit()) {
@ -1057,18 +1040,21 @@ int Client::write_streams() {
ngtcp2_pkt_info pi;
size_t gso_size;
auto ts = util::timestamp();
auto txbuf = std::span{tx_.data};
auto txbuf = std::span{txbuf_};
auto buflen = util::clamp_buffer_size(conn_, txbuf.size(), config.gso_burst);
ngtcp2_path_storage_zero(&ps);
auto nwrite =
ngtcp2_conn_write_aggregate_pkt(conn_, &ps.path, &pi, txbuf.data(),
txbuf.size(), &gso_size, ::write_pkt, ts);
auto nwrite = ngtcp2_conn_write_aggregate_pkt2(
conn_, &ps.path, &pi, txbuf.data(), buflen, &gso_size, ::write_pkt,
config.gso_burst, ts);
if (nwrite < 0) {
disconnect();
return -1;
}
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
if (nwrite == 0) {
return 0;
}
@ -1123,8 +1109,7 @@ void Client::update_timer() {
#ifdef HAVE_LINUX_RTNETLINK_H
namespace {
int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
int family) {
int bind_addr(Address &local_addr, int fd, const InAddr &ia, int family) {
addrinfo hints{
.ai_flags = AI_PASSIVE,
.ai_family = family,
@ -1134,15 +1119,16 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
char *node;
std::array<char, NI_MAXHOST> nodebuf;
if (iau) {
if (inet_ntop(family, iau, nodebuf.data(), nodebuf.size()) == nullptr) {
if (in_addr_empty(ia)) {
node = nullptr;
} else {
if (inet_ntop(family, in_addr_get_ptr(ia), nodebuf.data(),
nodebuf.size()) == nullptr) {
std::cerr << "inet_ntop: " << strerror(errno) << std::endl;
return -1;
}
node = nodebuf.data();
} else {
node = nullptr;
}
if (auto rv = getaddrinfo(node, "0", &hints, &res); rv != 0) {
@ -1150,7 +1136,7 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
return -1;
}
auto res_d = defer(freeaddrinfo, res);
auto res_d = defer([res] { freeaddrinfo(res); });
for (rp = res; rp; rp = rp->ai_next) {
if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
@ -1163,13 +1149,14 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
return -1;
}
socklen_t len = sizeof(local_addr.su.storage);
if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
sockaddr_storage ss;
socklen_t len = sizeof(ss);
if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) == -1) {
std::cerr << "getsockname: " << strerror(errno) << std::endl;
return -1;
}
local_addr.len = len;
local_addr.ifindex = 0;
local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
return 0;
}
@ -1179,18 +1166,19 @@ int bind_addr(Address &local_addr, int fd, const in_addr_union *iau,
#ifndef HAVE_LINUX_RTNETLINK_H
namespace {
int connect_sock(Address &local_addr, int fd, const Address &remote_addr) {
if (connect(fd, &remote_addr.su.sa, remote_addr.len) != 0) {
if (connect(fd, remote_addr.as_sockaddr(), remote_addr.size()) != 0) {
std::cerr << "connect: " << strerror(errno) << std::endl;
return -1;
}
socklen_t len = sizeof(local_addr.su.storage);
if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
sockaddr_storage ss;
socklen_t len = sizeof(ss);
if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) == -1) {
std::cerr << "getsockname: " << strerror(errno) << std::endl;
return -1;
}
local_addr.len = len;
local_addr.ifindex = 0;
local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
return 0;
}
@ -1226,7 +1214,7 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
return -1;
}
auto res_d = defer(freeaddrinfo, res);
auto res_d = defer([res] { freeaddrinfo(res); });
int fd = -1;
@ -1244,9 +1232,7 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
return -1;
}
remote_addr.len = rp->ai_addrlen;
memcpy(&remote_addr.su, rp->ai_addr, rp->ai_addrlen);
remote_addr.ifindex = 0;
remote_addr.set(rp->ai_addr);
return fd;
}
@ -1254,9 +1240,9 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
#ifdef HAVE_LINUX_RTNETLINK_H
in_addr_union iau;
InAddr ia;
if (get_local_addr(iau, remote_addr) != 0) {
if (get_local_addr(ia, remote_addr) != 0) {
std::cerr << "Could not get local address for a selected preferred address"
<< std::endl;
return nullptr;
@ -1264,12 +1250,14 @@ std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
auto current_path = ngtcp2_conn_get_path(conn_);
auto current_ep = static_cast<Endpoint *>(current_path->user_data);
if (addreq(&current_ep->addr.su.sa, iau)) {
if (addreq(current_ep->addr, ia)) {
return current_ep;
}
#endif // defined(HAVE_LINUX_RTNETLINK_H)
auto fd = udp_sock(remote_addr.su.sa.sa_family);
auto family = remote_addr.family();
auto fd = udp_sock(family);
if (fd == -1) {
return nullptr;
}
@ -1277,7 +1265,7 @@ std::optional<Endpoint *> Client::endpoint_for(const Address &remote_addr) {
Address local_addr;
#ifdef HAVE_LINUX_RTNETLINK_H
if (bind_addr(local_addr, fd, &iau, remote_addr.su.sa.sa_family) != 0) {
if (bind_addr(local_addr, fd, ia, family) != 0) {
close(fd);
return nullptr;
}
@ -1312,21 +1300,23 @@ int Client::change_local_addr() {
std::cerr << "Changing local address" << std::endl;
}
auto nfd = udp_sock(remote_addr_.su.sa.sa_family);
auto family = remote_addr_.family();
auto nfd = udp_sock(family);
if (nfd == -1) {
return -1;
}
#ifdef HAVE_LINUX_RTNETLINK_H
in_addr_union iau;
InAddr ia;
if (get_local_addr(iau, remote_addr_) != 0) {
if (get_local_addr(ia, remote_addr_) != 0) {
std::cerr << "Could not get local address" << std::endl;
close(nfd);
return -1;
}
if (bind_addr(local_addr, nfd, &iau, remote_addr_.su.sa.sa_family) != 0) {
if (bind_addr(local_addr, nfd, ia, family) != 0) {
close(nfd);
return -1;
}
@ -1338,8 +1328,8 @@ int Client::change_local_addr() {
#endif // !defined(HAVE_LINUX_RTNETLINK_H)
if (!config.quiet) {
std::cerr << "Local address is now "
<< util::straddr(&local_addr.su.sa, local_addr.len) << std::endl;
std::cerr << "Local address is now " << util::straddr(local_addr)
<< std::endl;
}
endpoints_.emplace_back();
@ -1350,8 +1340,7 @@ int Client::change_local_addr() {
ev_io_init(&ep.rev, readcb, nfd, EV_READ);
ep.rev.data = &ep;
ngtcp2_addr addr;
ngtcp2_addr_init(&addr, &local_addr.su.sa, local_addr.len);
auto addr = as_ngtcp2_addr(local_addr);
if (config.nat_rebinding) {
ngtcp2_conn_set_local_addr(conn_, &addr);
@ -1359,11 +1348,7 @@ int Client::change_local_addr() {
} else {
auto path = ngtcp2_path{
.local = addr,
.remote =
{
.addr = const_cast<sockaddr *>(&remote_addr_.su.sa),
.addrlen = remote_addr_.len,
},
.remote = as_ngtcp2_addr(remote_addr_),
.user_data = &ep,
};
if (auto rv = ngtcp2_conn_initiate_immediate_migration(conn_, &path,
@ -1572,8 +1557,7 @@ Client::send_packet(const Endpoint &ep, const ngtcp2_addr &remote_addr,
assert(static_cast<size_t>(nwrite) == data.size());
if (!config.quiet) {
std::cerr << "Sent packet: local="
<< util::straddr(&ep.addr.su.sa, ep.addr.len) << " remote="
std::cerr << "Sent packet: local=" << util::straddr(ep.addr) << " remote="
<< util::straddr(remote_addr.addr, remote_addr.addrlen)
<< " ecn=0x" << std::hex << ecn << std::dec << " " << nwrite
<< " bytes" << std::endl;
@ -1591,11 +1575,10 @@ void Client::on_send_blocked(const ngtcp2_path &path, unsigned int ecn,
auto &p = tx_.blocked;
memcpy(&p.remote_addr.su, path.remote.addr, path.remote.addrlen);
p.remote_addr.set(path.remote.addr);
auto &ep = *static_cast<Endpoint *>(path.user_data);
p.remote_addr.len = path.remote.addrlen;
p.endpoint = &ep;
p.ecn = ecn;
p.data = data;
@ -1623,13 +1606,8 @@ int Client::send_blocked_packet() {
auto &p = tx_.blocked;
ngtcp2_addr remote_addr{
.addr = &p.remote_addr.su.sa,
.addrlen = p.remote_addr.len,
};
auto [rest, rv] =
send_packet(*p.endpoint, remote_addr, p.ecn, p.data, p.gso_size);
auto [rest, rv] = send_packet(*p.endpoint, as_ngtcp2_addr(p.remote_addr),
p.ecn, p.data, p.gso_size);
if (rv != 0) {
assert(NETWORK_ERR_SEND_BLOCKED == rv);
@ -1801,15 +1779,13 @@ int Client::select_preferred_address(Address &selected_addr,
if (!paddr->ipv4_present) {
return -1;
}
selected_addr.su.in = paddr->ipv4;
selected_addr.len = sizeof(paddr->ipv4);
selected_addr.skaddr.emplace<sockaddr_in>(paddr->ipv4);
break;
case AF_INET6:
if (!paddr->ipv6_present) {
return -1;
}
selected_addr.su.in6 = paddr->ipv6;
selected_addr.len = sizeof(paddr->ipv6);
selected_addr.skaddr.emplace<sockaddr_in6>(paddr->ipv6);
break;
default:
return -1;
@ -1817,8 +1793,8 @@ int Client::select_preferred_address(Address &selected_addr,
if (!config.quiet) {
char host[NI_MAXHOST], service[NI_MAXSERV];
if (auto rv = getnameinfo(&selected_addr.su.sa, selected_addr.len, host,
sizeof(host), service, sizeof(service),
if (auto rv = getnameinfo(selected_addr.as_sockaddr(), selected_addr.size(),
host, sizeof(host), service, sizeof(service),
NI_NUMERICHOST | NI_NUMERICSERV);
rv != 0) {
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
@ -1847,15 +1823,15 @@ int run(Client &c, const char *addr, const char *port,
}
#ifdef HAVE_LINUX_RTNETLINK_H
in_addr_union iau;
InAddr ia;
if (get_local_addr(iau, remote_addr) != 0) {
if (get_local_addr(ia, remote_addr) != 0) {
std::cerr << "Could not get local address" << std::endl;
close(fd);
return -1;
}
if (bind_addr(local_addr, fd, &iau, remote_addr.su.sa.sa_family) != 0) {
if (bind_addr(local_addr, fd, ia, remote_addr.family()) != 0) {
close(fd);
return -1;
}
@ -1949,35 +1925,11 @@ void print_usage() {
}
} // namespace
namespace {
void config_set_default(Config &config) {
config = Config{
.tx_loss_prob = 0.,
.rx_loss_prob = 0.,
.fd = -1,
.ciphers = util::crypto_default_ciphers(),
.groups = util::crypto_default_groups(),
.version = NGTCP2_PROTO_VER_V1,
.timeout = 30 * NGTCP2_SECONDS,
.http_method = "GET"sv,
.max_data = 24_m,
.max_stream_data_bidi_local = 16_m,
.max_stream_data_uni = 16_m,
.max_streams_uni = 100,
.cc_algo = NGTCP2_CC_ALGO_CUBIC,
.initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT,
.handshake_timeout = UINT64_MAX,
.ack_thresh = 2,
.initial_pkt_num = UINT32_MAX,
};
}
} // namespace
namespace {
void print_help() {
print_usage();
config_set_default(config);
Config config;
std::cout << R"(
<HOST> Remote server host (DNS name or IP address). In case of
@ -2186,6 +2138,15 @@ Options:
the handshake fails with ech_required alert, ECH retry
configs, if provided by server, will be written to
<PATH>.
--no-gso Disables GSO.
--show-stat Print the connection statistics when the connection is
closed.
--gso-burst=<N>
The maximum number of packets to aggregate for GSO. If
GSO is disabled, this is the maximum number of packets
to send per an event loop in a single connection. It
defaults to 0, which means it is not limited by the
configuration.
-h, --help Display this help and exit.
---
@ -2206,7 +2167,6 @@ Options:
} // namespace
int main(int argc, char **argv) {
config_set_default(config);
char *data_path = nullptr;
const char *private_key_file = nullptr;
const char *cert_file = nullptr;
@ -2270,6 +2230,9 @@ int main(int argc, char **argv) {
{"initial-pkt-num", required_argument, &flag, 42},
{"pmtud-probes", required_argument, &flag, 43},
{"ech-config-list-file", required_argument, &flag, 44},
{"no-gso", no_argument, &flag, 45},
{"show-stat", no_argument, &flag, 46},
{"gso-burst", required_argument, &flag, 47},
{},
};
@ -2580,9 +2543,9 @@ int main(int argc, char **argv) {
if (auto n = util::parse_uint_iec(optarg); !n) {
std::cerr << "max-udp-payload-size: invalid argument" << std::endl;
exit(EXIT_FAILURE);
} else if (*n > 64_k) {
std::cerr << "max-udp-payload-size: must not exceed 65536"
<< std::endl;
} else if (*n > NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE) {
std::cerr << "max-udp-payload-size: must not exceed "
<< NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE << std::endl;
exit(EXIT_FAILURE);
} else if (*n == 0) {
std::cerr << "max-udp-payload-size: must not be 0" << std::endl;
@ -2700,10 +2663,12 @@ int main(int argc, char **argv) {
if (auto n = util::parse_uint_iec(s); !n) {
std::cerr << "pmtud-probes: invalid argument" << std::endl;
exit(EXIT_FAILURE);
} else if (*n <= 1200 || *n >= 64_k) {
std::cerr
<< "pmtud-probes: must be in range [1201, 65535], inclusive."
<< std::endl;
} else if (*n <= NGTCP2_MAX_UDP_PAYLOAD_SIZE ||
*n > NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE) {
std::cerr << "pmtud-probes: must be in range ["
<< NGTCP2_MAX_UDP_PAYLOAD_SIZE + 1 << ", "
<< NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE << "], inclusive."
<< std::endl;
exit(EXIT_FAILURE);
} else {
config.pmtud_probes.push_back(static_cast<uint16_t>(*n));
@ -2715,11 +2680,37 @@ int main(int argc, char **argv) {
// --ech-config-list-file
config.ech_config_list_file = optarg;
break;
case 45:
// --no-gso
config.no_gso = true;
break;
case 46:
// --show-stat
config.show_stat = true;
break;
case 47: {
// --gso-burst
auto n = util::parse_uint(optarg);
if (!n) {
std::cerr << "gso-burst: invalid argument" << std::endl;
exit(EXIT_FAILURE);
}
if (*n > 64) {
std::cerr << "gso-burst: must be in range [0, 64], inclusive."
<< std::endl;
exit(EXIT_FAILURE);
}
config.gso_burst = static_cast<size_t>(*n);
break;
}
}
break;
default:
break;
};
}
}
if (argc - optind < 2) {
@ -2815,7 +2806,7 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
auto ev_loop_d = defer(ev_loop_destroy, EV_DEFAULT);
auto ev_loop_d = defer([] { ev_loop_destroy(EV_DEFAULT); });
auto keylog_filename = getenv("SSLKEYLOGFILE");
if (keylog_filename) {

View File

@ -61,7 +61,7 @@ struct Stream {
Request req;
int64_t stream_id;
int fd;
int fd{-1};
std::string rawreqbuf;
nghttp3_buf reqbuf;
};
@ -77,8 +77,8 @@ class Client;
struct Endpoint {
Address addr;
ev_io rev;
Client *client;
int fd;
Client *client{};
int fd{};
};
class Client : public ClientBase {
@ -167,22 +167,22 @@ private:
std::set<Stream *, StreamIDLess> sendq_;
std::vector<uint32_t> offered_versions_;
// addr_ is the server host address.
const char *addr_;
const char *addr_{};
// port_ is the server port.
const char *port_;
const char *port_{};
// nstreams_done_ is the number of streams opened.
size_t nstreams_done_;
size_t nstreams_done_{};
// nstreams_closed_ is the number of streams get closed.
size_t nstreams_closed_;
size_t nstreams_closed_{};
// nkey_update_ is the number of key update occurred.
size_t nkey_update_;
size_t nkey_update_{};
uint32_t client_chosen_version_;
uint32_t original_version_;
// early_data_ is true if client attempts to do 0RTT data transfer.
bool early_data_;
bool early_data_{};
// handshake_confirmed_ gets true after handshake has been
// confirmed.
bool handshake_confirmed_;
bool handshake_confirmed_{};
bool no_gso_;
struct {
@ -195,8 +195,8 @@ private:
std::span<const uint8_t> data;
size_t gso_size;
} blocked;
std::array<uint8_t, 64_k> data;
} tx_;
} tx_{};
std::array<uint8_t, 64_k> txbuf_;
};
#endif // !defined(H09CLIENT_H)

View File

@ -66,22 +66,14 @@ namespace {
constexpr size_t max_preferred_versionslen = 4;
} // namespace
namespace {
constexpr size_t NGTCP2_STATELESS_RESET_BURST = 100;
} // namespace
namespace {
constexpr size_t NGTCP2_TX_BUFLEN = 64_k;
} // namespace
namespace {
auto randgen = util::make_mt19937();
} // namespace
Config config{};
Config config;
Stream::Stream(int64_t stream_id, Handler *handler)
: stream_id(stream_id), handler(handler), eos(false) {
: stream_id{stream_id}, handler{handler} {
nghttp3_buf_init(&respbuf);
htp.data = this;
http_parser_init(&htp, HTTP_REQUEST);
@ -156,10 +148,10 @@ enum FileEntryFlag {
};
struct FileEntry {
uint64_t len;
void *map;
int fd;
uint8_t flags;
uint64_t len{};
void *map{};
int fd{};
uint8_t flags{};
};
namespace {
@ -183,7 +175,7 @@ std::pair<FileEntry, int> Stream::open_file(const std::string &path) {
return {{}, -1};
}
FileEntry fe{};
FileEntry fe;
if (st.st_mode & S_IFDIR) {
fe.flags |= FILE_ENTRY_TYPE_DIR;
fe.fd = -1;
@ -319,10 +311,7 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
goto fail;
}
rv = h->on_write();
if (rv != 0) {
goto fail;
}
h->signal_write();
return;
@ -339,23 +328,14 @@ fail:
} // namespace
Handler::Handler(struct ev_loop *loop, Server *server)
: loop_(loop),
server_(server),
qlog_(nullptr),
scid_{},
nkey_update_(0),
: loop_{loop},
server_{server},
no_gso_{
#ifdef UDP_SEGMENT
false
config.no_gso
#else // !defined(UDP_SEGMENT)
true
#endif // !defined(UDP_SEGMENT)
},
close_wait_{
.next_pkts_recv = 1,
},
tx_{
.data = std::unique_ptr<uint8_t[]>(new uint8_t[NGTCP2_TX_BUFLEN]),
} {
ev_io_init(&wev_, writecb, 0, EV_WRITE);
wev_.data = this;
@ -416,19 +396,17 @@ int Handler::handshake_completed() {
token.data(), config.static_secret.data(), config.static_secret.size(),
path->remote.addr, path->remote.addrlen, t);
if (tokenlen < 0) {
if (!config.quiet) {
std::cerr << "Unable to generate token" << std::endl;
}
std::cerr << "Unable to generate token" << std::endl;
return 0;
}
if (auto rv = ngtcp2_conn_submit_new_token(conn_, token.data(),
as_unsigned(tokenlen));
rv != 0) {
if (!config.quiet) {
std::cerr << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv)
<< std::endl;
}
std::cerr << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
@ -515,9 +493,9 @@ void Handler::on_stream_open(int64_t stream_id) {
if (!ngtcp2_is_bidi_stream(stream_id)) {
return;
}
auto it = streams_.find(stream_id);
(void)it;
assert(it == std::ranges::end(streams_));
assert(!streams_.contains(stream_id));
streams_.emplace(stream_id, std::make_unique<Stream>(stream_id, this));
}
@ -610,9 +588,7 @@ int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
token.data(), config.static_secret.data(), config.static_secret.size(),
path->remote.addr, path->remote.addrlen, t);
if (tokenlen < 0) {
if (!config.quiet) {
std::cerr << "Unable to generate token" << std::endl;
}
std::cerr << "Unable to generate token" << std::endl;
return 0;
}
@ -620,10 +596,8 @@ int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path,
if (auto rv =
ngtcp2_conn_submit_new_token(conn, token.data(), as_unsigned(tokenlen));
rv != 0) {
if (!config.quiet) {
std::cerr << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv)
<< std::endl;
}
std::cerr << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv)
<< std::endl;
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@ -670,7 +644,7 @@ void Handler::write_qlog(const void *data, size_t datalen) {
}
int Handler::init(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen, const ngtcp2_cid *dcid,
const Address &remote_addr, const ngtcp2_cid *dcid,
const ngtcp2_cid *scid, const ngtcp2_cid *ocid,
std::span<const uint8_t> token, ngtcp2_token_type token_type,
uint32_t version, TLSServerContext &tls_ctx) {
@ -789,16 +763,19 @@ int Handler::init(const Endpoint &ep, const Address &local_addr,
return -1;
}
if (config.preferred_ipv4_addr.len || config.preferred_ipv6_addr.len) {
if (!config.preferred_ipv4_addr.empty() ||
!config.preferred_ipv6_addr.empty()) {
params.preferred_addr_present = 1;
if (config.preferred_ipv4_addr.len) {
params.preferred_addr.ipv4 = config.preferred_ipv4_addr.su.in;
if (!config.preferred_ipv4_addr.empty()) {
params.preferred_addr.ipv4 =
std::get<sockaddr_in>(config.preferred_ipv4_addr.skaddr);
params.preferred_addr.ipv4_present = 1;
}
if (config.preferred_ipv6_addr.len) {
params.preferred_addr.ipv6 = config.preferred_ipv6_addr.su.in6;
if (!config.preferred_ipv6_addr.empty()) {
params.preferred_addr.ipv6 =
std::get<sockaddr_in6>(config.preferred_ipv6_addr.skaddr);
params.preferred_addr.ipv6_present = 1;
}
@ -820,16 +797,8 @@ int Handler::init(const Endpoint &ep, const Address &local_addr,
}
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.local = as_ngtcp2_addr(local_addr),
.remote = as_ngtcp2_addr(remote_addr),
.user_data = const_cast<Endpoint *>(&ep),
};
if (auto rv =
@ -854,20 +823,11 @@ int Handler::init(const Endpoint &ep, const Address &local_addr,
}
int Handler::feed_data(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data) {
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.local = as_ngtcp2_addr(local_addr),
.remote = as_ngtcp2_addr(remote_addr),
.user_data = const_cast<Endpoint *>(&ep),
};
@ -901,9 +861,9 @@ int Handler::feed_data(const Endpoint &ep, const Address &local_addr,
}
int Handler::on_read(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi, std::span<const uint8_t> data) {
if (auto rv = feed_data(ep, local_addr, sa, salen, pi, data); rv != 0) {
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data) {
if (auto rv = feed_data(ep, local_addr, remote_addr, pi, data); rv != 0) {
return rv;
}
@ -1030,17 +990,20 @@ int Handler::write_streams() {
ngtcp2_pkt_info pi;
size_t gso_size;
auto ts = util::timestamp();
auto txbuf = std::span{tx_.data.get(), NGTCP2_TX_BUFLEN};
auto txbuf = std::span{txbuf_};
auto buflen = util::clamp_buffer_size(conn_, txbuf.size(), config.gso_burst);
ngtcp2_path_storage_zero(&ps);
auto nwrite =
ngtcp2_conn_write_aggregate_pkt(conn_, &ps.path, &pi, txbuf.data(),
txbuf.size(), &gso_size, ::write_pkt, ts);
auto nwrite = ngtcp2_conn_write_aggregate_pkt2(
conn_, &ps.path, &pi, txbuf.data(), buflen, &gso_size, ::write_pkt,
config.gso_burst, ts);
if (nwrite < 0) {
return handle_error();
}
ngtcp2_conn_update_pkt_tx_time(conn_, ts);
if (nwrite == 0) {
return 0;
}
@ -1078,11 +1041,9 @@ void Handler::on_send_blocked(const ngtcp2_path &path, unsigned int ecn,
auto &p = tx_.blocked;
memcpy(&p.local_addr.su, path.local.addr, path.local.addrlen);
memcpy(&p.remote_addr.su, path.remote.addr, path.remote.addrlen);
p.local_addr.set(path.local.addr);
p.remote_addr.set(path.remote.addr);
p.local_addr.len = path.local.addrlen;
p.remote_addr.len = path.remote.addrlen;
p.endpoint = static_cast<Endpoint *>(path.user_data);
p.ecn = ecn;
p.data = data;
@ -1108,17 +1069,9 @@ int Handler::send_blocked_packet() {
auto &p = tx_.blocked;
ngtcp2_addr local_addr{
.addr = &p.local_addr.su.sa,
.addrlen = p.local_addr.len,
};
ngtcp2_addr remote_addr{
.addr = &p.remote_addr.su.sa,
.addrlen = p.remote_addr.len,
};
auto [rest, rv] = server_->send_packet(
*p.endpoint, no_gso_, local_addr, remote_addr, p.ecn, p.data, p.gso_size);
*p.endpoint, no_gso_, as_ngtcp2_addr(p.local_addr),
as_ngtcp2_addr(p.remote_addr), p.ecn, p.data, p.gso_size);
if (rv != 0) {
assert(NETWORK_ERR_SEND_BLOCKED == rv);
@ -1230,7 +1183,7 @@ int Handler::send_conn_close() {
}
int Handler::send_conn_close(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen,
const Address &remote_addr,
const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data) {
assert(conn_closebuf_ && conn_closebuf_->size());
@ -1244,21 +1197,8 @@ int Handler::send_conn_close(const Endpoint &ep, const Address &local_addr,
return 0;
}
auto path = ngtcp2_path{
.local =
{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
},
.remote =
{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
},
.user_data = const_cast<Endpoint *>(&ep),
};
auto rv = server_->send_packet(ep, path.local, path.remote,
auto rv = server_->send_packet(ep, as_ngtcp2_addr(local_addr),
as_ngtcp2_addr(remote_addr),
/* ecn = */ 0, conn_closebuf_->data());
if (rv != 0) {
return rv;
@ -1444,9 +1384,7 @@ void siginthandler(struct ev_loop *loop, ev_signal *watcher, int revents) {
} // namespace
Server::Server(struct ev_loop *loop, TLSServerContext &tls_ctx)
: loop_(loop),
tls_ctx_(tls_ctx),
stateless_reset_bucket_(NGTCP2_STATELESS_RESET_BURST) {
: loop_{loop}, tls_ctx_{tls_ctx} {
ev_signal_init(&sigintev_, siginthandler, SIGINT);
ev_timer_init(
@ -1513,7 +1451,7 @@ int create_sock(Address &local_addr, const char *addr, const char *port,
return -1;
}
auto res_d = defer(freeaddrinfo, res);
auto res_d = defer([res] { freeaddrinfo(res); });
int fd = -1;
@ -1565,14 +1503,15 @@ int create_sock(Address &local_addr, const char *addr, const char *port,
return -1;
}
socklen_t len = sizeof(local_addr.su.storage);
if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
sockaddr_storage ss;
socklen_t len = sizeof(ss);
if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) == -1) {
std::cerr << "getsockname: " << strerror(errno) << std::endl;
close(fd);
return -1;
}
local_addr.len = len;
local_addr.ifindex = 0;
local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
return fd;
}
@ -1593,6 +1532,7 @@ int add_endpoint(std::vector<Endpoint> &endpoints, const char *addr,
ep.addr = dest;
ep.fd = fd;
ev_io_init(&ep.rev, sreadcb, 0, EV_READ);
ev_set_priority(&ep.rev, EV_MAXPRI);
return 0;
}
@ -1600,14 +1540,16 @@ int add_endpoint(std::vector<Endpoint> &endpoints, const char *addr,
namespace {
int add_endpoint(std::vector<Endpoint> &endpoints, const Address &addr) {
auto fd = util::create_nonblock_socket(addr.su.sa.sa_family, SOCK_DGRAM, 0);
auto family = addr.family();
auto fd = util::create_nonblock_socket(family, SOCK_DGRAM, 0);
if (fd == -1) {
std::cerr << "socket: " << strerror(errno) << std::endl;
return -1;
}
int val = 1;
if (addr.su.sa.sa_family == AF_INET6) {
if (family == AF_INET6) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
static_cast<socklen_t>(sizeof(val))) == -1) {
std::cerr << "setsockopt: " << strerror(errno) << std::endl;
@ -1634,12 +1576,12 @@ int add_endpoint(std::vector<Endpoint> &endpoints, const Address &addr) {
return -1;
}
fd_set_recv_ecn(fd, addr.su.sa.sa_family);
fd_set_ip_mtu_discover(fd, addr.su.sa.sa_family);
fd_set_ip_dontfrag(fd, addr.su.sa.sa_family);
fd_set_recv_ecn(fd, family);
fd_set_ip_mtu_discover(fd, family);
fd_set_ip_dontfrag(fd, family);
fd_set_udp_gro(fd);
if (bind(fd, &addr.su.sa, addr.len) == -1) {
if (bind(fd, addr.as_sockaddr(), addr.size()) == -1) {
std::cerr << "bind: " << strerror(errno) << std::endl;
close(fd);
return -1;
@ -1650,6 +1592,7 @@ int add_endpoint(std::vector<Endpoint> &endpoints, const Address &addr) {
ep.addr = addr;
ep.fd = fd;
ev_io_init(&ep.rev, sreadcb, 0, EV_READ);
ev_set_priority(&ep.rev, EV_MAXPRI);
return 0;
}
@ -1671,11 +1614,11 @@ int Server::init(const char *addr, const char *port) {
return -1;
}
if (config.preferred_ipv4_addr.len &&
if (!config.preferred_ipv4_addr.empty() &&
add_endpoint(endpoints_, config.preferred_ipv4_addr) != 0) {
return -1;
}
if (config.preferred_ipv6_addr.len &&
if (!config.preferred_ipv6_addr.empty() &&
add_endpoint(endpoints_, config.preferred_ipv6_addr) != 0) {
return -1;
}
@ -1695,7 +1638,7 @@ int Server::init(const char *addr, const char *port) {
}
int Server::on_read(const Endpoint &ep) {
sockaddr_union su;
sockaddr_storage ss;
std::array<uint8_t, 64_k> buf;
size_t pktcnt = 0;
ngtcp2_pkt_info pi;
@ -1709,14 +1652,21 @@ int Server::on_read(const Endpoint &ep) {
CMSG_SPACE(sizeof(int))];
msghdr msg{
.msg_name = &su,
.msg_name = &ss,
.msg_iov = &msg_iov,
.msg_iovlen = 1,
.msg_control = msg_ctrl,
};
for (; pktcnt < 10;) {
msg.msg_namelen = sizeof(su);
auto start = util::timestamp();
for (; pktcnt < MAX_RECV_PKTS;) {
if (util::recv_pkt_time_threshold_exceeded(
config.cc_algo == NGTCP2_CC_ALGO_BBR, start, pktcnt)) {
return 0;
}
msg.msg_namelen = sizeof(ss);
msg.msg_controllen = sizeof(msg_ctrl);
auto nread = recvmsg(ep.fd, &msg, 0);
@ -1734,14 +1684,17 @@ int Server::on_read(const Endpoint &ep) {
continue;
}
if (util::prohibited_port(util::port(&su))) {
Address remote_addr;
remote_addr.set(reinterpret_cast<const sockaddr *>(&ss));
if (util::prohibited_port(remote_addr.port())) {
++pktcnt;
continue;
}
pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
auto local_addr = msghdr_get_local_addr(&msg, su.storage.ss_family);
pi.ecn = msghdr_get_ecn(&msg, ss.ss_family);
auto local_addr = msghdr_get_local_addr(&msg, ss.ss_family);
if (!local_addr) {
++pktcnt;
std::cerr << "Unable to obtain local address" << std::endl;
@ -1753,7 +1706,7 @@ int Server::on_read(const Endpoint &ep) {
gso_size = static_cast<size_t>(nread);
}
set_port(*local_addr, ep.addr);
local_addr->port(ep.addr.port());
auto data = std::span{buf.data(), static_cast<size_t>(nread)};
@ -1764,10 +1717,8 @@ int Server::on_read(const Endpoint &ep) {
if (!config.quiet) {
std::array<char, IF_NAMESIZE> ifname;
std::cerr << "Received packet: local="
<< util::straddr(&local_addr->su.sa, local_addr->len)
<< " remote=" << util::straddr(&su.sa, msg.msg_namelen)
<< " if="
std::cerr << "Received packet: local=" << util::straddr(*local_addr)
<< " remote=" << util::straddr(remote_addr) << " if="
<< if_indextoname(local_addr->ifindex, ifname.data())
<< " ecn=0x" << std::hex << static_cast<uint32_t>(pi.ecn)
<< std::dec << " " << datalen << " bytes" << std::endl;
@ -1783,8 +1734,7 @@ int Server::on_read(const Endpoint &ep) {
std::cerr << "** Simulated incoming packet loss **" << std::endl;
}
} else {
read_pkt(ep, *local_addr, &su.sa, msg.msg_namelen, &pi,
{data.data(), datalen});
read_pkt(ep, *local_addr, remote_addr, &pi, {data.data(), datalen});
}
data = data.subspan(datalen);
@ -1795,8 +1745,7 @@ int Server::on_read(const Endpoint &ep) {
}
void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data) {
ngtcp2_version_cid vc;
@ -1807,7 +1756,8 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
break;
case NGTCP2_ERR_VERSION_NEGOTIATION:
send_version_negotiation(vc.version, {vc.scid, vc.scidlen},
{vc.dcid, vc.dcidlen}, ep, local_addr, sa, salen);
{vc.dcid, vc.dcidlen}, ep, local_addr,
remote_addr);
return;
default:
std::cerr << "Could not decode version and CID from QUIC packet header: "
@ -1829,7 +1779,7 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
if (!(data[0] & 0x80) && data.size() >= NGTCP2_SV_SCIDLEN + 21) {
send_stateless_reset(data.size(), {vc.dcid, vc.dcidlen}, ep, local_addr,
sa, salen);
remote_addr);
}
return;
@ -1844,25 +1794,25 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
if (config.validate_addr || hd.tokenlen) {
std::cerr << "Perform stateless address validation" << std::endl;
if (hd.tokenlen == 0) {
send_retry(&hd, ep, local_addr, sa, salen, data.size() * 3);
send_retry(&hd, ep, local_addr, remote_addr, data.size() * 3);
return;
}
if (hd.token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY2 &&
hd.dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
send_stateless_connection_close(&hd, ep, local_addr, sa, salen);
send_stateless_connection_close(&hd, ep, local_addr, remote_addr);
return;
}
switch (hd.token[0]) {
case NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY2:
switch (verify_retry_token(&ocid, &hd, sa, salen)) {
switch (verify_retry_token(&ocid, &hd, remote_addr)) {
case 0:
pocid = &ocid;
token_type = NGTCP2_TOKEN_TYPE_RETRY;
break;
case -1:
send_stateless_connection_close(&hd, ep, local_addr, sa, salen);
send_stateless_connection_close(&hd, ep, local_addr, remote_addr);
return;
case 1:
hd.token = nullptr;
@ -1872,9 +1822,9 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
break;
case NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR:
if (verify_token(&hd, sa, salen) != 0) {
if (verify_token(&hd, remote_addr) != 0) {
if (config.validate_addr) {
send_retry(&hd, ep, local_addr, sa, salen, data.size() * 3);
send_retry(&hd, ep, local_addr, remote_addr, data.size() * 3);
return;
}
@ -1889,7 +1839,7 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
std::cerr << "Ignore unrecognized token" << std::endl;
}
if (config.validate_addr) {
send_retry(&hd, ep, local_addr, sa, salen, data.size() * 3);
send_retry(&hd, ep, local_addr, remote_addr, data.size() * 3);
return;
}
@ -1900,17 +1850,17 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
}
auto h = std::make_unique<Handler>(loop_, this);
if (h->init(ep, local_addr, sa, salen, &hd.scid, &hd.dcid, pocid,
if (h->init(ep, local_addr, remote_addr, &hd.scid, &hd.dcid, pocid,
{hd.token, hd.tokenlen}, token_type, hd.version,
tls_ctx_) != 0) {
return;
}
switch (h->on_read(ep, local_addr, sa, salen, pi, data)) {
switch (h->on_read(ep, local_addr, remote_addr, pi, data)) {
case 0:
break;
case NETWORK_ERR_RETRY:
send_retry(&hd, ep, local_addr, sa, salen, data.size() * 3);
send_retry(&hd, ep, local_addr, remote_addr, data.size() * 3);
return;
default:
return;
@ -1941,7 +1891,7 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
auto h = (*handler_it).second;
auto conn = h->conn();
if (ngtcp2_conn_in_closing_period(conn)) {
if (h->send_conn_close(ep, local_addr, sa, salen, pi, data) != 0) {
if (h->send_conn_close(ep, local_addr, remote_addr, pi, data) != 0) {
remove(h);
}
return;
@ -1950,7 +1900,7 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
return;
}
if (auto rv = h->on_read(ep, local_addr, sa, salen, pi, data); rv != 0) {
if (auto rv = h->on_read(ep, local_addr, remote_addr, pi, data); rv != 0) {
if (rv != NETWORK_ERR_CLOSE_WAIT) {
remove(h);
}
@ -1961,11 +1911,10 @@ void Server::read_pkt(const Endpoint &ep, const Address &local_addr,
}
namespace {
uint32_t generate_reserved_version(const sockaddr *sa, socklen_t salen,
uint32_t version) {
uint32_t generate_reserved_version(const Address &addr, uint32_t version) {
uint32_t h = 0x811C9DC5u;
const uint8_t *p = (const uint8_t *)sa;
const uint8_t *ep = p + salen;
const uint8_t *p = reinterpret_cast<const uint8_t *>(addr.as_sockaddr());
const uint8_t *ep = p + addr.size();
for (; p != ep; ++p) {
h ^= *p;
h *= 0x01000193u;
@ -1988,13 +1937,13 @@ int Server::send_version_negotiation(uint32_t version,
std::span<const uint8_t> scid,
const Endpoint &ep,
const Address &local_addr,
const sockaddr *sa, socklen_t salen) {
const Address &remote_addr) {
Buffer buf{NGTCP2_MAX_UDP_PAYLOAD_SIZE};
std::array<uint32_t, 1 + max_preferred_versionslen> sv;
auto p = std::ranges::begin(sv);
*p++ = generate_reserved_version(sa, salen, version);
*p++ = generate_reserved_version(remote_addr, version);
if (config.preferred_versions.empty()) {
*p++ = NGTCP2_PROTO_VER_V1;
@ -2016,17 +1965,8 @@ int Server::send_version_negotiation(uint32_t version,
buf.push(as_unsigned(nwrite));
ngtcp2_addr laddr{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
};
ngtcp2_addr raddr{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
};
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.data()) !=
NETWORK_ERR_OK) {
if (send_packet(ep, as_ngtcp2_addr(local_addr), as_ngtcp2_addr(remote_addr),
/* ecn = */ 0, buf.data()) != NETWORK_ERR_OK) {
return -1;
}
@ -2034,13 +1974,14 @@ int Server::send_version_negotiation(uint32_t version,
}
int Server::send_retry(const ngtcp2_pkt_hd *chd, const Endpoint &ep,
const Address &local_addr, const sockaddr *sa,
socklen_t salen, size_t max_pktlen) {
const Address &local_addr, const Address &remote_addr,
size_t max_pktlen) {
std::array<char, NI_MAXHOST> host;
std::array<char, NI_MAXSERV> port;
if (auto rv = getnameinfo(sa, salen, host.data(), host.size(), port.data(),
port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
if (auto rv = getnameinfo(remote_addr.as_sockaddr(), remote_addr.size(),
host.data(), host.size(), port.data(), port.size(),
NI_NUMERICHOST | NI_NUMERICSERV);
rv != 0) {
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
return -1;
@ -2064,7 +2005,8 @@ int Server::send_retry(const ngtcp2_pkt_hd *chd, const Endpoint &ep,
auto tokenlen = ngtcp2_crypto_generate_retry_token2(
token.data(), config.static_secret.data(), config.static_secret.size(),
chd->version, sa, salen, &scid, &chd->dcid, t);
chd->version, remote_addr.as_sockaddr(), remote_addr.size(), &scid,
&chd->dcid, t);
if (tokenlen < 0) {
return -1;
}
@ -2087,17 +2029,8 @@ int Server::send_retry(const ngtcp2_pkt_hd *chd, const Endpoint &ep,
buf.push(as_unsigned(nwrite));
ngtcp2_addr laddr{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
};
ngtcp2_addr raddr{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
};
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.data()) !=
NETWORK_ERR_OK) {
if (send_packet(ep, as_ngtcp2_addr(local_addr), as_ngtcp2_addr(remote_addr),
/* ecn = */ 0, buf.data()) != NETWORK_ERR_OK) {
return -1;
}
@ -2107,8 +2040,7 @@ int Server::send_retry(const ngtcp2_pkt_hd *chd, const Endpoint &ep,
int Server::send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
const Endpoint &ep,
const Address &local_addr,
const sockaddr *sa,
socklen_t salen) {
const Address &remote_addr) {
Buffer buf{NGTCP2_MAX_UDP_PAYLOAD_SIZE};
auto nwrite = ngtcp2_crypto_write_connection_close(
@ -2121,17 +2053,8 @@ int Server::send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
buf.push(as_unsigned(nwrite));
ngtcp2_addr laddr{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
};
ngtcp2_addr raddr{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
};
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.data()) !=
NETWORK_ERR_OK) {
if (send_packet(ep, as_ngtcp2_addr(local_addr), as_ngtcp2_addr(remote_addr),
/* ecn = */ 0, buf.data()) != NETWORK_ERR_OK) {
return -1;
}
@ -2140,7 +2063,7 @@ int Server::send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
int Server::send_stateless_reset(size_t pktlen, std::span<const uint8_t> dcid,
const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen) {
const Address &remote_addr) {
if (stateless_reset_bucket_ == 0) {
return 0;
}
@ -2196,17 +2119,8 @@ int Server::send_stateless_reset(size_t pktlen, std::span<const uint8_t> dcid,
buf.push(as_unsigned(nwrite));
ngtcp2_addr laddr{
.addr = const_cast<sockaddr *>(&local_addr.su.sa),
.addrlen = local_addr.len,
};
ngtcp2_addr raddr{
.addr = const_cast<sockaddr *>(sa),
.addrlen = salen,
};
if (send_packet(ep, laddr, raddr, /* ecn = */ 0, buf.data()) !=
NETWORK_ERR_OK) {
if (send_packet(ep, as_ngtcp2_addr(local_addr), as_ngtcp2_addr(remote_addr),
/* ecn = */ 0, buf.data()) != NETWORK_ERR_OK) {
return -1;
}
@ -2214,14 +2128,15 @@ int Server::send_stateless_reset(size_t pktlen, std::span<const uint8_t> dcid,
}
int Server::verify_retry_token(ngtcp2_cid *ocid, const ngtcp2_pkt_hd *hd,
const sockaddr *sa, socklen_t salen) {
const Address &remote_addr) {
int rv;
if (!config.quiet) {
std::array<char, NI_MAXHOST> host;
std::array<char, NI_MAXSERV> port;
if (auto rv = getnameinfo(sa, salen, host.data(), host.size(), port.data(),
if (auto rv = getnameinfo(remote_addr.as_sockaddr(), remote_addr.size(),
host.data(), host.size(), port.data(),
port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
rv != 0) {
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
@ -2237,8 +2152,8 @@ int Server::verify_retry_token(ngtcp2_cid *ocid, const ngtcp2_pkt_hd *hd,
rv = ngtcp2_crypto_verify_retry_token2(
ocid, hd->token, hd->tokenlen, config.static_secret.data(),
config.static_secret.size(), hd->version, sa, salen, &hd->dcid,
10 * NGTCP2_SECONDS, t);
config.static_secret.size(), hd->version, remote_addr.as_sockaddr(),
remote_addr.size(), &hd->dcid, 10 * NGTCP2_SECONDS, t);
switch (rv) {
case 0:
break;
@ -2260,13 +2175,13 @@ int Server::verify_retry_token(ngtcp2_cid *ocid, const ngtcp2_pkt_hd *hd,
return 0;
}
int Server::verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
socklen_t salen) {
int Server::verify_token(const ngtcp2_pkt_hd *hd, const Address &remote_addr) {
std::array<char, NI_MAXHOST> host;
std::array<char, NI_MAXSERV> port;
if (auto rv = getnameinfo(sa, salen, host.data(), host.size(), port.data(),
port.size(), NI_NUMERICHOST | NI_NUMERICSERV);
if (auto rv = getnameinfo(remote_addr.as_sockaddr(), remote_addr.size(),
host.data(), host.size(), port.data(), port.size(),
NI_NUMERICHOST | NI_NUMERICSERV);
rv != 0) {
std::cerr << "getnameinfo: " << gai_strerror(rv) << std::endl;
return -1;
@ -2280,10 +2195,10 @@ int Server::verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
auto t = util::system_clock_now();
if (ngtcp2_crypto_verify_regular_token(hd->token, hd->tokenlen,
config.static_secret.data(),
config.static_secret.size(), sa, salen,
3600 * NGTCP2_SECONDS, t) != 0) {
if (ngtcp2_crypto_verify_regular_token(
hd->token, hd->tokenlen, config.static_secret.data(),
config.static_secret.size(), remote_addr.as_sockaddr(),
remote_addr.size(), 3600 * NGTCP2_SECONDS, t) != 0) {
std::cerr << "Could not verify token" << std::endl;
return -1;
@ -2559,8 +2474,7 @@ int parse_host_port(Address &dest, int af, const std::string_view &host_port) {
return -1;
}
dest.len = res->ai_addrlen;
memcpy(&dest.su, res->ai_addr, res->ai_addrlen);
dest.set(res->ai_addr);
freeaddrinfo(res);
@ -2581,43 +2495,11 @@ void print_usage() {
}
} // namespace
namespace {
void config_set_default(Config &config) {
auto path = realpath(".", nullptr);
assert(path);
auto htdocs = std::string(path);
free(path);
config = Config{
.tx_loss_prob = 0.,
.rx_loss_prob = 0.,
.ciphers = util::crypto_default_ciphers(),
.groups = util::crypto_default_groups(),
.htdocs = std::move(htdocs),
.mime_types_file = "/etc/mime.types"sv,
.timeout = 30 * NGTCP2_SECONDS,
.max_data = 1_m,
.max_stream_data_bidi_remote = 256_k,
.max_stream_data_uni = 256_k,
.max_streams_bidi = 100,
.max_streams_uni = 3,
.max_window = 6_m,
.max_stream_window = 6_m,
.max_dyn_length = 20_m,
.cc_algo = NGTCP2_CC_ALGO_CUBIC,
.initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT,
.handshake_timeout = UINT64_MAX,
.ack_thresh = 2,
.initial_pkt_num = UINT32_MAX,
};
}
} // namespace
namespace {
void print_help() {
print_usage();
config_set_default(config);
Config config;
std::cout << R"(
<ADDR> Address to listen to. '*' binds to any address.
@ -2776,12 +2658,21 @@ Options:
Specify UDP datagram payload sizes to probe in Path MTU
Discovery. <SIZE> must be strictly larger than 1200.
--ech-config-file=<PATH>
Read private key and ECHConfig from |PATH|. The file
denoted by |PATH| must contain private key and ECHConfig
Read private key and ECHConfig from <PATH>. The file
denoted by <PATH> must contain private key and ECHConfig
as described in
https://datatracker.ietf.org/doc/html/draft-farrell-tls-pemesni.
ECH configuration is only applied if an underlying TLS
stack supports it.
--no-gso Disables GSO.
--show-stat Print the connection statistics when the connection is
closed.
--gso-burst=<N>
The maximum number of packets to aggregate for GSO. If
GSO is disabled, this is the maximum number of packets
to send per an event loop in a single connection. It
defaults to 0, which means it is not limited by the
configuration.
-h, --help Display this help and exit.
---
@ -2804,8 +2695,6 @@ Options:
std::ofstream keylog_file;
int main(int argc, char **argv) {
config_set_default(config);
if (argc) {
prog = basename(argv[0]);
}
@ -2854,6 +2743,9 @@ int main(int argc, char **argv) {
{"initial-pkt-num", required_argument, &flag, 31},
{"pmtud-probes", required_argument, &flag, 32},
{"ech-config-file", required_argument, &flag, 33},
{"no-gso", no_argument, &flag, 35},
{"show-stat", no_argument, &flag, 36},
{"gso-burst", required_argument, &flag, 37},
{},
};
@ -3060,9 +2952,9 @@ int main(int argc, char **argv) {
if (auto n = util::parse_uint_iec(optarg); !n) {
std::cerr << "max-udp-payload-size: invalid argument" << std::endl;
exit(EXIT_FAILURE);
} else if (*n > 64_k) {
std::cerr << "max-udp-payload-size: must not exceed 65536"
<< std::endl;
} else if (*n > NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE) {
std::cerr << "max-udp-payload-size: must not exceed "
<< NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE << std::endl;
exit(EXIT_FAILURE);
} else {
config.max_udp_payload_size = *n;
@ -3192,10 +3084,12 @@ int main(int argc, char **argv) {
if (auto n = util::parse_uint_iec(s); !n) {
std::cerr << "pmtud-probes: invalid argument" << std::endl;
exit(EXIT_FAILURE);
} else if (*n <= 1200 || *n >= 64_k) {
std::cerr
<< "pmtud-probes: must be in range [1201, 65535], inclusive."
<< std::endl;
} else if (*n <= NGTCP2_MAX_UDP_PAYLOAD_SIZE ||
*n > NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE) {
std::cerr << "pmtud-probes: must be in range ["
<< NGTCP2_MAX_UDP_PAYLOAD_SIZE + 1 << ", "
<< NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE << "], inclusive."
<< std::endl;
exit(EXIT_FAILURE);
} else {
config.pmtud_probes.push_back(static_cast<uint16_t>(*n));
@ -3207,11 +3101,37 @@ int main(int argc, char **argv) {
// --ech-config-file
ech_config_file = optarg;
break;
case 35:
// --no-gso
config.no_gso = true;
break;
case 36:
// --show-stat
config.show_stat = true;
break;
case 37: {
// --gso-burst
auto n = util::parse_uint(optarg);
if (!n) {
std::cerr << "gso-burst: invalid argument" << std::endl;
exit(EXIT_FAILURE);
}
if (*n > 64) {
std::cerr << "gso-burst: must be in range [0, 64], inclusive."
<< std::endl;
exit(EXIT_FAILURE);
}
config.gso_burst = static_cast<size_t>(*n);
break;
}
}
break;
default:
break;
};
}
}
if (argc - optind < 4) {
@ -3265,7 +3185,7 @@ int main(int argc, char **argv) {
std::cerr << "Using document root " << config.htdocs << std::endl;
auto ev_loop_d = defer(ev_loop_destroy, EV_DEFAULT);
auto ev_loop_d = defer([] { ev_loop_destroy(EV_DEFAULT); });
auto keylog_filename = getenv("SSLKEYLOGFILE");
if (keylog_filename) {

View File

@ -52,14 +52,6 @@
using namespace ngtcp2;
struct HTTPHeader {
HTTPHeader(const std::string_view &name, const std::string_view &value)
: name(name), value(value) {}
std::string_view name;
std::string_view value;
};
class Handler;
struct FileEntry;
@ -79,7 +71,7 @@ struct Stream {
nghttp3_buf respbuf;
http_parser htp;
// eos gets true when one HTTP request message is seen.
bool eos;
bool eos{};
};
struct StreamIDLess {
@ -94,8 +86,8 @@ class Server;
struct Endpoint {
Address addr;
ev_io rev;
Server *server;
int fd;
Server *server{};
int fd{};
};
class Handler : public HandlerBase {
@ -103,19 +95,19 @@ public:
Handler(struct ev_loop *loop, Server *server);
~Handler();
int init(const Endpoint &ep, const Address &local_addr, const sockaddr *sa,
socklen_t salen, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
const ngtcp2_cid *ocid, std::span<const uint8_t> token,
ngtcp2_token_type token_type, uint32_t version,
TLSServerContext &tls_ctx);
int init(const Endpoint &ep, const Address &local_addr,
const Address &remote_addr, const ngtcp2_cid *dcid,
const ngtcp2_cid *scid, const ngtcp2_cid *ocid,
std::span<const uint8_t> token, ngtcp2_token_type token_type,
uint32_t version, TLSServerContext &tls_ctx);
int on_read(const Endpoint &ep, const Address &local_addr, const sockaddr *sa,
socklen_t salen, const ngtcp2_pkt_info *pi,
int on_read(const Endpoint &ep, const Address &local_addr,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
int on_write();
int write_streams();
int feed_data(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen, const ngtcp2_pkt_info *pi,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
void update_timer();
int handle_expiry();
@ -135,8 +127,8 @@ public:
int handle_error();
int send_conn_close();
int send_conn_close(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi, std::span<const uint8_t> data);
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
int update_key(uint8_t *rx_secret, uint8_t *tx_secret,
ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
@ -166,8 +158,8 @@ private:
Server *server_;
ev_io wev_;
ev_timer timer_;
FILE *qlog_;
ngtcp2_cid scid_;
FILE *qlog_{};
ngtcp2_cid scid_{};
std::unordered_map<int64_t, std::unique_ptr<Stream>> streams_;
std::set<Stream *, StreamIDLess> sendq_;
// conn_closebuf_ contains a packet which contains CONNECTION_CLOSE.
@ -175,14 +167,14 @@ private:
// packet in draining period.
std::unique_ptr<Buffer> conn_closebuf_;
// nkey_update_ is the number of key update occurred.
size_t nkey_update_;
size_t nkey_update_{};
bool no_gso_;
struct {
size_t bytes_recv;
size_t bytes_sent;
size_t num_pkts_recv;
size_t next_pkts_recv;
} close_wait_;
size_t next_pkts_recv = 1;
} close_wait_{};
struct {
bool send_blocked;
@ -195,8 +187,8 @@ private:
std::span<const uint8_t> data;
size_t gso_size;
} blocked;
std::unique_ptr<uint8_t[]> data;
} tx_;
} tx_{};
std::array<uint8_t, 64_k> txbuf_;
};
class Server {
@ -210,26 +202,25 @@ public:
int on_read(const Endpoint &ep);
void read_pkt(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen, const ngtcp2_pkt_info *pi,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
int send_version_negotiation(uint32_t version, std::span<const uint8_t> dcid,
std::span<const uint8_t> scid,
const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen);
const Address &remote_addr);
int send_retry(const ngtcp2_pkt_hd *chd, const Endpoint &ep,
const Address &local_addr, const sockaddr *sa, socklen_t salen,
const Address &local_addr, const Address &remote_addr,
size_t max_pktlen);
int send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
const Endpoint &ep,
const Address &local_addr,
const sockaddr *sa, socklen_t salen);
const Address &remote_addr);
int send_stateless_reset(size_t pktlen, std::span<const uint8_t> dcid,
const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen);
const Address &remote_addr);
int verify_retry_token(ngtcp2_cid *ocid, const ngtcp2_pkt_hd *hd,
const sockaddr *sa, socklen_t salen);
int verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
socklen_t salen);
const Address &remote_addr);
int verify_token(const ngtcp2_pkt_hd *hd, const Address &remote_addr);
int send_packet(const Endpoint &ep, const ngtcp2_addr &local_addr,
const ngtcp2_addr &remote_addr, unsigned int ecn,
std::span<const uint8_t> data);
@ -251,7 +242,7 @@ private:
TLSServerContext &tls_ctx_;
ev_signal sigintev_;
ev_timer stateless_reset_regen_timer_;
size_t stateless_reset_bucket_;
size_t stateless_reset_bucket_{NGTCP2_STATELESS_RESET_BURST};
};
#endif // !defined(H09SERVER_H)

View File

@ -43,6 +43,7 @@
#endif // defined(HAVE_ARPA_INET_H)
#include <array>
#include <variant>
#include <ngtcp2/ngtcp2.h>
@ -50,29 +51,40 @@ namespace ngtcp2 {
enum network_error {
NETWORK_ERR_OK = 0,
NETWORK_ERR_FATAL = -10,
NETWORK_ERR_SEND_BLOCKED = -11,
NETWORK_ERR_CLOSE_WAIT = -12,
NETWORK_ERR_RETRY = -13,
NETWORK_ERR_DROP_CONN = -14,
};
union in_addr_union {
in_addr in;
in6_addr in6;
};
using InAddr = std::variant<std::monostate, in_addr, in6_addr>;
union sockaddr_union {
sockaddr_storage storage;
sockaddr sa;
sockaddr_in6 in6;
sockaddr_in in;
};
using Sockaddr = std::variant<std::monostate, sockaddr_in, sockaddr_in6>;
struct Address {
socklen_t len;
union sockaddr_union su;
uint32_t ifindex;
// as_sockaddr returns the pointer to the stored address casted to
// const sockaddr *.
[[nodiscard]] const sockaddr *as_sockaddr() const;
[[nodiscard]] sockaddr *as_sockaddr();
// family returns the address family.
[[nodiscard]] int family() const;
// port returns the port.
[[nodiscard]] uint16_t port() const;
// port sets |port| to this address.
void port(uint16_t port);
// set stores |sa| to this address. The address family is
// determined by |sa|->sa_family, and |sa| must point to the memory
// that contains valid object which is either sockaddr_in or
// sockaddr_in6.
void set(const sockaddr *sa);
// size returns the size of the stored address.
[[nodiscard]] socklen_t size() const;
// empty returns true if this address does not contain any
// meaningful address.
[[nodiscard]] bool empty() const;
Sockaddr skaddr;
uint32_t ifindex{};
};
} // namespace ngtcp2

File diff suppressed because it is too large Load Diff

View File

@ -51,14 +51,6 @@
using namespace ngtcp2;
struct HTTPHeader {
HTTPHeader(const std::string_view &name, const std::string_view &value)
: name(name), value(value) {}
std::string_view name;
std::string_view value;
};
class Handler;
struct FileEntry;
@ -83,15 +75,15 @@ struct Stream {
std::string authority;
std::string status_resp_body;
// data is a pointer to the memory which maps file denoted by fd.
uint8_t *data;
uint8_t *data{};
// datalen is the length of mapped file by data.
uint64_t datalen;
uint64_t datalen{};
// dynresp is true if dynamic data response is enabled.
bool dynresp;
bool dynresp{};
// dyndataleft is the number of dynamic data left to send.
uint64_t dyndataleft;
uint64_t dyndataleft{};
// dynbuflen is the number of bytes in-flight.
uint64_t dynbuflen;
uint64_t dynbuflen{};
};
class Server;
@ -100,8 +92,8 @@ class Server;
struct Endpoint {
Address addr;
ev_io rev;
Server *server;
int fd;
Server *server{};
int fd{};
};
class Handler : public HandlerBase {
@ -109,19 +101,19 @@ public:
Handler(struct ev_loop *loop, Server *server);
~Handler();
int init(const Endpoint &ep, const Address &local_addr, const sockaddr *sa,
socklen_t salen, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
const ngtcp2_cid *ocid, std::span<const uint8_t> token,
ngtcp2_token_type token_type, uint32_t version,
TLSServerContext &tls_ctx);
int init(const Endpoint &ep, const Address &local_addr,
const Address &remote_addr, const ngtcp2_cid *dcid,
const ngtcp2_cid *scid, const ngtcp2_cid *ocid,
std::span<const uint8_t> token, ngtcp2_token_type token_type,
uint32_t version, TLSServerContext &tls_ctx);
int on_read(const Endpoint &ep, const Address &local_addr, const sockaddr *sa,
socklen_t salen, const ngtcp2_pkt_info *pi,
int on_read(const Endpoint &ep, const Address &local_addr,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
int on_write();
int write_streams();
int feed_data(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen, const ngtcp2_pkt_info *pi,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
void update_timer();
int handle_expiry();
@ -140,8 +132,8 @@ public:
int handle_error();
int send_conn_close();
int send_conn_close(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen,
const ngtcp2_pkt_info *pi, std::span<const uint8_t> data);
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
int update_key(uint8_t *rx_secret, uint8_t *tx_secret,
ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
@ -185,23 +177,23 @@ private:
Server *server_;
ev_io wev_;
ev_timer timer_;
FILE *qlog_;
ngtcp2_cid scid_;
nghttp3_conn *httpconn_;
FILE *qlog_{};
ngtcp2_cid scid_{};
nghttp3_conn *httpconn_{};
std::unordered_map<int64_t, std::unique_ptr<Stream>> streams_;
// conn_closebuf_ contains a packet which contains CONNECTION_CLOSE.
// This packet is repeatedly sent as a response to the incoming
// packet in draining period.
std::unique_ptr<Buffer> conn_closebuf_;
// nkey_update_ is the number of key update occurred.
size_t nkey_update_;
size_t nkey_update_{};
bool no_gso_;
struct {
size_t bytes_recv;
size_t bytes_sent;
size_t num_pkts_recv;
size_t next_pkts_recv;
} close_wait_;
size_t next_pkts_recv = 1;
} close_wait_{};
struct {
bool send_blocked;
@ -214,8 +206,8 @@ private:
std::span<const uint8_t> data;
size_t gso_size;
} blocked;
std::unique_ptr<uint8_t[]> data;
} tx_;
} tx_{};
std::array<uint8_t, 64_k> txbuf_;
};
class Server {
@ -229,26 +221,25 @@ public:
int on_read(const Endpoint &ep);
void read_pkt(const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen, const ngtcp2_pkt_info *pi,
const Address &remote_addr, const ngtcp2_pkt_info *pi,
std::span<const uint8_t> data);
int send_version_negotiation(uint32_t version, std::span<const uint8_t> dcid,
std::span<const uint8_t> scid,
const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen);
const Address &remote_addr);
int send_retry(const ngtcp2_pkt_hd *chd, const Endpoint &ep,
const Address &local_addr, const sockaddr *sa, socklen_t salen,
const Address &local_addr, const Address &remote_addr,
size_t max_pktlen);
int send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
const Endpoint &ep,
const Address &local_addr,
const sockaddr *sa, socklen_t salen);
const Address &remote_addr);
int send_stateless_reset(size_t pktlen, std::span<const uint8_t> dcid,
const Endpoint &ep, const Address &local_addr,
const sockaddr *sa, socklen_t salen);
const Address &remote_addr);
int verify_retry_token(ngtcp2_cid *ocid, const ngtcp2_pkt_hd *hd,
const sockaddr *sa, socklen_t salen);
int verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
socklen_t salen);
const Address &remote_addr);
int verify_token(const ngtcp2_pkt_hd *hd, const Address &remote_addr);
int send_packet(const Endpoint &ep, const ngtcp2_addr &local_addr,
const ngtcp2_addr &remote_addr, unsigned int ecn,
std::span<const uint8_t> data);
@ -270,7 +261,7 @@ private:
TLSServerContext &tls_ctx_;
ev_signal sigintev_;
ev_timer stateless_reset_regen_timer_;
size_t stateless_reset_bucket_;
size_t stateless_reset_bucket_{NGTCP2_STATELESS_RESET_BURST};
};
#endif // !defined(SERVER_H)

View File

@ -35,20 +35,24 @@ using namespace ngtcp2;
extern Config config;
Buffer::Buffer(const uint8_t *data, size_t datalen)
: buf{data, data + datalen}, begin(buf.data()), tail(begin + datalen) {}
Buffer::Buffer(size_t datalen) : buf(datalen), begin(buf.data()), tail(begin) {}
: buf{data, data + datalen}, begin{buf.data()}, tail{begin + datalen} {}
Buffer::Buffer(size_t datalen) : buf(datalen), begin{buf.data()}, tail{begin} {}
static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) {
auto h = static_cast<HandlerBase *>(conn_ref->user_data);
return h->conn();
}
HandlerBase::HandlerBase() : conn_ref_{get_conn, this}, conn_(nullptr) {
HandlerBase::HandlerBase() : conn_ref_{get_conn, this} {
ngtcp2_ccerr_default(&last_error_);
}
HandlerBase::~HandlerBase() {
if (conn_) {
if (config.show_stat) {
debug::print_conn_info(conn_);
}
ngtcp2_conn_del(conn_);
}
}

View File

@ -51,93 +51,93 @@ struct Config {
Address preferred_ipv4_addr;
Address preferred_ipv6_addr;
// tx_loss_prob is probability of losing outgoing packet.
double tx_loss_prob;
double tx_loss_prob{};
// rx_loss_prob is probability of losing incoming packet.
double rx_loss_prob;
double rx_loss_prob{};
// ciphers is the list of enabled ciphers.
const char *ciphers;
const char *ciphers{util::crypto_default_ciphers()};
// groups is the list of supported groups.
const char *groups;
const char *groups{util::crypto_default_groups()};
// htdocs is a root directory to serve documents.
std::string htdocs;
std::string htdocs{util::realpath(".")};
// mime_types_file is a path to "MIME media types and the
// extensions" file. Ubuntu mime-support package includes it in
// /etc/mime/types.
std::string_view mime_types_file;
std::string_view mime_types_file{"/etc/mime.types"sv};
// mime_types maps file extension to MIME media type.
std::unordered_map<std::string, std::string> mime_types;
// port is the port number which server listens on for incoming
// connections.
uint16_t port;
uint16_t port{};
// quiet suppresses the output normally shown except for the error
// messages.
bool quiet;
bool quiet{};
// timeout is an idle timeout for QUIC connection.
ngtcp2_duration timeout;
ngtcp2_duration timeout{30 * NGTCP2_SECONDS};
// show_secret is true if transport secrets should be printed out.
bool show_secret;
bool show_secret{};
// validate_addr is true if server requires address validation.
bool validate_addr;
bool validate_addr{};
// early_response is true if server starts sending response when it
// receives HTTP header fields without waiting for request body. If
// HTTP response data is written before receiving request body,
// STOP_SENDING is sent.
bool early_response;
bool early_response{};
// verify_client is true if server verifies client with X.509
// certificate based authentication.
bool verify_client;
bool verify_client{};
// qlog_dir is the path to directory where qlog is stored.
std::string_view qlog_dir;
// no_quic_dump is true if hexdump of QUIC STREAM and CRYPTO data
// should be disabled.
bool no_quic_dump;
bool no_quic_dump{};
// no_http_dump is true if hexdump of HTTP response body should be
// disabled.
bool no_http_dump;
bool no_http_dump{};
// max_data is the initial connection-level flow control window.
uint64_t max_data;
uint64_t max_data{1_m};
// max_stream_data_bidi_local is the initial stream-level flow
// control window for a bidirectional stream that the local endpoint
// initiates.
uint64_t max_stream_data_bidi_local;
uint64_t max_stream_data_bidi_local{};
// max_stream_data_bidi_remote is the initial stream-level flow
// control window for a bidirectional stream that the remote
// endpoint initiates.
uint64_t max_stream_data_bidi_remote;
uint64_t max_stream_data_bidi_remote{256_k};
// max_stream_data_uni is the initial stream-level flow control
// window for a unidirectional stream.
uint64_t max_stream_data_uni;
uint64_t max_stream_data_uni{256_k};
// max_streams_bidi is the number of the concurrent bidirectional
// streams.
uint64_t max_streams_bidi;
uint64_t max_streams_bidi{100};
// max_streams_uni is the number of the concurrent unidirectional
// streams.
uint64_t max_streams_uni;
uint64_t max_streams_uni{3};
// max_window is the maximum connection-level flow control window
// size if auto-tuning is enabled.
uint64_t max_window;
uint64_t max_window{6_m};
// max_stream_window is the maximum stream-level flow control window
// size if auto-tuning is enabled.
uint64_t max_stream_window;
uint64_t max_stream_window{6_m};
// max_dyn_length is the maximum length of dynamically generated
// response.
uint64_t max_dyn_length;
uint64_t max_dyn_length{20_m};
// static_secret is used to derive keying materials for Retry and
// Stateless Retry token.
std::array<uint8_t, 32> static_secret;
// cc_algo is the congestion controller algorithm.
ngtcp2_cc_algo cc_algo;
ngtcp2_cc_algo cc_algo{NGTCP2_CC_ALGO_CUBIC};
// initial_rtt is an initial RTT.
ngtcp2_duration initial_rtt;
ngtcp2_duration initial_rtt{NGTCP2_DEFAULT_INITIAL_RTT};
// max_udp_payload_size is the maximum UDP payload size that server
// transmits.
size_t max_udp_payload_size;
size_t max_udp_payload_size{};
// send_trailers controls whether server sends trailer fields or
// not.
bool send_trailers;
bool send_trailers{};
// handshake_timeout is the period of time before giving up QUIC
// connection establishment.
ngtcp2_duration handshake_timeout;
ngtcp2_duration handshake_timeout{UINT64_MAX};
// preferred_versions includes QUIC versions in the order of
// preference. Server negotiates one of those versions if a client
// initially selects a less preferred version.
@ -147,21 +147,39 @@ struct Config {
// transport_parameter.
std::vector<uint32_t> available_versions;
// no_pmtud disables Path MTU Discovery.
bool no_pmtud;
bool no_pmtud{};
// ack_thresh is the minimum number of the received ACK eliciting
// packets that triggers immediate acknowledgement.
size_t ack_thresh;
size_t ack_thresh{2};
// initial_pkt_num is the initial packet number for each packet
// number space. If it is set to UINT32_MAX, it is chosen randomly.
uint32_t initial_pkt_num;
uint32_t initial_pkt_num{UINT32_MAX};
// pmtud_probes is the array of UDP datagram payload size to probes.
std::vector<uint16_t> pmtud_probes;
// ech_config contains server-side ECH configuration.
util::ECHServerConfig ech_config;
util::ECHServerConfig ech_config{};
// origin_list contains a payload of ORIGIN frame.
std::optional<std::vector<uint8_t>> origin_list;
// no_gso disables GSO.
bool no_gso{};
// show_stat, if true, displays the connection statistics when the
// connection is closed.
bool show_stat{};
// gso_burst is the number of packets to aggregate in GSO. 0 means
// it is not limited by the configuration.
size_t gso_burst{};
};
struct HTTPHeader {
HTTPHeader(const std::string_view &name, const std::string_view &value)
: name{name}, value{value} {}
std::string_view name;
std::string_view value;
};
inline constexpr size_t NGTCP2_STATELESS_RESET_BURST = 100;
struct Buffer {
Buffer(const uint8_t *data, size_t datalen);
explicit Buffer(size_t datalen);
@ -197,7 +215,7 @@ public:
protected:
ngtcp2_crypto_conn_ref conn_ref_;
TLSServerSession tls_session_;
ngtcp2_conn *conn_;
ngtcp2_conn *conn_{};
ngtcp2_ccerr last_error_;
};

View File

@ -170,10 +170,9 @@ std::optional<Address> msghdr_get_local_addr(msghdr *msg, int family) {
in_pktinfo pktinfo;
memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
Address res{
.len = sizeof(res.su.in),
.ifindex = static_cast<uint32_t>(pktinfo.ipi_ifindex),
};
auto &sa = res.su.in;
auto &sa = res.skaddr.emplace<sockaddr_in>();
sa.sin_family = AF_INET;
sa.sin_addr = pktinfo.ipi_addr;
return res;
@ -186,10 +185,9 @@ std::optional<Address> msghdr_get_local_addr(msghdr *msg, int family) {
in6_pktinfo pktinfo;
memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
Address res{
.len = sizeof(res.su.in6),
.ifindex = static_cast<uint32_t>(pktinfo.ipi6_ifindex),
};
auto &sa = res.su.in6;
auto &sa = res.skaddr.emplace<sockaddr_in6>();
sa.sin6_family = AF_INET6;
sa.sin6_addr = pktinfo.ipi6_addr;
return res;
@ -216,64 +214,52 @@ size_t msghdr_get_udp_gro(msghdr *msg) {
return static_cast<size_t>(gso_size);
}
void set_port(Address &dst, const Address &src) {
switch (dst.su.storage.ss_family) {
case AF_INET:
assert(AF_INET == src.su.storage.ss_family);
dst.su.in.sin_port = src.su.in.sin_port;
return;
case AF_INET6:
assert(AF_INET6 == src.su.storage.ss_family);
dst.su.in6.sin6_port = src.su.in6.sin6_port;
return;
default:
assert(0);
}
}
#ifdef HAVE_LINUX_RTNETLINK_H
struct nlmsg {
nlmsghdr hdr;
rtmsg msg;
rtattr dst;
in_addr_union dst_addr;
uint8_t dst_addr[sizeof(sockaddr_storage)];
};
namespace {
int send_netlink_msg(int fd, const Address &remote_addr, uint32_t seq) {
nlmsg nlmsg{
.hdr =
{
.nlmsg_type = RTM_GETROUTE,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
.nlmsg_seq = seq,
},
.msg =
{
.rtm_family = static_cast<unsigned char>(remote_addr.su.sa.sa_family),
.rtm_protocol = RTPROT_KERNEL,
},
.dst =
{
.rta_type = RTA_DST,
},
.hdr{
.nlmsg_type = RTM_GETROUTE,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
.nlmsg_seq = seq,
},
.msg{
.rtm_family = static_cast<unsigned char>(remote_addr.family()),
.rtm_protocol = RTPROT_KERNEL,
},
.dst{
.rta_type = RTA_DST,
},
};
switch (remote_addr.su.sa.sa_family) {
case AF_INET:
nlmsg.dst.rta_len = RTA_LENGTH(sizeof(remote_addr.su.in.sin_addr));
memcpy(RTA_DATA(&nlmsg.dst), &remote_addr.su.in.sin_addr,
sizeof(remote_addr.su.in.sin_addr));
break;
case AF_INET6:
nlmsg.dst.rta_len = RTA_LENGTH(sizeof(remote_addr.su.in6.sin6_addr));
memcpy(RTA_DATA(&nlmsg.dst), &remote_addr.su.in6.sin6_addr,
sizeof(remote_addr.su.in6.sin6_addr));
break;
default:
assert(0);
}
std::visit(
[&nlmsg](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, sockaddr_in>) {
nlmsg.dst.rta_len = RTA_LENGTH(sizeof(arg.sin_addr));
memcpy(RTA_DATA(&nlmsg.dst), &arg.sin_addr, sizeof(arg.sin_addr));
return;
}
if constexpr (std::is_same_v<T, sockaddr_in6>) {
nlmsg.dst.rta_len = RTA_LENGTH(sizeof(arg.sin6_addr));
memcpy(RTA_DATA(&nlmsg.dst), &arg.sin6_addr, sizeof(arg.sin6_addr));
return;
}
assert(0);
abort();
},
remote_addr.skaddr);
nlmsg.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(nlmsg.msg) + nlmsg.dst.rta_len);
@ -309,7 +295,7 @@ int send_netlink_msg(int fd, const Address &remote_addr, uint32_t seq) {
} // namespace
namespace {
int recv_netlink_msg(in_addr_union &iau, int fd, uint32_t seq) {
int recv_netlink_msg(InAddr &ia, int fd, uint32_t seq) {
std::array<uint8_t, 8192> buf;
iovec iov = {
.iov_base = buf.data(),
@ -334,8 +320,6 @@ int recv_netlink_msg(in_addr_union &iau, int fd, uint32_t seq) {
return -1;
}
size_t in_addrlen = 0;
for (auto hdr = reinterpret_cast<nlmsghdr *>(buf.data());
NLMSG_OK(hdr, nread); hdr = NLMSG_NEXT(hdr, nread)) {
if (seq != hdr->nlmsg_seq) {
@ -372,28 +356,42 @@ int recv_netlink_msg(in_addr_union &iau, int fd, uint32_t seq) {
}
switch (static_cast<rtmsg *>(NLMSG_DATA(hdr))->rtm_family) {
case AF_INET:
in_addrlen = sizeof(in_addr);
case AF_INET: {
constexpr auto in_addrlen = sizeof(in_addr);
if (RTA_LENGTH(in_addrlen) != rta->rta_len) {
return -1;
}
in_addr addr;
memcpy(&addr, RTA_DATA(rta), in_addrlen);
ia.emplace<in_addr>(addr);
break;
case AF_INET6:
in_addrlen = sizeof(in6_addr);
}
case AF_INET6: {
constexpr auto in_addrlen = sizeof(in6_addr);
if (RTA_LENGTH(in_addrlen) != rta->rta_len) {
return -1;
}
in6_addr addr;
memcpy(&addr, RTA_DATA(rta), in_addrlen);
ia.emplace<in6_addr>(addr);
break;
}
default:
assert(0);
abort();
}
if (RTA_LENGTH(in_addrlen) != rta->rta_len) {
return -1;
}
memcpy(&iau, RTA_DATA(rta), in_addrlen);
break;
}
}
if (in_addrlen == 0) {
if (in_addr_empty(ia)) {
return -1;
}
@ -459,7 +457,7 @@ int recv_netlink_msg(in_addr_union &iau, int fd, uint32_t seq) {
}
} // namespace
int get_local_addr(in_addr_union &iau, const Address &remote_addr) {
int get_local_addr(InAddr &ia, const Address &remote_addr) {
sockaddr_nl sa{
.nl_family = AF_NETLINK,
};
@ -471,7 +469,7 @@ int get_local_addr(in_addr_union &iau, const Address &remote_addr) {
return -1;
}
auto fd_d = defer(close, fd);
auto fd_d = defer([fd] { close(fd); });
if (bind(fd, reinterpret_cast<sockaddr *>(&sa), sizeof(sa)) == -1) {
std::cerr << "bind: Could not bind netlink socket: " << strerror(errno)
@ -485,23 +483,163 @@ int get_local_addr(in_addr_union &iau, const Address &remote_addr) {
return -1;
}
return recv_netlink_msg(iau, fd, seq);
return recv_netlink_msg(ia, fd, seq);
}
#endif // defined(HAVE_LINUX_NETLINK_H)
bool addreq(const sockaddr *sa, const in_addr_union &iau) {
bool addreq(const Address &addr, const InAddr &ia) {
return std::visit(
[&ia](auto &&arg) -> bool {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, sockaddr_in>) {
auto rhs = std::get_if<in_addr>(&ia);
return rhs && memcmp(&arg.sin_addr, rhs, sizeof(*rhs)) == 0;
}
if constexpr (std::is_same_v<T, sockaddr_in6>) {
auto rhs = std::get_if<in6_addr>(&ia);
return rhs && memcmp(&arg.sin6_addr, rhs, sizeof(*rhs)) == 0;
}
assert(0);
abort();
},
addr.skaddr);
}
const void *in_addr_get_ptr(const InAddr &ia) {
return std::visit(
[](auto &&arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
std::monostate>) {
assert(0);
abort();
}
return reinterpret_cast<const void *>(&arg);
},
ia);
}
bool in_addr_empty(const InAddr &ia) {
return std::holds_alternative<std::monostate>(ia);
}
const sockaddr *as_sockaddr(const Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
std::monostate>) {
assert(0);
abort();
}
return reinterpret_cast<const sockaddr *>(&arg);
},
skaddr);
}
sockaddr *as_sockaddr(Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
std::monostate>) {
assert(0);
abort();
}
return reinterpret_cast<sockaddr *>(&arg);
},
skaddr);
}
int sockaddr_family(const Sockaddr &skaddr) {
return as_sockaddr(skaddr)->sa_family;
}
uint16_t sockaddr_port(const Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) -> uint16_t {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, sockaddr_in>) {
return ntohs(arg.sin_port);
}
if constexpr (std::is_same_v<T, sockaddr_in6>) {
return ntohs(arg.sin6_port);
}
assert(0);
abort();
},
skaddr);
}
void sockaddr_port(Sockaddr &skaddr, uint16_t port) {
std::visit(
[port](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, sockaddr_in>) {
arg.sin_port = htons(port);
return;
}
if constexpr (std::is_same_v<T, sockaddr_in6>) {
arg.sin6_port = htons(port);
return;
}
assert(0);
abort();
},
skaddr);
}
void sockaddr_set(Sockaddr &skaddr, const sockaddr *sa) {
switch (sa->sa_family) {
case AF_INET:
return memcmp(&reinterpret_cast<const sockaddr_in *>(sa)->sin_addr, &iau.in,
sizeof(iau.in)) == 0;
skaddr.emplace<sockaddr_in>(*reinterpret_cast<const sockaddr_in *>(sa));
return;
case AF_INET6:
return memcmp(&reinterpret_cast<const sockaddr_in6 *>(sa)->sin6_addr,
&iau.in6, sizeof(iau.in6)) == 0;
skaddr.emplace<sockaddr_in6>(*reinterpret_cast<const sockaddr_in6 *>(sa));
return;
default:
assert(0);
abort();
}
}
socklen_t sockaddr_size(const Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) { return static_cast<socklen_t>(sizeof(arg)); }, skaddr);
}
bool sockaddr_empty(const Sockaddr &skaddr) {
return std::holds_alternative<std::monostate>(skaddr);
}
const sockaddr *Address::as_sockaddr() const {
return ngtcp2::as_sockaddr(skaddr);
}
sockaddr *Address::as_sockaddr() { return ngtcp2::as_sockaddr(skaddr); }
int Address::family() const { return sockaddr_family(skaddr); }
uint16_t Address::port() const { return sockaddr_port(skaddr); }
void Address::port(uint16_t port) { sockaddr_port(skaddr, port); }
void Address::set(const sockaddr *sa) { sockaddr_set(skaddr, sa); }
socklen_t Address::size() const { return sockaddr_size(skaddr); }
bool Address::empty() const { return sockaddr_empty(skaddr); }
} // namespace ngtcp2

View File

@ -51,15 +51,17 @@ consteval std::span<const uint8_t> as_uint8_span(const uint8_t (&s)[N]) {
return {s, N - 1};
}
constexpr uint8_t RAW_HQ_ALPN[] = "\xahq-interop";
constexpr auto HQ_ALPN = as_uint8_span(RAW_HQ_ALPN);
constexpr auto HQ_ALPN_V1 = as_uint8_span(RAW_HQ_ALPN);
inline constexpr uint8_t RAW_HQ_ALPN[] = "\xahq-interop";
inline constexpr auto HQ_ALPN = as_uint8_span(RAW_HQ_ALPN);
inline constexpr auto HQ_ALPN_V1 = as_uint8_span(RAW_HQ_ALPN);
constexpr uint8_t RAW_H3_ALPN[] = "\x2h3";
constexpr auto H3_ALPN = as_uint8_span(RAW_H3_ALPN);
constexpr auto H3_ALPN_V1 = as_uint8_span(RAW_H3_ALPN);
inline constexpr uint8_t RAW_H3_ALPN[] = "\x2h3";
inline constexpr auto H3_ALPN = as_uint8_span(RAW_H3_ALPN);
inline constexpr auto H3_ALPN_V1 = as_uint8_span(RAW_H3_ALPN);
constexpr uint32_t TLS_ALERT_ECH_REQUIRED = 121;
inline constexpr uint32_t TLS_ALERT_ECH_REQUIRED = 121;
inline constexpr size_t MAX_RECV_PKTS = 64;
// msghdr_get_ecn gets ECN bits from |msg|. |family| is the address
// family from which packet is received.
@ -85,14 +87,61 @@ std::optional<Address> msghdr_get_local_addr(msghdr *msg, int family);
// not found, or UDP_GRO is not supported, this function returns 0.
size_t msghdr_get_udp_gro(msghdr *msg);
void set_port(Address &dst, const Address &src);
// get_local_addr stores preferred local address (interface address)
// in |iau| for a given destination address |remote_addr|.
int get_local_addr(in_addr_union &iau, const Address &remote_addr);
// in |ia| for a given destination address |remote_addr|.
int get_local_addr(InAddr &ia, const Address &remote_addr);
// addreq returns true if |sa| and |iau| contain the same address.
bool addreq(const sockaddr *sa, const in_addr_union &iau);
// addreq returns true if |addr| and |ia| contain the same address.
bool addreq(const Address &addr, const InAddr &ia);
// in_addr_get_ptr returns the pointer to the stored address in |ia|.
// It is undefined if |ia| contains std::monostate.
const void *in_addr_get_ptr(const InAddr &ia);
// in_addr_empty returns true if |ia| if it does not contain any
// meaningful address.
bool in_addr_empty(const InAddr &ia);
// as_sockaddr returns the pointer to the stored address casted to
// const sockaddr *.
[[nodiscard]] const sockaddr *as_sockaddr(const Sockaddr &skaddr);
[[nodiscard]] sockaddr *as_sockaddr(Sockaddr &skaddr);
// sockaddr_family returns the address family.
[[nodiscard]] int sockaddr_family(const Sockaddr &skaddr);
// sockaddr_port returns the port.
[[nodiscard]] uint16_t sockaddr_port(const Sockaddr &skaddr);
// sockaddr_port sets |port| to |skaddr|.
void sockaddr_port(Sockaddr &skaddr, uint16_t port);
// sockaddr_set stores |sa| to |skaddr|. The address family is
// determined by |sa|->sa_family, and |sa| must point to the memory
// that contains valid object which is either sockaddr_in or
// sockaddr_in6.
void sockaddr_set(Sockaddr &skaddr, const sockaddr *sa);
// sockaddr_size returns the size of the stored address.
[[nodiscard]] socklen_t sockaddr_size(const Sockaddr &skaddr);
// sockaddr_empty returns true if |skaddr| does not contain any
// meaningful address.
[[nodiscard]] bool sockaddr_empty(const Sockaddr &skaddr);
[[nodiscard]] inline ngtcp2_addr as_ngtcp2_addr(const Address &addr) {
return {
.addr = const_cast<sockaddr *>(addr.as_sockaddr()),
.addrlen = addr.size(),
};
}
[[nodiscard]] inline ngtcp2_addr as_ngtcp2_addr(Address &addr) {
return {
.addr = addr.as_sockaddr(),
.addrlen = addr.size(),
};
}
} // namespace ngtcp2

977
deps/ngtcp2/ngtcp2/examples/sim.cc vendored Normal file
View File

@ -0,0 +1,977 @@
/*
* ngtcp2
*
* Copyright (c) 2025 ngtcp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "sim.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cmath>
#include <utility>
#include <string_view>
#include <iostream>
#include "ngtcp2/ngtcp2_crypto_wolfssl.h"
#include "util.h"
#include "shared.h"
#include "debug.h"
using namespace std::literals;
namespace ngtcp2 {
namespace {
constexpr auto ALPN_LIST = "ngtcp2-sim"sv;
constexpr size_t CIDLEN = 10;
constexpr uint8_t SERVER_SECRET[] = "server_secret";
int generate_secure_random(std::span<uint8_t> data) {
if (wolfSSL_RAND_bytes(data.data(), static_cast<int>(data.size())) != 1) {
return -1;
}
return 0;
}
void rand_bytes(uint8_t *dest, size_t destlen,
const ngtcp2_rand_ctx *rand_ctx) {
auto rv = generate_secure_random({dest, destlen});
(void)rv;
assert(0 == rv);
}
int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, uint8_t *token,
size_t cidlen, void *user_data) {
if (generate_secure_random({cid->data, cidlen}) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
cid->datalen = cidlen;
if (ngtcp2_crypto_generate_stateless_reset_token(
token, SERVER_SECRET, sizeof(SERVER_SECRET) - 1, cid) != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
} // namespace
ngtcp2_tstamp to_ngtcp2_tstamp(const Timestamp &ts) {
return static_cast<ngtcp2_tstamp>(ts.time_since_epoch().count());
}
Timestamp to_timestamp(ngtcp2_tstamp ts) {
return Timestamp{Timestamp::duration{ts}};
}
uint64_t LinkConfig::compute_expected_goodput(Timestamp::duration rtt) const {
// Assume 80% usage ratio.
uint64_t g = rate * 8 / 10;
if (loss < 1e-9) {
return g;
}
constexpr double margin = 0.95;
return std::min(g,
static_cast<uint64_t>(MAX_UDP_PAYLOAD_SIZE * NGTCP2_SECONDS /
static_cast<double>(rtt.count()) /
sqrt(loss) * 8 * margin));
}
namespace {
int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
uint64_t offset, const uint8_t *data, size_t datalen,
void *user_data, void *stream_user_data) {
ngtcp2_conn_extend_max_stream_offset(conn, stream_id, datalen);
ngtcp2_conn_extend_max_offset(conn, datalen);
return 0;
}
} // namespace
ngtcp2_callbacks default_client_callbacks() {
return ngtcp2_callbacks{
.client_initial = ngtcp2_crypto_client_initial_cb,
.recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb,
.encrypt = ngtcp2_crypto_encrypt_cb,
.decrypt = ngtcp2_crypto_decrypt_cb,
.hp_mask = ngtcp2_crypto_hp_mask_cb,
.recv_stream_data = recv_stream_data,
.recv_retry = ngtcp2_crypto_recv_retry_cb,
.rand = rand_bytes,
.get_new_connection_id = get_new_connection_id,
.update_key = ngtcp2_crypto_update_key_cb,
.delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb,
.delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
.get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb,
.version_negotiation = ngtcp2_crypto_version_negotiation_cb,
};
}
ngtcp2_callbacks default_server_callbacks() {
return ngtcp2_callbacks{
.recv_client_initial = ngtcp2_crypto_recv_client_initial_cb,
.recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb,
.encrypt = ngtcp2_crypto_encrypt_cb,
.decrypt = ngtcp2_crypto_decrypt_cb,
.hp_mask = ngtcp2_crypto_hp_mask_cb,
.recv_stream_data = recv_stream_data,
.rand = rand_bytes,
.get_new_connection_id = get_new_connection_id,
.update_key = ngtcp2_crypto_update_key_cb,
.delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb,
.delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
.get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb,
.version_negotiation = ngtcp2_crypto_version_negotiation_cb,
};
}
ngtcp2_settings default_client_settings() {
ngtcp2_settings settings;
ngtcp2_settings_default(&settings);
settings.log_printf = debug::log_printf;
return settings;
}
ngtcp2_settings default_server_settings() {
ngtcp2_settings settings;
ngtcp2_settings_default(&settings);
settings.log_printf = debug::log_printf;
return settings;
}
ngtcp2_transport_params default_client_transport_params() {
ngtcp2_transport_params params;
ngtcp2_transport_params_default(&params);
return params;
}
ngtcp2_transport_params default_server_transport_params() {
ngtcp2_transport_params params;
ngtcp2_transport_params_default(&params);
return params;
}
Sockaddr getaddrinfo(const char *host, const char *svc) {
auto hints = addrinfo{
.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV,
.ai_family = AF_UNSPEC,
};
addrinfo *rp;
auto rv = getaddrinfo(host, svc, &hints, &rp);
(void)rv;
assert(0 == rv);
Sockaddr skaddr;
sockaddr_set(skaddr, rp->ai_addr);
freeaddrinfo(rp);
return skaddr;
}
ngtcp2_addr default_client_addr() {
static auto skaddr = getaddrinfo("10.0.1.1", "12345");
return ngtcp2_addr{
.addr = as_sockaddr(skaddr),
.addrlen = sockaddr_size(skaddr),
};
}
ngtcp2_addr default_server_addr() {
static auto skaddr = getaddrinfo("10.0.2.1", "443");
return ngtcp2_addr{
.addr = as_sockaddr(skaddr),
.addrlen = sockaddr_size(skaddr),
};
}
EndpointConfig default_client_endpoint_config() {
return EndpointConfig{
.callbacks = default_client_callbacks(),
.settings = default_client_settings(),
.params = default_client_transport_params(),
.local_addr = default_client_addr(),
};
}
EndpointConfig default_server_endpoint_config() {
return EndpointConfig{
.server = true,
.callbacks = default_server_callbacks(),
.settings = default_server_settings(),
.params = default_server_transport_params(),
.local_addr = default_server_addr(),
};
}
namespace {
ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) {
auto ep = static_cast<Endpoint *>(conn_ref->user_data);
return ep->get_conn();
}
} // namespace
Endpoint::Endpoint()
: conn_ref_{ngtcp2::get_conn, this}, channel_{config_.link} {}
Endpoint::Endpoint(const EndpointConfig &config)
: config_{config},
conn_ref_{ngtcp2::get_conn, this},
channel_{config_.link} {}
Endpoint::Endpoint(Endpoint &&other) noexcept
: config_{std::exchange(other.config_, {})},
ssl_ctx_{std::exchange(other.ssl_ctx_, nullptr)},
ssl_{std::exchange(other.ssl_, nullptr)},
conn_{std::exchange(other.conn_, nullptr)},
conn_ref_{ngtcp2::get_conn, this},
channel_{std::exchange(other.channel_, {})},
initialized_{std::exchange(other.initialized_, false)} {}
Endpoint::~Endpoint() {
ngtcp2_conn_del(conn_);
if (ssl_) {
wolfSSL_free(ssl_);
}
if (ssl_ctx_) {
wolfSSL_CTX_free(ssl_ctx_);
}
}
Endpoint &Endpoint::operator=(Endpoint &&other) noexcept {
ngtcp2_conn_del(conn_);
if (ssl_) {
wolfSSL_free(ssl_);
}
if (ssl_ctx_) {
wolfSSL_CTX_free(ssl_ctx_);
}
config_ = std::exchange(other.config_, {});
ssl_ctx_ = std::exchange(other.ssl_ctx_, nullptr);
ssl_ = std::exchange(other.ssl_, nullptr);
conn_ = std::exchange(other.conn_, nullptr);
conn_ref_ = {ngtcp2::get_conn, this};
channel_ = std::exchange(other.channel_, {});
initialized_ = std::exchange(other.initialized_, false);
if (ssl_) {
wolfSSL_set_app_data(ssl_, &conn_ref_);
}
return *this;
}
namespace {
constexpr auto tls_key = R"(-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwEvkGGgXAcRaG7Z8
gA7C6+W2RsW9gcjV9e5ybr0ikaahRANCAASCo35bDi+Q/q/CzHI1e5QaBrbqbFhW
G20QbVAeMK8l0oC8OGD3PSpZK1HXwALwzhMuwhxDos3ANb5naa5y17fQ
-----END PRIVATE KEY-----
)"sv;
constexpr auto tls_crt = R"(-----BEGIN CERTIFICATE-----
MIICBzCCAa2gAwIBAgIUd2l6Pce3S0QH3dQC0Q/CjHbmggowCgYIKoZIzj0EAwIw
WTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1
MTExNDExNTcwMFoXDTI1MTIxNDExNTcwMFowWTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
gqN+Ww4vkP6vwsxyNXuUGga26mxYVhttEG1QHjCvJdKAvDhg9z0qWStR18AC8M4T
LsIcQ6LNwDW+Z2mucte30KNTMFEwHQYDVR0OBBYEFFVgXLoLwzpf6+twP5z8Ujr2
5mxnMB8GA1UdIwQYMBaAFFVgXLoLwzpf6+twP5z8Ujr25mxnMA8GA1UdEwEB/wQF
MAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAO4tnDNRAcooz62vf2m7vTyDqFCjcaIv
SJ9Gq0lvEXEcAiBwWBNUASBqLaje3hmtgwxcF7EIqqiGo5j8f9Ufgu6SRg==
-----END CERTIFICATE-----
)"sv;
} // namespace
int Endpoint::setup_server(std::span<const uint8_t> original_dcid,
std::span<const uint8_t> client_scid,
uint32_t version, const ngtcp2_addr *remote_addr) {
int rv;
ngtcp2_cid scid{
.datalen = CIDLEN,
};
if (generate_secure_random({scid.data, scid.datalen}) != 0) {
return -1;
}
ngtcp2_cid dcid;
ngtcp2_cid_init(&dcid, client_scid.data(), client_scid.size());
auto params = config_.params;
ngtcp2_cid_init(&params.original_dcid, original_dcid.data(),
original_dcid.size());
params.original_dcid_present = 1;
if (ngtcp2_crypto_generate_stateless_reset_token(
params.stateless_reset_token, SERVER_SECRET, sizeof(SERVER_SECRET) - 1,
&scid)) {
return -1;
}
auto path = ngtcp2_path{
.local = config_.local_addr,
.remote = *remote_addr,
};
rv = ngtcp2_conn_server_new(&conn_, &dcid, &scid, &path, version,
&config_.callbacks, &config_.settings, &params,
nullptr, config_.user_data);
if (rv != 0) {
return -1;
}
ssl_ctx_ = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
if (!ssl_ctx_) {
return -1;
}
if (ngtcp2_crypto_wolfssl_configure_server_context(ssl_ctx_) != 0) {
return -1;
}
if (wolfSSL_CTX_use_certificate_buffer(
ssl_ctx_, reinterpret_cast<const uint8_t *>(tls_crt.data()),
static_cast<long>(tls_crt.size()), SSL_FILETYPE_PEM) != SSL_SUCCESS) {
return -1;
}
if (wolfSSL_CTX_use_PrivateKey_buffer(
ssl_ctx_, reinterpret_cast<const uint8_t *>(tls_key.data()),
static_cast<long>(tls_key.size()), SSL_FILETYPE_PEM) != SSL_SUCCESS) {
return -1;
}
ssl_ = wolfSSL_new(ssl_ctx_);
if (!ssl_) {
return -1;
}
if (wolfSSL_UseALPN(ssl_, const_cast<char *>(ALPN_LIST.data()),
ALPN_LIST.size(),
WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) {
return -1;
}
wolfSSL_set_app_data(ssl_, &conn_ref_);
wolfSSL_set_accept_state(ssl_);
wolfSSL_set_quic_transport_version(ssl_, 0x39);
ngtcp2_conn_set_tls_native_handle(conn_, ssl_);
initialized_ = true;
return 0;
}
int Endpoint::setup_client(const ngtcp2_addr *remote_addr) {
int rv;
ngtcp2_cid dcid{
.datalen = CIDLEN,
};
ngtcp2_cid scid{
.datalen = CIDLEN,
};
if (generate_secure_random({dcid.data, dcid.datalen}) != 0 ||
generate_secure_random({scid.data, scid.datalen}) != 0) {
assert(0);
return -1;
}
auto path = ngtcp2_path{
.local = config_.local_addr,
.remote = *remote_addr,
};
rv = ngtcp2_conn_client_new(&conn_, &dcid, &scid, &path, NGTCP2_PROTO_VER_V1,
&config_.callbacks, &config_.settings,
&config_.params, nullptr, config_.user_data);
if (rv != 0) {
return -1;
}
ssl_ctx_ = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
if (!ssl_ctx_) {
return -1;
}
if (ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx_) != 0) {
return -1;
}
ssl_ = wolfSSL_new(ssl_ctx_);
if (!ssl_) {
return -1;
}
if (wolfSSL_UseALPN(ssl_, const_cast<char *>(ALPN_LIST.data()),
ALPN_LIST.size(),
WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) {
return -1;
}
wolfSSL_set_app_data(ssl_, &conn_ref_);
wolfSSL_set_connect_state(ssl_);
wolfSSL_set_quic_transport_version(ssl_, 0x39);
ngtcp2_conn_set_tls_native_handle(conn_, ssl_);
initialized_ = true;
return 0;
}
int Endpoint::on_read(const NetworkPath &path, std::span<const uint8_t> pkt,
const Context &ctx) {
auto ts = to_ngtcp2_tstamp(ctx.ts);
auto cpath = to_ngtcp2_path(path);
auto rv =
ngtcp2_conn_read_pkt(conn_, &cpath, nullptr, pkt.data(), pkt.size(), ts);
if (rv != 0) {
std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
return -1;
}
ctx.endpoint->get_channel().schedule_timeout(ctx.ts);
return 0;
}
int Endpoint::on_write(const Context &ctx) {
if (config_.on_write(conn_, ctx) != 0) {
return -1;
}
auto next_expiry_ts = ngtcp2_conn_get_expiry(conn_);
if (next_expiry_ts == UINT64_MAX) {
return 0;
}
ctx.endpoint->get_channel().schedule_timeout(to_timestamp(next_expiry_ts));
return 0;
}
int Endpoint::on_timeout(const Context &ctx) {
auto rv = ngtcp2_conn_handle_expiry(conn_, to_ngtcp2_tstamp(ctx.ts));
if (rv != 0) {
std::cerr << "ngtcp2_conn_handle_expiry: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
return on_write(ctx);
}
NetworkPath to_network_path(const ngtcp2_path *path) {
NetworkPath res;
res.local.set(path->local.addr);
res.remote.set(path->remote.addr);
return res;
}
ngtcp2_path to_ngtcp2_path(const NetworkPath &path) {
return {
.local = as_ngtcp2_addr(path.local),
.remote = as_ngtcp2_addr(path.remote),
};
}
NetworkPath NetworkPath::invert() {
auto path = *this;
std::swap(path.local, path.remote);
return path;
}
Channel::Channel(const LinkConfig &config)
: link_config_{config}, gen_{link_config_.seed} {}
Channel::Channel(Channel &&other) noexcept
: link_config_{std::exchange(other.link_config_, {})},
gen_{std::exchange(other.gen_, {})},
tx_queue_{std::exchange(other.tx_queue_, {})},
tx_queue_size_{std::exchange(other.tx_queue_size_, 0)},
link_free_ts_{std::exchange(other.link_free_ts_, {})},
queue_{std::exchange(other.queue_, {})},
timeout_{std::exchange(other.timeout_, {})},
ts_{std::exchange(other.ts_, {})} {}
Channel &Channel::operator=(Channel &&other) noexcept {
link_config_ = std::exchange(other.link_config_, {});
gen_ = std::exchange(other.gen_, {});
tx_queue_ = std::exchange(other.tx_queue_, {});
tx_queue_size_ = std::exchange(other.tx_queue_size_, 0);
link_free_ts_ = std::exchange(other.link_free_ts_, {});
queue_ = std::exchange(other.queue_, {});
timeout_ = std::exchange(other.timeout_, {});
ts_ = std::exchange(other.ts_, {});
return *this;
}
void Channel::send_pkt(const NetworkPath &path, std::span<uint8_t> pkt) {
auto rate = link_config_.rate / 8;
if (rate == 0) {
queue_.emplace(Event{
.ts = ts_ + link_config_.delay,
.type = EVENT_TYPE_PKT,
.path = path,
.pkt = std::vector(std::ranges::begin(pkt), std::ranges::end(pkt)),
});
return;
}
if (link_config_.limit && tx_queue_size_ + pkt.size() > link_config_.limit) {
return;
}
auto departure_ts = std::max(ts_, link_free_ts_) +
Timestamp::duration{pkt.size() * NGTCP2_SECONDS / rate};
if (!decide_pkt_lost()) {
queue_.emplace(Event{
.ts = departure_ts + link_config_.delay,
.type = EVENT_TYPE_PKT,
.path = path,
.pkt = std::vector(std::ranges::begin(pkt), std::ranges::end(pkt)),
});
}
tx_queue_.emplace_back(TxPacket{
.departure_ts = departure_ts,
.size = pkt.size(),
});
tx_queue_size_ += pkt.size();
link_free_ts_ = departure_ts;
}
bool Channel::decide_pkt_lost() {
return std::uniform_real_distribution<>(0, 1.0)(gen_) < link_config_.loss;
}
void Channel::pop_tx_queue() {
size_t n = 0;
auto it = std::ranges::find_if(tx_queue_, [&n, this](const auto &pkt) {
if (pkt.departure_ts > ts_) {
return true;
}
n += pkt.size;
return false;
});
assert(tx_queue_size_ >= n);
tx_queue_size_ -= n;
tx_queue_.erase(std::ranges::begin(tx_queue_), it);
}
void Channel::schedule_timeout(Timestamp ts) {
timeout_ = std::min(timeout_, ts);
}
Timestamp Channel::get_next_timestamp() const {
if (queue_.empty()) {
return timeout_;
}
auto &top = queue_.top();
return std::min(timeout_, top.ts);
}
Event Channel::get_next_event() {
if (!queue_.empty() && queue_.top().ts <= timeout_) {
auto &top = const_cast<Event &>(queue_.top());
auto ev = Event{
.ts = top.ts,
.type = top.type,
.path = top.path,
.pkt = std::move(top.pkt),
};
queue_.pop();
return ev;
}
return Event{
.ts = std::exchange(timeout_, Timestamp::max()),
.type = EVENT_TYPE_TIMEOUT,
};
}
Simulator::Simulator(Endpoint client, Endpoint server)
: client_{std::move(client)}, server_{std::move(server)} {}
Simulator::Simulator(Simulator &&other) noexcept
: client_{std::exchange(client_, {})},
server_{std::exchange(server_, {})},
max_events_{std::exchange(other.max_events_, 0)} {}
Simulator &Simulator::operator=(Simulator &&other) noexcept {
client_ = std::exchange(other.client_, {});
server_ = std::exchange(other.server_, {});
max_events_ = std::exchange(other.max_events_, 0);
return *this;
}
std::optional<std::tuple<Event, Endpoint &>> Simulator::get_next_event() {
auto &client_chan = client_.get_channel();
auto &server_chan = server_.get_channel();
auto client_next_ts = client_chan.get_next_timestamp();
auto server_next_ts = server_chan.get_next_timestamp();
if (client_next_ts == Timestamp::max() &&
server_next_ts == Timestamp::max()) {
return {};
}
if (client_next_ts <= server_next_ts) {
auto ev = client_chan.get_next_event();
return {{std::move(ev), client_}};
}
auto ev = server_chan.get_next_event();
return {{std::move(ev), server_}};
}
int Simulator::run() {
if (client_.get_initialized() ||
client_.setup_client(&server_.get_endpoint_config().local_addr) != 0) {
return -1;
}
auto ts = Timestamp{};
auto &client_chan = client_.get_channel();
auto &server_chan = server_.get_channel();
client_chan.schedule_timeout(ts);
size_t k = 0;
for (; k < max_events_; ++k) {
auto maybe_event = get_next_event();
if (!maybe_event) {
break;
}
auto &[event, ep] = *maybe_event;
assert(ts <= event.ts);
ts = event.ts;
client_chan.set_timestamp(ts);
server_chan.set_timestamp(ts);
client_chan.pop_tx_queue();
server_chan.pop_tx_queue();
switch (event.type) {
case EVENT_TYPE_TIMEOUT: {
auto ctx = Context{
.sim = this,
.ts = ts,
.endpoint = &ep,
};
if (ep.on_timeout(ctx) != 0) {
return -1;
}
break;
}
case EVENT_TYPE_PKT:
if (deliver_pkt(ep, event.path.invert(), event.pkt, ts) != 0) {
return -1;
}
break;
}
}
if (k == max_events_) {
return -1;
}
return 0;
}
Endpoint &Simulator::get_opposite_endpoint(const Endpoint &ep) {
return &ep == &client_ ? server_ : client_;
}
int Simulator::deliver_pkt(Endpoint &remote_ep, const NetworkPath &path,
std::span<const uint8_t> pkt, Timestamp ts) {
auto &local_ep = get_opposite_endpoint(remote_ep);
if (!local_ep.get_initialized() && local_ep.get_endpoint_config().server) {
ngtcp2_version_cid vcid;
auto rv =
ngtcp2_pkt_decode_version_cid(&vcid, pkt.data(), pkt.size(), CIDLEN);
if (rv != 0) {
return 0;
}
ngtcp2_pkt_hd hd;
if (ngtcp2_accept(&hd, pkt.data(), pkt.size()) != 0) {
return 0;
}
if (local_ep.setup_server(
{vcid.dcid, vcid.dcidlen}, {vcid.scid, vcid.scidlen}, vcid.version,
&remote_ep.get_endpoint_config().local_addr) != 0) {
return -1;
}
}
auto ctx = Context{
.sim = this,
.ts = ts,
.endpoint = &local_ep,
};
return local_ep.on_read(path, pkt, ctx);
}
void HandshakeApp::configure(EndpointConfig &config) {
auto handshake_confirmed = [](ngtcp2_conn *conn, void *user_data) {
auto app = static_cast<HandshakeApp *>(user_data);
app->handshake_confirmed();
return 0;
};
if (config.server) {
config.callbacks.handshake_completed = handshake_confirmed;
} else {
config.callbacks.handshake_confirmed = handshake_confirmed;
}
config.on_write = [](ngtcp2_conn *conn, const Context &ctx) {
std::array<uint8_t, MAX_UDP_PAYLOAD_SIZE> buf;
auto ts = to_ngtcp2_tstamp(ctx.ts);
ngtcp2_path_storage ps;
ngtcp2_path_storage_zero(&ps);
auto nwrite = ngtcp2_conn_write_pkt(conn, &ps.path, nullptr, buf.data(),
buf.size(), ts);
if (nwrite < 0) {
std::cerr << "ngtcp2_conn_write_pkt: "
<< ngtcp2_strerror(static_cast<int>(nwrite)) << std::endl;
return -1;
}
if (nwrite == 0) {
return 0;
}
ngtcp2_conn_update_pkt_tx_time(conn, ts);
ctx.endpoint->get_channel().send_pkt(
to_network_path(&ps.path), {buf.data(), static_cast<size_t>(nwrite)});
return 0;
};
config.user_data = this;
}
UniStreamApp::UniStreamApp(uint64_t max_bytes) : max_bytes_{max_bytes} {}
namespace {
std::array<uint8_t, 4096> nulldata;
} // namespace
void UniStreamApp::configure(EndpointConfig &config) {
config.callbacks.stream_close = [](ngtcp2_conn *conn, uint32_t flags,
int64_t stream_id, uint64_t app_error_code,
void *user_data, void *stream_user_data) {
auto app = static_cast<UniStreamApp *>(user_data);
app->stream_close(conn, stream_id);
return 0;
};
config.callbacks.extend_max_local_streams_uni =
[](ngtcp2_conn *conn, uint64_t max_streams, void *user_data) {
auto app = static_cast<UniStreamApp *>(user_data);
return app->extend_max_local_streams_uni(conn);
};
config.on_write = [this](ngtcp2_conn *conn, const Context &ctx) {
return on_write(conn, ctx);
};
config.user_data = this;
}
uint64_t UniStreamApp::compute_goodput() const {
auto d = get_transmit_duration();
if (d == Timestamp::duration::zero()) {
return 0;
}
return static_cast<uint64_t>(static_cast<double>(bytes_sent_) *
NGTCP2_SECONDS / static_cast<double>(d.count()) *
8);
}
void UniStreamApp::stream_close(ngtcp2_conn *conn, int64_t stream_id) {
if (stream_id_ != stream_id) {
return;
}
if (is_all_bytes_sent()) {
end_ts_ = to_timestamp(ngtcp2_conn_get_timestamp(conn));
}
}
int UniStreamApp::extend_max_local_streams_uni(ngtcp2_conn *conn) {
if (stream_id_ != -1) {
return 0;
}
int64_t stream_id;
auto rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, nullptr);
if (rv != 0) {
std::cerr << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv)
<< std::endl;
return NGTCP2_ERR_CALLBACK_FAILURE;
}
stream_id_ = stream_id;
start_ts_ = to_timestamp(ngtcp2_conn_get_timestamp(conn));
return 0;
}
int UniStreamApp::on_write(ngtcp2_conn *conn, const Context &ctx) {
std::array<uint8_t, MAX_UDP_PAYLOAD_SIZE> buf;
int64_t stream_id;
ngtcp2_vec vec;
size_t veccnt;
uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_NONE;
if (stream_id_ != -1 && max_bytes_ > bytes_sent_) {
stream_id = stream_id_;
vec.base = nulldata.data();
vec.len = static_cast<size_t>(
std::min(static_cast<uint64_t>(buf.size()), max_bytes_ - bytes_sent_));
veccnt = 1;
if (bytes_sent_ + vec.len == max_bytes_) {
flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
}
} else {
stream_id = -1;
veccnt = 0;
}
auto ts = to_ngtcp2_tstamp(ctx.ts);
ngtcp2_path_storage ps;
ngtcp2_path_storage_zero(&ps);
ngtcp2_ssize ndatalen;
auto nwrite =
ngtcp2_conn_writev_stream(conn, &ps.path, nullptr, buf.data(), buf.size(),
&ndatalen, flags, stream_id, &vec, veccnt, ts);
if (nwrite < 0) {
if (nwrite == NGTCP2_ERR_STREAM_DATA_BLOCKED) {
return 0;
}
std::cerr << "ngtcp2_conn_writev_stream: "
<< ngtcp2_strerror(static_cast<int>(nwrite)) << std::endl;
return -1;
}
if (nwrite == 0) {
return 0;
}
if (ndatalen > 0) {
bytes_sent_ += static_cast<size_t>(ndatalen);
}
ngtcp2_conn_update_pkt_tx_time(conn, ts);
ctx.endpoint->get_channel().send_pkt(
to_network_path(&ps.path), {buf.data(), static_cast<size_t>(nwrite)});
return 0;
}
} // namespace ngtcp2

289
deps/ngtcp2/ngtcp2/examples/sim.h vendored Normal file
View File

@ -0,0 +1,289 @@
/*
* ngtcp2
*
* Copyright (c) 2025 ngtcp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SIM_H
#define SIM_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif // defined(HAVE_CONFIG_H)
#include <functional>
#include <chrono>
#include <queue>
#include <span>
#include <random>
#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include "network.h"
namespace ngtcp2 {
inline constexpr size_t MAX_UDP_PAYLOAD_SIZE = 1500;
using Timestamp =
std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds>;
ngtcp2_tstamp to_ngtcp2_tstamp(const Timestamp &ts);
Timestamp to_timestamp(ngtcp2_tstamp ts);
class Simulator;
class Endpoint;
struct Context {
Simulator *sim;
Timestamp ts;
Endpoint *endpoint;
};
constexpr unsigned long long operator""_kbps(unsigned long long k) {
return k * 1'000;
}
constexpr unsigned long long operator""_mbps(unsigned long long m) {
return m * 1'000'000;
}
constexpr unsigned long long operator""_gbps(unsigned long long g) {
return g * 1'000'000'000;
}
struct LinkConfig {
// compute_expected_goodput computes the expected goodput with the
// given |rtt| in bits per second.
uint64_t compute_expected_goodput(Timestamp::duration rtt) const;
// delay is the one-way link delay.
Timestamp::duration delay;
// rate is the bandwidth of this link measured in bits per second
// (e.g., 10_mbps).
uint64_t rate{};
// limit is the maximum queue length of the outgoing packet measured
// in bytes.
uint64_t limit{};
// loss is the probability of losing a packet.
double loss{};
// seed is a seed value for the random number generator.
std::mt19937::result_type seed{};
};
struct EndpointConfig {
bool server{};
ngtcp2_callbacks callbacks{};
ngtcp2_settings settings{};
ngtcp2_transport_params params{};
ngtcp2_addr local_addr{};
void *user_data{};
LinkConfig link;
std::function<int(ngtcp2_conn *, const Context &)> on_write;
};
ngtcp2_callbacks default_client_callbacks();
ngtcp2_callbacks default_server_callbacks();
ngtcp2_settings default_client_settings();
ngtcp2_settings default_server_settings();
ngtcp2_transport_params default_client_transport_params();
ngtcp2_transport_params default_server_transport_params();
ngtcp2_addr default_client_addr();
ngtcp2_addr default_server_addr();
EndpointConfig default_client_endpoint_config();
EndpointConfig default_server_endpoint_config();
struct NetworkPath {
NetworkPath invert();
Address local{};
Address remote{};
};
NetworkPath to_network_path(const ngtcp2_path *path);
ngtcp2_path to_ngtcp2_path(const NetworkPath &path);
enum EventType {
EVENT_TYPE_TIMEOUT,
EVENT_TYPE_PKT,
};
struct Event {
Timestamp ts;
EventType type;
NetworkPath path;
std::vector<uint8_t> pkt;
};
constexpr bool operator>(const Event &lhs, const Event &rhs) {
return lhs.ts > rhs.ts;
}
struct TxPacket {
Timestamp departure_ts;
size_t size;
};
class Channel {
public:
Channel() = default;
Channel(const LinkConfig &config);
Channel(const Channel &) = delete;
Channel(Channel &&) noexcept;
Channel &operator=(const Channel &) = delete;
Channel &operator=(Channel &&) noexcept;
void send_pkt(const NetworkPath &path, std::span<uint8_t> pkt);
void schedule_timeout(Timestamp ts);
void set_timestamp(Timestamp ts) { ts_ = ts; }
Timestamp get_next_timestamp() const;
Event get_next_event();
void pop_tx_queue();
private:
bool decide_pkt_lost();
LinkConfig link_config_;
std::mt19937 gen_;
std::deque<TxPacket> tx_queue_;
size_t tx_queue_size_{};
Timestamp link_free_ts_;
using EventQueue =
std::priority_queue<Event, std::vector<Event>, std::greater<Event>>;
EventQueue queue_;
Timestamp timeout_{Timestamp::max()};
Timestamp ts_{};
};
class Endpoint {
public:
Endpoint();
explicit Endpoint(const EndpointConfig &config);
Endpoint(Endpoint &&endpoint) noexcept;
Endpoint(const Endpoint &) = delete;
~Endpoint();
Endpoint &operator=(const Endpoint &) = delete;
Endpoint &operator=(Endpoint &&) noexcept;
int setup_client(const ngtcp2_addr *remote_addr);
int setup_server(std::span<const uint8_t> original_dcid,
std::span<const uint8_t> client_scid, uint32_t version,
const ngtcp2_addr *remote_addr);
ngtcp2_conn *get_conn() const { return conn_; }
bool get_initialized() const { return initialized_; }
const EndpointConfig &get_endpoint_config() const { return config_; }
int on_read(const NetworkPath &path, std::span<const uint8_t> pkt,
const Context &ctx);
int on_write(const Context &ctx);
int on_timeout(const Context &ctx);
Channel &get_channel() { return channel_; }
private:
EndpointConfig config_;
WOLFSSL_CTX *ssl_ctx_{};
WOLFSSL *ssl_{};
ngtcp2_conn *conn_{};
ngtcp2_crypto_conn_ref conn_ref_{};
Channel channel_;
bool initialized_{};
};
class Simulator {
public:
Simulator(Endpoint client, Endpoint server);
Simulator(const Simulator &) = delete;
Simulator(Simulator &&) noexcept;
Simulator &operator=(const Simulator &) = delete;
Simulator &operator=(Simulator &&) noexcept;
int run();
void set_max_events(size_t n) { max_events_ = n; }
private:
Endpoint &get_opposite_endpoint(const Endpoint &ep);
std::optional<std::tuple<Event, Endpoint &>> get_next_event();
int deliver_pkt(Endpoint &remote_ep, const NetworkPath &path,
std::span<const uint8_t> pkt, Timestamp ts);
Endpoint client_;
Endpoint server_;
size_t max_events_{1'000'000};
};
class HandshakeApp {
public:
void configure(EndpointConfig &config);
bool get_handshake_confirmed() const { return handshake_confirmed_; }
private:
void handshake_confirmed() { handshake_confirmed_ = true; }
bool handshake_confirmed_{};
};
class UniStreamApp {
public:
UniStreamApp(uint64_t max_bytes);
void configure(EndpointConfig &config);
uint64_t get_bytes_sent() const { return bytes_sent_; }
Timestamp::duration get_transmit_duration() const {
return end_ts_ - start_ts_;
}
bool is_all_bytes_sent() const { return bytes_sent_ == max_bytes_; }
// compute_goodput computes goodput in bits per second.
uint64_t compute_goodput() const;
private:
void stream_close(ngtcp2_conn *conn, int64_t stream_id);
int extend_max_local_streams_uni(ngtcp2_conn *conn);
int on_write(ngtcp2_conn *conn, const Context &ctx);
int64_t stream_id_{-1};
uint64_t max_bytes_{};
uint64_t bytes_sent_{};
Timestamp start_ts_{Timestamp::max()};
Timestamp end_ts_{Timestamp::max()};
};
} // namespace ngtcp2
#endif // SIM_H

148
deps/ngtcp2/ngtcp2/examples/sim_test.cc vendored Normal file
View File

@ -0,0 +1,148 @@
/*
* ngtcp2
*
* Copyright (c) 2025 ngtcp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "sim_test.h"
#include <iostream>
#include "sim.h"
#include "util.h"
using namespace std::literals;
namespace ngtcp2 {
namespace {
const MunitTest tests[]{
munit_void_test(test_sim_handshake),
munit_void_test(test_sim_unistream),
munit_test_end(),
};
} // namespace
const MunitSuite sim_suite{
.prefix = "/sim",
.tests = tests,
};
void test_sim_handshake(void) {
struct Test {
const char *name;
Timestamp::duration delay;
};
auto tests = std::to_array<Test>({
{
.name = "short delay",
.delay = 15ms,
},
{
.name = "long delay",
.delay = 1h,
},
});
for (auto &t : tests) {
munit_logf(MUNIT_LOG_INFO, "testcase: %s", t.name);
auto link = LinkConfig{
.delay = t.delay,
};
HandshakeApp clapp;
auto cl = default_client_endpoint_config();
clapp.configure(cl);
cl.link = link;
HandshakeApp svapp;
auto sv = default_server_endpoint_config();
svapp.configure(sv);
sv.link = link;
int rv;
{
rv = Simulator{Endpoint(cl), Endpoint(sv)}.run();
}
assert_int(0, ==, rv);
assert_true(clapp.get_handshake_confirmed());
assert_true(svapp.get_handshake_confirmed());
}
}
void test_sim_unistream(void) {
struct Test {
const char *name;
double loss;
};
auto tests = std::to_array<Test>({
{
.name = "no loss",
},
{
.name = "1% loss",
.loss = 0.01,
},
});
for (auto &t : tests) {
munit_logf(MUNIT_LOG_INFO, "testcase: %s", t.name);
auto link = LinkConfig{
.delay = 15ms,
.rate = 10_mbps,
.limit = MAX_UDP_PAYLOAD_SIZE * 25,
.loss = t.loss,
.seed = munit_rand_uint32(),
};
HandshakeApp clapp;
auto cl = default_client_endpoint_config();
clapp.configure(cl);
cl.params.initial_max_streams_uni = 1;
cl.params.initial_max_stream_data_uni = 6_m;
cl.params.initial_max_data = 6_m;
cl.link = link;
UniStreamApp svapp(10_m);
auto sv = default_server_endpoint_config();
svapp.configure(sv);
sv.link = link;
int rv;
{
rv = Simulator{Endpoint(cl), Endpoint(sv)}.run();
}
assert_int(0, ==, rv);
assert_true(svapp.is_all_bytes_sent());
assert_uint64(link.compute_expected_goodput(link.delay * 2), <=,
svapp.compute_goodput());
}
}
} // namespace ngtcp2

45
deps/ngtcp2/ngtcp2/examples/sim_test.h vendored Normal file
View File

@ -0,0 +1,45 @@
/*
* ngtcp2
*
* Copyright (c) 2025 ngtcp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SIM_TEST_H
#define SIM_TEST_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif // defined(HAVE_CONFIG_H)
#define MUNIT_ENABLE_ASSERT_ALIASES
#include "munitxx.h"
namespace ngtcp2 {
extern const MunitSuite sim_suite;
munit_void_test_decl(test_sim_handshake)
munit_void_test_decl(test_sim_unistream)
} // namespace ngtcp2
#endif // !defined(SIM_TEST_H)

View File

@ -40,6 +40,7 @@
#ifndef SIPHASH_H
#define SIPHASH_H
#include <array>
#include <bit>
#include <concepts>
#include <algorithm>

View File

@ -41,20 +41,21 @@ template <std::unsigned_integral T>
return static_cast<std::make_signed_t<T>>(n);
}
// inspired by <http://blog.korfuri.fr/post/go-defer-in-cpp/>, but our
// template can take functions returning other than void.
template <typename F, typename... T> struct Defer {
Defer(F &&f, T &&...t)
: f(std::bind(std::forward<F>(f), std::forward<T>(t)...)) {}
Defer(Defer &&o) noexcept : f(std::move(o.f)) {}
template <typename F> struct Defer {
explicit Defer(F &&f) noexcept(std::is_nothrow_constructible_v<F, F &&>)
: f(std::forward<F>(f)) {}
~Defer() { f(); }
using ResultType = std::invoke_result_t<F, T...>;
std::function<ResultType()> f;
Defer(Defer &&o) = delete;
Defer(const Defer &) = delete;
Defer &operator=(const Defer &) = delete;
Defer &operator=(Defer &&) = delete;
F f;
};
template <typename F, typename... T> Defer<F, T...> defer(F &&f, T &&...t) {
return Defer<F, T...>(std::forward<F>(f), std::forward<T>(t)...);
template <typename F> [[nodiscard]] Defer<std::decay_t<F>> defer(F &&f) {
return Defer<std::decay_t<F>>(std::forward<F>(f));
}
template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) {

View File

@ -38,8 +38,6 @@
extern Config config;
TLSClientContext::TLSClientContext() : ssl_ctx_{nullptr} {}
TLSClientContext::~TLSClientContext() {
if (ssl_ctx_) {
SSL_CTX_free(ssl_ctx_);

View File

@ -33,7 +33,7 @@
class TLSClientContext {
public:
TLSClientContext();
TLSClientContext() = default;
~TLSClientContext();
int init(const char *private_key_file, const char *cert_file);
@ -43,7 +43,7 @@ public:
void enable_keylog();
private:
SSL_CTX *ssl_ctx_;
SSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_CLIENT_CONTEXT_BORINGSSL_H)

View File

@ -38,7 +38,7 @@
#include "template.h"
namespace {
auto _ = []() {
auto _ = [] {
if (ngtcp2_crypto_ossl_init() != 0) {
assert(0);
abort();
@ -50,8 +50,6 @@ auto _ = []() {
extern Config config;
TLSClientContext::TLSClientContext() : ssl_ctx_{nullptr} {}
TLSClientContext::~TLSClientContext() {
if (ssl_ctx_) {
SSL_CTX_free(ssl_ctx_);

View File

@ -33,7 +33,7 @@
class TLSClientContext {
public:
TLSClientContext();
TLSClientContext() = default;
~TLSClientContext();
int init(const char *private_key_file, const char *cert_file);
@ -43,7 +43,7 @@ public:
void enable_keylog();
private:
SSL_CTX *ssl_ctx_;
SSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_CLIENT_CONTEXT_OSSL_H)

View File

@ -92,13 +92,13 @@ ptls_cipher_suite_t *cipher_suites[] = {
} // namespace
TLSClientContext::TLSClientContext()
: ctx_{
.random_bytes = ptls_openssl_random_bytes,
.get_time = &ptls_get_time,
.key_exchanges = key_exchanges,
.cipher_suites = cipher_suites,
.require_dhe_on_psk = 1,
}, sign_cert_{} {}
: ctx_{
.random_bytes = ptls_openssl_random_bytes,
.get_time = &ptls_get_time,
.key_exchanges = key_exchanges,
.cipher_suites = cipher_suites,
.require_dhe_on_psk = 1,
} {}
TLSClientContext::~TLSClientContext() {
if (sign_cert_.key) {
@ -147,7 +147,7 @@ int TLSClientContext::load_private_key(const char *private_key_file) {
return -1;
}
auto fp_d = defer(fclose, fp);
auto fp_d = defer([fp] { fclose(fp); });
auto pkey = PEM_read_PrivateKey(fp, nullptr, nullptr, nullptr);
if (pkey == nullptr) {
@ -156,7 +156,7 @@ int TLSClientContext::load_private_key(const char *private_key_file) {
return -1;
}
auto pkey_d = defer(EVP_PKEY_free, pkey);
auto pkey_d = defer([pkey] { EVP_PKEY_free(pkey); });
if (ptls_openssl_init_sign_certificate(&sign_cert_, pkey) != 0) {
std::cerr << "ptls_openssl_init_sign_certificate failed" << std::endl;

View File

@ -47,7 +47,7 @@ private:
int load_private_key(const char *private_key_file);
ptls_context_t ctx_;
ptls_openssl_sign_certificate_t sign_cert_;
ptls_openssl_sign_certificate_t sign_cert_{};
};
#endif // !defined(TLS_CLIENT_CONTEXT_PICOTLS_H)

View File

@ -38,7 +38,7 @@
#include "template.h"
namespace {
auto _ = []() {
auto _ = [] {
if (ngtcp2_crypto_quictls_init() != 0) {
assert(0);
abort();
@ -50,8 +50,6 @@ auto _ = []() {
extern Config config;
TLSClientContext::TLSClientContext() : ssl_ctx_{nullptr} {}
TLSClientContext::~TLSClientContext() {
if (ssl_ctx_) {
SSL_CTX_free(ssl_ctx_);

View File

@ -33,7 +33,7 @@
class TLSClientContext {
public:
TLSClientContext();
TLSClientContext() = default;
~TLSClientContext();
int init(const char *private_key_file, const char *cert_file);
@ -43,7 +43,7 @@ public:
void enable_keylog();
private:
SSL_CTX *ssl_ctx_;
SSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_CLIENT_CONTEXT_QUICTLS_H)

View File

@ -39,8 +39,6 @@
extern Config config;
TLSClientContext::TLSClientContext() : ssl_ctx_{nullptr} {}
TLSClientContext::~TLSClientContext() {
if (ssl_ctx_) {
wolfSSL_CTX_free(ssl_ctx_);
@ -86,7 +84,7 @@ int new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) {
return 0;
}
auto f_d = defer(wolfSSL_BIO_free, f);
auto f_d = defer([f] { wolfSSL_BIO_free(f); });
if (!wolfSSL_PEM_write_bio(f, "WOLFSSL SESSION PARAMETERS", "", sbuffer,
sz)) {

View File

@ -35,7 +35,7 @@
class TLSClientContext {
public:
TLSClientContext();
TLSClientContext() = default;
~TLSClientContext();
int init(const char *private_key_file, const char *cert_file);
@ -45,7 +45,7 @@ public:
void enable_keylog();
private:
WOLFSSL_CTX *ssl_ctx_;
WOLFSSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_CLIENT_CONTEXT_WOLFSSL_H)

View File

@ -33,10 +33,6 @@
#include "template.h"
#include "util.h"
TLSClientSession::TLSClientSession() {}
TLSClientSession::~TLSClientSession() {}
extern Config config;
int TLSClientSession::init(bool &early_data_enabled,

View File

@ -39,8 +39,7 @@ class ClientBase;
class TLSClientSession : public TLSSessionBase {
public:
TLSClientSession();
~TLSClientSession();
TLSClientSession() = default;
int init(bool &early_data_enabled, const TLSClientContext &tls_ctx,
const char *remote_addr, ClientBase *client, uint32_t quic_version,

View File

@ -34,10 +34,6 @@
#include "template.h"
#include "util.h"
TLSClientSession::TLSClientSession() {}
TLSClientSession::~TLSClientSession() {}
extern Config config;
int TLSClientSession::init(bool &early_data_enabled,

View File

@ -39,8 +39,7 @@ class ClientBase;
class TLSClientSession : public TLSSessionBase {
public:
TLSClientSession();
~TLSClientSession();
TLSClientSession() = default;
int init(bool &early_data_enabled, const TLSClientContext &tls_ctx,
const char *remote_addr, ClientBase *client, uint32_t quic_version,

View File

@ -44,8 +44,6 @@ using namespace std::literals;
extern Config config;
TLSClientSession::TLSClientSession() {}
TLSClientSession::~TLSClientSession() {
auto &hsprops = cptls_.handshake_properties;
@ -126,7 +124,7 @@ int TLSClientSession::init(bool &early_data_enabled, TLSClientContext &tls_ctx,
std::cerr << "Could not read TLS session file " << config.session_file
<< std::endl;
} else {
auto f_d = defer(BIO_free, f);
auto f_d = defer([f] { BIO_free(f); });
char *name, *header;
unsigned char *data;

View File

@ -39,7 +39,7 @@ class ClientBase;
class TLSClientSession : public TLSSessionBase {
public:
TLSClientSession();
TLSClientSession() = default;
~TLSClientSession();
int init(bool &early_data_enabled, TLSClientContext &tls_ctx,

View File

@ -34,10 +34,6 @@
#include "template.h"
#include "util.h"
TLSClientSession::TLSClientSession() {}
TLSClientSession::~TLSClientSession() {}
extern Config config;
int TLSClientSession::init(bool &early_data_enabled,

View File

@ -39,8 +39,7 @@ class ClientBase;
class TLSClientSession : public TLSSessionBase {
public:
TLSClientSession();
~TLSClientSession();
TLSClientSession() = default;
int init(bool &early_data_enabled, const TLSClientContext &tls_ctx,
const char *remote_addr, ClientBase *client, uint32_t quic_version,

View File

@ -35,10 +35,6 @@
using namespace std::literals;
TLSClientSession::TLSClientSession() {}
TLSClientSession::~TLSClientSession() {}
extern Config config;
namespace {

View File

@ -39,8 +39,7 @@ class ClientBase;
class TLSClientSession : public TLSSessionBase {
public:
TLSClientSession();
~TLSClientSession();
TLSClientSession() = default;
int init(bool &early_data_enabled, const TLSClientContext &tls_ctx,
const char *remote_addr, ClientBase *client, uint32_t quic_version,

View File

@ -40,8 +40,6 @@
extern Config config;
TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {}
TLSServerContext::~TLSServerContext() {
if (ssl_ctx_) {
SSL_CTX_free(ssl_ctx_);
@ -228,10 +226,10 @@ int TLSServerContext::init(const char *private_key_file, const char *cert_file,
return -1;
}
auto pkey_d = defer(EVP_HPKE_KEY_free, pkey);
auto pkey_d = defer([pkey] { EVP_HPKE_KEY_free(pkey); });
auto keys = SSL_ECH_KEYS_new();
auto keys_d = defer(SSL_ECH_KEYS_free, keys);
auto keys_d = defer([keys] { SSL_ECH_KEYS_free(keys); });
if (SSL_ECH_KEYS_add(keys, 1, echconf.ech_config.data(),
echconf.ech_config.size(), pkey) != 1) {

View File

@ -37,7 +37,7 @@ using namespace ngtcp2;
class TLSServerContext {
public:
TLSServerContext();
TLSServerContext() = default;
~TLSServerContext();
int init(const char *private_key_file, const char *cert_file,
@ -48,7 +48,7 @@ public:
void enable_keylog();
private:
SSL_CTX *ssl_ctx_;
SSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_SERVER_CONTEXT_BORINGSSL_H)

View File

@ -39,7 +39,7 @@
#include "template.h"
namespace {
auto _ = []() {
auto _ = [] {
if (ngtcp2_crypto_ossl_init() != 0) {
assert(0);
abort();
@ -51,8 +51,6 @@ auto _ = []() {
extern Config config;
TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {}
TLSServerContext::~TLSServerContext() {
if (ssl_ctx_) {
SSL_CTX_free(ssl_ctx_);

View File

@ -37,7 +37,7 @@ using namespace ngtcp2;
class TLSServerContext {
public:
TLSServerContext();
TLSServerContext() = default;
~TLSServerContext();
int init(const char *private_key_file, const char *cert_file,
@ -48,7 +48,7 @@ public:
void enable_keylog();
private:
SSL_CTX *ssl_ctx_;
SSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_SERVER_CONTEXT_OSSL_H)

View File

@ -90,8 +90,6 @@ ptls_on_client_hello_t on_client_hello_hq = {on_client_hello_hq_cb};
} // namespace
namespace {
auto ticket_hmac = EVP_sha256();
std::span<const uint8_t> get_ticket_key_name() {
static std::array<uint8_t, 16> key_name;
ptls_openssl_random_bytes(key_name.data(), key_name.size());
@ -123,14 +121,21 @@ int ticket_key_cb(unsigned char *key_name, unsigned char *iv,
static const auto static_key_name = get_ticket_key_name();
static const auto static_key = get_ticket_key();
static const auto static_hmac_key = get_ticket_hmac_key();
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static const auto ticket_hmac = EVP_MD_fetch(nullptr, "sha256", nullptr);
static const auto aes_256_cbc =
EVP_CIPHER_fetch(nullptr, "AES-256-CBC", nullptr);
#else // OPENSSL_VERSION_NUMBER < 0x30000000L
static const auto ticket_hmac = EVP_sha256();
static const auto aes_256_cbc = EVP_aes_256_cbc();
#endif // OPENSSL_VERSION_NUMBER < 0x30000000L
if (enc) {
ptls_openssl_random_bytes(iv, EVP_MAX_IV_LENGTH);
std::ranges::copy(static_key_name, key_name);
if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, static_key.data(),
iv)) {
if (!EVP_EncryptInit_ex(ctx, aes_256_cbc, nullptr, static_key.data(), iv)) {
return 0;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
@ -163,8 +168,7 @@ int ticket_key_cb(unsigned char *key_name, unsigned char *iv,
return 0;
}
if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, static_key.data(),
iv)) {
if (!EVP_DecryptInit_ex(ctx, aes_256_cbc, nullptr, static_key.data(), iv)) {
return 0;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
@ -280,18 +284,16 @@ ptls_cipher_suite_t *cipher_suites[] = {
} // namespace
TLSServerContext::TLSServerContext()
: ctx_{
.random_bytes = ptls_openssl_random_bytes,
.get_time = &ptls_get_time,
.key_exchanges = key_exchanges,
.cipher_suites = cipher_suites,
.ticket_lifetime = 86400,
.require_dhe_on_psk = 1,
.server_cipher_preference = 1,
.encrypt_ticket = &encrypt_ticket,
},
sign_cert_{}
{}
: ctx_{
.random_bytes = ptls_openssl_random_bytes,
.get_time = &ptls_get_time,
.key_exchanges = key_exchanges,
.cipher_suites = cipher_suites,
.ticket_lifetime = 86400,
.require_dhe_on_psk = 1,
.server_cipher_preference = 1,
.encrypt_ticket = &encrypt_ticket,
} {}
TLSServerContext::~TLSServerContext() {
if (sign_cert_.key) {
@ -317,7 +319,7 @@ int TLSServerContext::init(const char *private_key_file, const char *cert_file,
ctx_.on_client_hello = &on_client_hello_hq;
break;
};
}
if (ngtcp2_crypto_picotls_configure_server_context(&ctx_) != 0) {
std::cerr << "ngtcp2_crypto_picotls_configure_server_context failed"
@ -349,7 +351,7 @@ int TLSServerContext::load_private_key(const char *private_key_file) {
return -1;
}
auto fp_d = defer(fclose, fp);
auto fp_d = defer([fp] { fclose(fp); });
auto pkey = PEM_read_PrivateKey(fp, nullptr, nullptr, nullptr);
if (pkey == nullptr) {
@ -358,7 +360,7 @@ int TLSServerContext::load_private_key(const char *private_key_file) {
return -1;
}
auto pkey_d = defer(EVP_PKEY_free, pkey);
auto pkey_d = defer([pkey] { EVP_PKEY_free(pkey); });
if (ptls_openssl_init_sign_certificate(&sign_cert_, pkey) != 0) {
std::cerr << "ptls_openssl_init_sign_certificate failed" << std::endl;

View File

@ -52,7 +52,7 @@ private:
int load_private_key(const char *private_key_file);
ptls_context_t ctx_;
ptls_openssl_sign_certificate_t sign_cert_;
ptls_openssl_sign_certificate_t sign_cert_{};
};
#endif // !defined(TLS_SERVER_CONTEXT_PICOTLS_H)

View File

@ -39,7 +39,7 @@
#include "template.h"
namespace {
auto _ = []() {
auto _ = [] {
if (ngtcp2_crypto_quictls_init() != 0) {
assert(0);
abort();
@ -51,8 +51,6 @@ auto _ = []() {
extern Config config;
TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {}
TLSServerContext::~TLSServerContext() {
if (ssl_ctx_) {
SSL_CTX_free(ssl_ctx_);

View File

@ -37,7 +37,7 @@ using namespace ngtcp2;
class TLSServerContext {
public:
TLSServerContext();
TLSServerContext() = default;
~TLSServerContext();
int init(const char *private_key_file, const char *cert_file,
@ -48,7 +48,7 @@ public:
void enable_keylog();
private:
SSL_CTX *ssl_ctx_;
SSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_SERVER_CONTEXT_QUICTLS_H)

View File

@ -37,8 +37,6 @@
extern Config config;
TLSServerContext::TLSServerContext() : ssl_ctx_{nullptr} {}
TLSServerContext::~TLSServerContext() {
if (ssl_ctx_) {
wolfSSL_CTX_free(ssl_ctx_);

View File

@ -38,7 +38,7 @@ using namespace ngtcp2;
class TLSServerContext {
public:
TLSServerContext();
TLSServerContext() = default;
~TLSServerContext();
int init(const char *private_key_file, const char *cert_file,
@ -49,7 +49,7 @@ public:
void enable_keylog();
private:
WOLFSSL_CTX *ssl_ctx_;
WOLFSSL_CTX *ssl_ctx_{};
};
#endif // !defined(TLS_SERVER_CONTEXT_WOLFSSL_H)

View File

@ -34,10 +34,6 @@
extern Config config;
TLSServerSession::TLSServerSession() {}
TLSServerSession::~TLSServerSession() {}
int TLSServerSession::init(const TLSServerContext &tls_ctx,
HandlerBase *handler) {
auto ssl_ctx = tls_ctx.get_native_handle();

View File

@ -36,8 +36,7 @@ class HandlerBase;
class TLSServerSession : public TLSSessionBase {
public:
TLSServerSession();
~TLSServerSession();
TLSServerSession() = default;
int init(const TLSServerContext &tls_ctx, HandlerBase *handler);
// ticket is sent automatically.

View File

@ -31,10 +31,6 @@
#include "tls_server_context_ossl.h"
#include "server_base.h"
TLSServerSession::TLSServerSession() {}
TLSServerSession::~TLSServerSession() {}
int TLSServerSession::init(const TLSServerContext &tls_ctx,
HandlerBase *handler) {
auto ssl_ctx = tls_ctx.get_native_handle();

View File

@ -36,8 +36,7 @@ class HandlerBase;
class TLSServerSession : public TLSSessionBase {
public:
TLSServerSession();
~TLSServerSession();
TLSServerSession() = default;
int init(const TLSServerContext &tls_ctx, HandlerBase *handler);
// ticket is sent automatically.

View File

@ -37,10 +37,6 @@ using namespace ngtcp2;
extern Config config;
TLSServerSession::TLSServerSession() {}
TLSServerSession::~TLSServerSession() {}
int TLSServerSession::init(TLSServerContext &tls_ctx, HandlerBase *handler) {
cptls_.ptls = ptls_server_new(tls_ctx.get_native_handle());
if (!cptls_.ptls) {

View File

@ -36,8 +36,7 @@ class HandlerBase;
class TLSServerSession : public TLSSessionBase {
public:
TLSServerSession();
~TLSServerSession();
TLSServerSession() = default;
int init(TLSServerContext &tls_ctx, HandlerBase *handler);
// ticket is sent automatically.

View File

@ -31,10 +31,6 @@
#include "tls_server_context_quictls.h"
#include "server_base.h"
TLSServerSession::TLSServerSession() {}
TLSServerSession::~TLSServerSession() {}
int TLSServerSession::init(const TLSServerContext &tls_ctx,
HandlerBase *handler) {
auto ssl_ctx = tls_ctx.get_native_handle();

View File

@ -36,8 +36,7 @@ class HandlerBase;
class TLSServerSession : public TLSSessionBase {
public:
TLSServerSession();
~TLSServerSession();
TLSServerSession() = default;
int init(const TLSServerContext &tls_ctx, HandlerBase *handler);
// ticket is sent automatically.

View File

@ -29,10 +29,6 @@
#include "tls_server_context_wolfssl.h"
#include "server_base.h"
TLSServerSession::TLSServerSession() {}
TLSServerSession::~TLSServerSession() {}
int TLSServerSession::init(const TLSServerContext &tls_ctx,
HandlerBase *handler) {
auto ssl_ctx = tls_ctx.get_native_handle();

View File

@ -36,8 +36,7 @@ class HandlerBase;
class TLSServerSession : public TLSSessionBase {
public:
TLSServerSession();
~TLSServerSession();
TLSServerSession() = default;
int init(const TLSServerContext &tls_ctx, HandlerBase *handler);
// ticket is sent automatically.

View File

@ -33,14 +33,14 @@ using namespace ngtcp2;
using namespace std::literals;
TLSSessionBase::TLSSessionBase() {
ngtcp2_crypto_ossl_ctx_new(&ossl_ctx_, NULL);
ngtcp2_crypto_ossl_ctx_new(&ossl_ctx_, nullptr);
}
TLSSessionBase::~TLSSessionBase() {
auto ssl = ngtcp2_crypto_ossl_ctx_get_ssl(ossl_ctx_);
if (ssl) {
SSL_set_app_data(ssl, NULL);
SSL_set_app_data(ssl, nullptr);
SSL_free(ssl);
}

View File

@ -32,8 +32,6 @@
using namespace ngtcp2;
using namespace std::literals;
TLSSessionBase::TLSSessionBase() : ssl_{nullptr} {}
TLSSessionBase::~TLSSessionBase() {
if (ssl_) {
SSL_free(ssl_);
@ -68,12 +66,11 @@ std::string_view TLSSessionBase::get_negotiated_group() const {
return ""sv;
}
auto key_del = defer(EVP_PKEY_free, key);
auto key_del = defer([key] { EVP_PKEY_free(key); });
auto nid = EVP_PKEY_id(key);
if (nid == EVP_PKEY_EC) {
auto ec = EVP_PKEY_get1_EC_KEY(key);
auto ec_del = defer(EC_KEY_free, ec);
auto ec = EVP_PKEY_get0_EC_KEY(key);
nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
}

View File

@ -36,7 +36,7 @@
class TLSSessionBase {
public:
TLSSessionBase();
TLSSessionBase() = default;
~TLSSessionBase();
SSL *get_native_handle() const;
@ -48,7 +48,7 @@ public:
void enable_keylog() {}
protected:
SSL *ssl_;
SSL *ssl_{};
};
#endif // !defined(TLS_SESSION_BASE_QUICTLS_H)

View File

@ -30,8 +30,6 @@
using namespace ngtcp2;
TLSSessionBase::TLSSessionBase() : ssl_{nullptr} {}
TLSSessionBase::~TLSSessionBase() {
if (ssl_) {
wolfSSL_free(ssl_);

View File

@ -38,7 +38,7 @@
class TLSSessionBase {
public:
TLSSessionBase();
TLSSessionBase() = default;
~TLSSessionBase();
WOLFSSL *get_native_handle() const;
@ -54,7 +54,7 @@ public:
void enable_keylog() {}
protected:
WOLFSSL *ssl_;
WOLFSSL *ssl_{};
};
#endif // !defined(TLS_SESSION_BASE_WOLFSSL_H)

View File

@ -35,7 +35,7 @@ namespace ngtcp2 {
namespace tls {
constexpr uint16_t CERTIFICATE_COMPRESSION_ALGO_BROTLI = 2;
inline constexpr uint16_t CERTIFICATE_COMPRESSION_ALGO_BROTLI = 2;
#ifdef HAVE_LIBBROTLI
int cert_compress(SSL *ssl, CBB *out, const uint8_t *in, size_t in_len);

View File

@ -40,6 +40,7 @@
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <chrono>
#include <array>
#include <iostream>
@ -108,7 +109,7 @@ uint64_t round2even(uint64_t n) {
} // namespace
std::string format_durationf(uint64_t ns) {
static constexpr const std::string_view units[] = {"us"sv, "ms"sv, "s"sv};
static constexpr std::string_view units[] = {"us"sv, "ms"sv, "s"sv};
if (ns < 1000) {
return format_uint(ns) + "ns";
}
@ -351,15 +352,8 @@ std::string straddr(const sockaddr *sa, socklen_t salen) {
return res;
}
uint16_t port(const sockaddr_union *su) {
switch (su->sa.sa_family) {
case AF_INET:
return ntohs(su->in.sin_port);
case AF_INET6:
return ntohs(su->in6.sin6_port);
default:
return 0;
}
std::string straddr(const Address &addr) {
return straddr(addr.as_sockaddr(), addr.size());
}
bool prohibited_port(uint16_t port) {
@ -796,7 +790,7 @@ std::optional<std::vector<uint8_t>> read_file(const std::string_view &path) {
return {};
}
auto fd_d = defer(close, fd);
auto fd_d = defer([fd] { close(fd); });
auto size = lseek(fd, 0, SEEK_END);
if (size == static_cast<off_t>(-1)) {
@ -809,13 +803,28 @@ std::optional<std::vector<uint8_t>> read_file(const std::string_view &path) {
return {};
}
auto addr_d = defer(munmap, addr, static_cast<size_t>(size));
auto addr_d =
defer([addr, size] { munmap(addr, static_cast<size_t>(size)); });
auto p = static_cast<uint8_t *>(addr);
return {{p, p + size}};
}
size_t clamp_buffer_size(ngtcp2_conn *conn, size_t buflen, size_t gso_burst) {
return std::min(gso_burst == 0
? ngtcp2_conn_get_send_quantum(conn)
: ngtcp2_conn_get_path_max_tx_udp_payload_size(conn) *
gso_burst,
buflen);
}
bool recv_pkt_time_threshold_exceeded(bool time_sensitive, ngtcp2_tstamp start,
size_t pktcnt) {
return time_sensitive && pktcnt &&
util::timestamp() - start >= NGTCP2_MILLISECONDS;
}
std::optional<ECHServerConfig>
read_ech_server_config(const std::string_view &path) {
auto pkey = read_hpke_private_key_pem(path);
@ -835,7 +844,7 @@ read_ech_server_config(const std::string_view &path) {
}
std::span<uint64_t, 2> generate_siphash_key() {
static auto key = []() {
static auto key = [] {
std::array<uint64_t, 2> key;
auto rv = generate_secure_random(as_writable_uint8_span(std::span{key}));
@ -852,6 +861,18 @@ std::span<uint64_t, 2> generate_siphash_key() {
return key;
}
std::string realpath(const char *path) {
auto cpath = ::realpath(path, nullptr);
if (!cpath) {
assert(0);
abort();
}
auto cpath_d = defer([cpath] { free(cpath); });
return cpath;
}
} // namespace util
std::ostream &operator<<(std::ostream &os, const ngtcp2_cid &cid) {

View File

@ -80,18 +80,23 @@ inline nghttp3_nv make_nv_nn(const std::string_view &name,
NGHTTP3_NV_FLAG_NO_COPY_NAME | NGHTTP3_NV_FLAG_NO_COPY_VALUE);
}
constinit const auto hexdigits = []() {
constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
inline constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
std::array<char, 512> tbl;
template <std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O format_hex_uint8(uint8_t b, O result) {
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif // __GNUC__
*result++ = LOWER_XDIGITS[b >> 4];
*result++ = LOWER_XDIGITS[b & 0xf];
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif // __GNUC__
for (size_t i = 0; i < 256; ++i) {
tbl[i * 2] = LOWER_XDIGITS[static_cast<size_t>(i >> 4)];
tbl[i * 2 + 1] = LOWER_XDIGITS[static_cast<size_t>(i & 0xf)];
}
return tbl;
}();
return result;
}
// format_hex converts a range [|first|, |last|) in hex format, and
// stores the result in another range, beginning at |result|. It
@ -102,9 +107,7 @@ requires(std::indirectly_writable<O, char> &&
sizeof(std::iter_value_t<I>) == sizeof(uint8_t))
constexpr O format_hex(I first, I last, O result) {
for (; first != last; ++first) {
result = std::ranges::copy_n(
hexdigits.data() + static_cast<uint8_t>(*first) * 2, 2, result)
.out;
result = format_hex_uint8(static_cast<uint8_t>(*first), result);
}
return result;
@ -172,7 +175,7 @@ template <std::unsigned_integral T, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O format_hex(T n, O result) {
if constexpr (sizeof(n) == 1) {
return std::ranges::copy_n(hexdigits.data() + n * 2, 2, result).out;
return format_hex_uint8(n, result);
}
if constexpr (std::endian::native == std::endian::little) {
@ -180,15 +183,14 @@ constexpr O format_hex(T n, O result) {
auto p = end + sizeof(n);
for (; p != end; --p) {
result =
std::ranges::copy_n(hexdigits.data() + *(p - 1) * 2, 2, result).out;
result = format_hex_uint8(*(p - 1), result);
}
} else {
auto p = reinterpret_cast<uint8_t *>(&n);
auto end = p + sizeof(n);
for (; p != end; ++p) {
result = std::ranges::copy_n(hexdigits.data() + *p * 2, 2, result).out;
result = format_hex_uint8(*p, result);
}
}
@ -231,29 +233,22 @@ bool numeric_host(const char *hostname, int family);
// or -1.
int hexdump(FILE *out, const void *data, size_t datalen);
static constexpr uint8_t lowcase_tbl[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
255,
};
inline constexpr auto lowcase_tbl = [] {
std::array<char, 256> tbl;
for (size_t i = 0; i < 256; ++i) {
if ('A' <= i && i <= 'Z') {
tbl[i] = static_cast<char>(i - 'A' + 'a');
} else {
tbl[i] = static_cast<char>(i);
}
}
return tbl;
}();
constexpr char lowcase(char c) noexcept {
return as_signed(lowcase_tbl[static_cast<uint8_t>(c)]);
return lowcase_tbl[static_cast<uint8_t>(c)];
}
struct CaseCmp {
@ -277,8 +272,8 @@ ngtcp2_cid make_cid_key(std::span<const uint8_t> cid);
// straddr stringifies |sa| of length |salen| in a format "[IP]:PORT".
std::string straddr(const sockaddr *sa, socklen_t salen);
// port returns port from |su|.
uint16_t port(const sockaddr_union *su);
// straddr stringifies |addr| in a format "[IP]:PORT".
std::string straddr(const Address &addr);
// prohibited_port returns true if |port| is prohibited as a client
// port.
@ -293,7 +288,7 @@ std::string_view strccalgo(ngtcp2_cc_algo cc_algo);
std::optional<std::unordered_map<std::string, std::string>>
read_mime_types(const std::string_view &filename);
constinit const auto count_digit_tbl = []() {
inline constexpr auto count_digit_tbl = [] {
std::array<uint64_t, std::numeric_limits<uint64_t>::digits10> tbl;
uint64_t x = 1;
@ -321,7 +316,7 @@ template <std::unsigned_integral T> constexpr size_t count_digit(T x) {
return y + 1;
}
constinit const auto utos_digits = []() {
inline constexpr auto utos_digits = [] {
std::array<char, 200> a;
for (size_t i = 0; i < 100; ++i) {
@ -443,7 +438,7 @@ int generate_secret(std::span<uint8_t> secret);
std::string normalize_path(const std::string_view &path);
template <std::predicate<size_t> Pred>
constexpr auto pred_tbl_gen256(Pred pred) {
consteval auto pred_tbl_gen256(Pred pred) {
std::array<bool, 256> tbl;
for (size_t i = 0; i < tbl.size(); ++i) {
@ -453,17 +448,19 @@ constexpr auto pred_tbl_gen256(Pred pred) {
return tbl;
}
constexpr auto digit_pred(size_t i) noexcept { return '0' <= i && i <= '9'; }
consteval auto digit_pred(size_t i) noexcept { return '0' <= i && i <= '9'; }
constinit const auto is_digit_tbl = pred_tbl_gen256(digit_pred);
inline constexpr auto is_digit_tbl = pred_tbl_gen256(digit_pred);
constexpr bool is_digit(char c) noexcept {
return is_digit_tbl[static_cast<uint8_t>(c)];
}
constinit const auto is_hex_digit_tbl = pred_tbl_gen256([](auto i) {
consteval auto hex_digit_pred(size_t i) noexcept {
return digit_pred(i) || ('A' <= i && i <= 'F') || ('a' <= i && i <= 'f');
});
}
inline constexpr auto is_hex_digit_tbl = pred_tbl_gen256(hex_digit_pred);
constexpr bool is_hex_digit(char c) noexcept {
return is_hex_digit_tbl[static_cast<uint8_t>(c)];
@ -478,7 +475,7 @@ constexpr bool is_hex_string(R &&r) {
return !(std::ranges::size(r) & 1) && std::ranges::all_of(r, is_hex_digit);
}
constinit const auto hex_to_uint_tbl = []() {
inline constexpr auto hex_to_uint_tbl = [] {
std::array<uint32_t, 256> tbl;
std::ranges::fill(tbl, 256);
@ -531,12 +528,17 @@ std::vector<std::string_view> split_str(const std::string_view &s,
char delim = ',');
// parse_version parses |s| to get 4 byte QUIC version. |s| must be a
// hex string and must start with "0x" (.e.g, 0x00000001).
// hex string and must start with "0x" (e.g., 0x00000001).
std::optional<uint32_t> parse_version(const std::string_view &s);
// read_file reads a file denoted by |path| and returns its content.
std::optional<std::vector<uint8_t>> read_file(const std::string_view &path);
size_t clamp_buffer_size(ngtcp2_conn *conn, size_t buflen, size_t gso_burst);
bool recv_pkt_time_threshold_exceeded(bool time_sensitive, ngtcp2_tstamp start,
size_t pktcnt);
enum HPKEPrivateKeyType : uint16_t {
HPKE_DHKEM_X25519_HKDF_SHA256 = 0x0020,
};
@ -576,6 +578,9 @@ constexpr std::string_view get_string(const std::string_view &uri,
return {uri.data() + p->off, p->len};
}
// realpath returns the canonicalized absolute path to |path|.
std::string realpath(const char *path);
} // namespace util
std::ostream &operator<<(std::ostream &os, const ngtcp2_cid &cid);
@ -585,7 +590,8 @@ std::ostream &operator<<(std::ostream &os, const ngtcp2_cid &cid);
namespace std {
template <> struct hash<ngtcp2_cid> {
hash() {
std::ranges::copy(ngtcp2::util::generate_siphash_key(), key.begin());
std::ranges::copy(ngtcp2::util::generate_siphash_key(),
std::ranges::begin(key));
}
std::size_t operator()(const ngtcp2_cid &cid) const noexcept {

View File

@ -68,10 +68,17 @@ int generate_secret(std::span<uint8_t> secret) {
return -1;
}
auto ctx_deleter = defer(EVP_MD_CTX_free, ctx);
auto ctx_deleter = defer([ctx] { EVP_MD_CTX_free(ctx); });
static const auto sha256 =
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MD_fetch(nullptr, "sha256", nullptr);
#else // OPENSSL_VERSION_NUMBER < 0x30000000L
EVP_sha256();
#endif // OPENSSL_VERSION_NUMBER < 0x30000000L
auto mdlen = static_cast<unsigned int>(secret.size());
if (!EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr) ||
if (!EVP_DigestInit_ex(ctx, sha256, nullptr) ||
!EVP_DigestUpdate(ctx, rand.data(), rand.size()) ||
!EVP_DigestFinal_ex(ctx, secret.data(), &mdlen)) {
return -1;
@ -80,10 +87,6 @@ int generate_secret(std::span<uint8_t> secret) {
return 0;
}
namespace {
void openssl_free_wrap(void *ptr) { OPENSSL_free(ptr); }
} // namespace
std::optional<HPKEPrivateKey>
read_hpke_private_key_pem(const std::string_view &filename) {
auto f = BIO_new_file(filename.data(), "r");
@ -92,7 +95,7 @@ read_hpke_private_key_pem(const std::string_view &filename) {
return {};
}
auto f_d = defer(BIO_free, f);
auto f_d = defer([f] { BIO_free(f); });
EVP_PKEY *pkey;
@ -100,7 +103,7 @@ read_hpke_private_key_pem(const std::string_view &filename) {
return {};
}
auto pkey_d = defer(EVP_PKEY_free, pkey);
auto pkey_d = defer([pkey] { EVP_PKEY_free(pkey); });
HPKEPrivateKey res;
@ -134,7 +137,7 @@ std::optional<std::vector<uint8_t>> read_pem(const std::string_view &filename,
return {};
}
auto f_d = defer(BIO_free, f);
auto f_d = defer([f] { BIO_free(f); });
for (;;) {
char *pem_type, *header;
@ -147,9 +150,11 @@ std::optional<std::vector<uint8_t>> read_pem(const std::string_view &filename,
return {};
}
auto pem_type_d = defer(openssl_free_wrap, pem_type);
auto pem_header = defer(openssl_free_wrap, header);
auto data_d = defer(openssl_free_wrap, data);
auto pem_d = defer([pem_type, header, data] {
OPENSSL_free(pem_type);
OPENSSL_free(header);
OPENSSL_free(data);
});
if (type != pem_type) {
continue;

View File

@ -419,6 +419,50 @@ void test_util_format_hex() {
assert_stdstring_equal("deadbeef"s, util::format_hex(a));
assert_stdstring_equal("deadbeef"s, util::format_hex(0xdeadbeef));
assert_stdstring_equal("beef"s, util::format_hex(a.data() + 2, 2));
std::array<char, 64> buf;
assert_stdsv_equal(
"00"sv, (std::string_view{std::ranges::begin(buf),
util::format_hex(static_cast<uint8_t>(0u),
std::ranges::begin(buf))}));
assert_stdsv_equal(
"ec"sv, (std::string_view{std::ranges::begin(buf),
util::format_hex(static_cast<uint8_t>(0xecu),
std::ranges::begin(buf))}));
assert_stdsv_equal(
"00000000"sv,
(std::string_view{std::ranges::begin(buf),
util::format_hex(0u, std::ranges::begin(buf))}));
assert_stdsv_equal(
"0000ab01"sv,
(std::string_view{std::ranges::begin(buf),
util::format_hex(0xab01u, std::ranges::begin(buf))}));
assert_stdsv_equal(
"deadbeefbaadf00d"sv,
(std::string_view{
std::ranges::begin(buf),
util::format_hex(0xdeadbeefbaadf00du, std::ranges::begin(buf))}));
assert_stdsv_equal(
"ffffffffffffffff"sv,
(std::string_view{std::ranges::begin(buf),
util::format_hex(std::numeric_limits<uint64_t>::max(),
std::ranges::begin(buf))}));
std::vector<char> char_vec;
util::format_hex(a, std::back_inserter(char_vec));
assert_stdsv_equal("deadbeef"sv,
(std::string_view{std::ranges::begin(char_vec),
std::ranges::end(char_vec)}));
std::vector<uint8_t> uint8_vec;
util::format_hex(a, std::back_inserter(uint8_vec));
assert_stdsv_equal(
"deadbeef"sv,
(std::string_view{reinterpret_cast<const char *>(uint8_vec.data()),
uint8_vec.size()}));
}
void test_util_decode_hex() {

View File

@ -87,7 +87,7 @@ std::optional<std::vector<uint8_t>> read_pem(const std::string_view &filename,
return {};
}
auto f_d = defer(wolfSSL_BIO_free, f);
auto f_d = defer([f] { wolfSSL_BIO_free(f); });
char *pem_type, *header;
unsigned char *data;
@ -98,9 +98,11 @@ std::optional<std::vector<uint8_t>> read_pem(const std::string_view &filename,
return {};
}
auto pem_type_d = defer(wolfSSL_OPENSSL_free, pem_type);
auto header_d = defer(wolfSSL_OPENSSL_free, header);
auto data_d = defer(wolfSSL_OPENSSL_free, data);
auto pem_d = defer([pem_type, header, data] {
wolfSSL_OPENSSL_free(pem_type);
wolfSSL_OPENSSL_free(header);
wolfSSL_OPENSSL_free(data);
});
if (type != pem_type) {
std::cerr << name << " file " << filename << " contains unexpected type"
@ -134,8 +136,12 @@ const char *crypto_default_ciphers() {
const char *crypto_default_groups() {
return "X25519:P-256:P-384:P-521"
#ifdef WOLFSSL_HAVE_MLKEM
# if LIBWOLFSSL_VERSION_HEX < 0x05008004
":X25519_ML_KEM_768"
#endif // WOLFSSL_HAVE_MLKEM
# else // LIBWOLFSSL_VERSION_HEX >= 0x05008004
":X25519MLKEM768"
# endif // LIBWOLFSSL_VERSION_HEX >= 0x05008004
#endif // WOLFSSL_HAVE_MLKEM
;
}

View File

@ -306,15 +306,29 @@ typedef struct ngtcp2_mem {
* @macro
*
* :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP
* datagram payload size that the local endpoint transmits.
* datagram payload size that the local endpoint transmits without
* Path MTU Discovery (PMTUD) or the custom settings (see
* :member:`ngtcp2_settings.max_tx_udp_payload_size` and
* :member:`ngtcp2_settings.no_tx_udp_payload_size_shaping`).
*/
#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200
/**
* @macro
*
* :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP
* :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE` is the maximum UDP datagram
* payload size that this library can output.
*/
#define NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE 65527
/**
* @macro
*
* :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` was the maximum UDP
* datagram payload size that Path MTU Discovery can discover.
*
* Deprecated since v1.17.0. Path MTU Discovery is not capped to this
* value anymore.
*/
#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452
@ -1561,7 +1575,8 @@ typedef struct ngtcp2_transport_params {
} ngtcp2_transport_params;
#define NGTCP2_CONN_INFO_V1 1
#define NGTCP2_CONN_INFO_VERSION NGTCP2_CONN_INFO_V1
#define NGTCP2_CONN_INFO_V2 2
#define NGTCP2_CONN_INFO_VERSION NGTCP2_CONN_INFO_V2
/**
* @struct
@ -1600,6 +1615,52 @@ typedef struct ngtcp2_conn_info {
* packets which have not been acknowledged.
*/
uint64_t bytes_in_flight;
/* The following fields have been added since NGTCP2_CONN_INFO_V2. */
/**
* :member:`pkt_sent` is the number of QUIC packets sent. This
* field has been available since v1.16.0.
*/
uint64_t pkt_sent;
/**
* :member:`bytes_sent` is the number of bytes (the sum of QUIC
* packet length) sent. This field has been available since
* v1.16.0.
*/
uint64_t bytes_sent;
/**
* :member:`pkt_recv` is the number of QUIC packets received,
* excluding discarded ones. This field has been available since
* v1.16.0.
*/
uint64_t pkt_recv;
/**
* :member:`bytes_recv` is the number of bytes (the sum of QUIC
* packet length) received, excluding discarded ones. This field
* has been available since v1.16.0.
*/
uint64_t bytes_recv;
/**
* :member:`pkt_lost` is the number of QUIC packets that are
* considered lost, excluding PMTUD packets. This field has been
* available since v1.16.0.
*/
uint64_t pkt_lost;
/**
* :member:`bytes_lost` is the number of bytes (the sum of QUIC
* packet length) lost, excluding PMTUD packets. This field has
* been available since v1.16.0.
*/
uint64_t bytes_lost;
/**
* :member:`ping_recv` is the number of PING frames received. This
* field has been available since v1.16.0.
*/
uint64_t ping_recv;
/**
* :member:`pkt_discarded` is the number of QUIC packets discarded.
* This field has been available since v1.16.0.
*/
uint64_t pkt_discarded;
} ngtcp2_conn_info;
/**
@ -1739,7 +1800,9 @@ typedef struct ngtcp2_settings {
ngtcp2_printf log_printf;
/**
* :member:`max_tx_udp_payload_size` is the maximum size of UDP
* datagram payload that the local endpoint transmits.
* datagram payload that the local endpoint transmits. This must be
* larger than or equal to :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`, and
* less then or equal to :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE`.
*/
size_t max_tx_udp_payload_size;
/**
@ -1904,12 +1967,13 @@ typedef struct ngtcp2_settings {
/**
* :member:`pmtud_probes` is the array of UDP datagram payload size
* to probe during Path MTU Discovery. The discovery is done in the
* order appeared in this array. The size must be strictly larger
* than 1200, otherwise the behavior is undefined. The maximum
* value in this array should be set to
* :member:`max_tx_udp_payload_size`. If this field is not set, the
* predefined PMTUD probes are made. This field has been available
* since v1.4.0.
* order appeared in this array. The payload size must be strictly
* larger than :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`, and less than
* or equal to :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE`. Otherwise
* the behavior is undefined. The maximum value in this array
* should be set to :member:`max_tx_udp_payload_size`. If this
* field is not set, the predefined PMTUD probes are made. This
* field has been available since v1.4.0.
*/
const uint16_t *pmtud_probes;
/**
@ -4108,9 +4172,7 @@ NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
* `ngtcp2_conn_get_expiry` returns the next expiry time. It returns
* ``UINT64_MAX`` if there is no next expiry.
*
* Call `ngtcp2_conn_handle_expiry` and then
* `ngtcp2_conn_writev_stream` (or `ngtcp2_conn_writev_datagram`) when
* the expiry time has passed.
* Call `ngtcp2_conn_handle_expiry` when the expiry time has passed.
*/
NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
@ -4118,6 +4180,20 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
* @function
*
* `ngtcp2_conn_handle_expiry` handles expired timer.
*
* If it returns :macro:`NGTCP2_ERR_IDLE_CLOSE`, it means that an idle
* timer has fired for this particular connection. In this case, drop
* the connection without calling
* `ngtcp2_conn_write_connection_close`. If it returns any of the
* other negative error codes, close the connection by sending the
* terminal packet produced by `ngtcp2_conn_write_connection_close`.
* Otherwise, schedule `ngtcp2_conn_writev_stream` call. An
* application may call any number of additional
* `ngtcp2_conn_read_pkt` and `ngtcp2_conn_handle_expiry` before
* calling `ngtcp2_conn_writev_stream`. After calling
* `ngtcp2_conn_writev_stream`, new expiry is set. The application
* should call `ngtcp2_conn_get_expiry` to get a new deadline and set
* the timer.
*/
NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
ngtcp2_tstamp ts);
@ -5503,7 +5579,9 @@ NGTCP2_EXTERN void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr,
* |destlen| could be shorten by some factors (e.g., server side
* amplification limit). This function returns
* :macro:`NGTCP2_ERR_NOBUF` if the resulting buffer is too small even
* if the given buffer has enough space.
* if the given buffer has enough space. This can happen if sending a
* packet would exceed a transmission limit (e.g., for amplification
* attack protection).
*
* This function must not be called from inside the callback
* functions.
@ -5518,7 +5596,8 @@ NGTCP2_EXTERN void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr,
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory
* :macro:`NGTCP2_ERR_NOBUF`
* Buffer is too small
* Buffer is too small or packet would exceed the transmission
* limit (e.g., for amplification attack protection).
* :macro:`NGTCP2_ERR_INVALID_STATE`
* The current state does not allow sending CONNECTION_CLOSE
* frame.
@ -5582,6 +5661,26 @@ NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn,
int64_t stream_id,
void *stream_user_data);
/**
* @function
*
* `ngtcp2_conn_get_stream_user_data` returns stream_user_data
* associated to the stream identified by |stream_id|. If the stream
* is not found, or no stream data is associated to the stream, this
* function returns NULL.
*
* The stream_user_data can be associated to the stream by one of the
* following functions:
*
* - `ngtcp2_conn_open_bidi_stream`
* - `ngtcp2_conn_open_uni_stream`
* - `ngtcp2_conn_set_stream_user_data`
*
* This function has been available since v1.17.0.
*/
NGTCP2_EXTERN void *ngtcp2_conn_get_stream_user_data(ngtcp2_conn *conn,
int64_t stream_id);
/**
* @function
*
@ -5667,14 +5766,23 @@ typedef ngtcp2_ssize (*ngtcp2_write_pkt)(ngtcp2_conn *conn, ngtcp2_path *path,
* first packet is `ngtcp2_conn_get_path_max_tx_udp_payload_size(conn)
* <ngtcp2_conn_get_path_max_tx_udp_payload_size>` bytes long. The
* application can adjust the length of the buffer to limit the number
* of packets to aggregate. If this function returns positive
* integer, all packets share the same :type:`ngtcp2_path` and
* :type:`ngtcp2_pkt_info` values, and they are assigned to the
* objects pointed by |path| and |pi| respectively. The length of all
* packets other than the last packet is assigned to |*pgsolen|. The
* length of last packet is equal to or less than |*pgsolen|.
* |write_pkt| must write a single packet. After all packets are
* written, this function calls `ngtcp2_conn_update_pkt_tx_time`.
* of packets to aggregate (or use `ngtcp2_conn_write_aggregate_pkt2`
* to control the number of packets to write directly). If this
* function returns positive integer, all packets share the same
* :type:`ngtcp2_path` and :type:`ngtcp2_pkt_info` values, and they
* are assigned to the objects pointed by |path| and |pi|
* respectively. The length of all packets other than the last packet
* is assigned to |*pgsolen|. The length of last packet is equal to
* or less than |*pgsolen|. |write_pkt| must write a single packet.
* After all packets are written, this function calls
* `ngtcp2_conn_update_pkt_tx_time`.
*
* This function is equivalent to call
* `ngtcp2_conn_write_aggregate_pkt2` with |buflen| = min(|buflen|,
* `ngtcp2_conn_get_send_quantum(conn)
* <ngtcp2_conn_get_send_quantum>`) and |num_pkts| = 0 followed by
* `ngtcp2_conn_update_pkt_tx_time(conn)
* <ngtcp2_conn_update_pkt_tx_time>`.
*
* This function returns the number of bytes written to the buffer, or
* a negative error code returned by |write_pkt|.
@ -5686,6 +5794,39 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_conn_write_aggregate_pkt2` behaves like
* `ngtcp2_conn_write_aggregate_pkt`, but it accepts |num_pkts| to
* specify the maximum number of packets to write. If |num_pkts| is
* 0, this function writes packets as much as possible. The actual
* number of packets to write is determined by the connection state
* (e.g., the congestion controller, data available to send) and the
* length of packet produced. It also does not clamp |buflen|, and
* does not call `ngtcp2_conn_update_pkt_tx_time`.
*
* This function offers more flexibility and optimization chances to
* an application. It can experiment different GSO buffer size
* strategy and number of GSO writes per event loop.
*
* This function has been available since v1.17.0.
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt2_versioned(
ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
ngtcp2_write_pkt write_pkt, size_t num_pkts, ngtcp2_tstamp ts);
/**
* @function
*
* `ngtcp2_conn_get_timestamp` returns the latest timestamp that is
* known to |conn|.
*
* This function has been available since v1.16.0.
*/
NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_timestamp(const ngtcp2_conn *conn);
/**
* @function
*
@ -5778,9 +5919,9 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps);
* * :member:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
* ``UINT64_MAX``
* * :member:`glitch_ratelim_burst
* <ngtcp2_settings.glitch_ratelim_burst>` = 1000
* <ngtcp2_settings.glitch_ratelim_burst>` = 4000
* * :member:`glitch_ratelim_rate
* <ngtcp2_settings.glitch_ratelim_rate>` = 33
* <ngtcp2_settings.glitch_ratelim_rate>` = 132
*/
NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version,
ngtcp2_settings *settings);
@ -6083,6 +6224,17 @@ NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
(CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \
(WRITE_PKT), (TS))
/*
* `ngtcp2_conn_write_aggregate_pkt2` is a wrapper around
* `ngtcp2_conn_write_aggregate_pkt2_versioned` to set the correct
* struct version.
*/
#define ngtcp2_conn_write_aggregate_pkt2(CONN, PATH, PI, BUF, BUFLEN, PGSOLEN, \
WRITE_PKT, NUM_PKTS, TS) \
ngtcp2_conn_write_aggregate_pkt2_versioned( \
(CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \
(WRITE_PKT), (NUM_PKTS), (TS))
/*
* `ngtcp2_settings_default` is a wrapper around
* `ngtcp2_settings_default_versioned` to set the correct struct

View File

@ -36,7 +36,7 @@
*
* Version number of the ngtcp2 library release.
*/
#define NGTCP2_VERSION "1.15.1"
#define NGTCP2_VERSION "1.19.0"
/**
* @macro
@ -46,6 +46,6 @@
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
* becomes 0x010203.
*/
#define NGTCP2_VERSION_NUM 0x010f01
#define NGTCP2_VERSION_NUM 0x011300
#endif /* !defined(NGTCP2_VERSION_H) */

View File

@ -34,9 +34,11 @@ ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent)
static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num,
ngtcp2_tstamp tstamp) {
ent->pkt_num = pkt_num;
ent->len = 1;
ent->tstamp = tstamp;
*ent = (ngtcp2_acktr_entry){
.pkt_num = pkt_num,
.len = 1,
.tstamp = tstamp,
};
}
int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
@ -219,8 +221,10 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
int64_t largest_ack) {
ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks.rb);
ent->largest_ack = largest_ack;
ent->pkt_num = pkt_num;
*ent = (ngtcp2_acktr_ack_entry){
.largest_ack = largest_ack,
.pkt_num = pkt_num,
};
return ent;
}
@ -333,16 +337,14 @@ void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK;
}
ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
ngtcp2_frame *fr, uint8_t type,
ngtcp2_tstamp ts,
ngtcp2_duration ack_delay,
uint64_t ack_delay_exponent) {
int ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, ngtcp2_ack *ack,
uint8_t type, ngtcp2_tstamp ts,
ngtcp2_duration ack_delay,
uint64_t ack_delay_exponent) {
int64_t last_pkt_num;
ngtcp2_ack_range *range;
ngtcp2_ksl_it it;
ngtcp2_acktr_entry *rpkt;
ngtcp2_ack *ack = &fr->ack;
ngtcp2_tstamp largest_ack_ts;
size_t num_acks;
@ -351,13 +353,13 @@ ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
}
if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) {
return NULL;
return -1;
}
it = ngtcp2_acktr_get(acktr);
if (ngtcp2_ksl_it_end(&it)) {
ngtcp2_acktr_commit_ack(acktr);
return NULL;
return -1;
}
num_acks = ngtcp2_ksl_len(&acktr->ents);
@ -420,7 +422,7 @@ ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1);
}
return fr;
return 0;
}
void ngtcp2_acktr_increase_ecn_counts(ngtcp2_acktr *acktr,

View File

@ -235,19 +235,18 @@ void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr);
/*
* ngtcp2_acktr_create_ack_frame creates ACK frame in the object
* pointed by |fr|, and returns |fr| if there are any received packets
* to acknowledge. If there are no packets to acknowledge, this
* function returns NULL. fr->ack.ranges must be able to contain at
* pointed by |ack|, and returns 0 if it successfully creates ACK
* frame in |ack|. If there are no packets to acknowledge, this
* function returns -1. |ack|->ranges must be able to contain at
* least NGTCP2_MAX_ACK_RANGES elements.
*
* Call ngtcp2_acktr_commit_ack after a created ACK frame is
* successfully serialized into a packet.
*/
ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
ngtcp2_frame *fr, uint8_t type,
ngtcp2_tstamp ts,
ngtcp2_duration ack_delay,
uint64_t ack_delay_exponent);
int ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, ngtcp2_ack *ack,
uint8_t type, ngtcp2_tstamp ts,
ngtcp2_duration ack_delay,
uint64_t ack_delay_exponent);
/*
* ngtcp2_acktr_increase_ecn_counts increases ECN counts from |pi|.

View File

@ -31,8 +31,11 @@
ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
ngtcp2_socklen addrlen) {
dest->addrlen = addrlen;
dest->addr = (ngtcp2_sockaddr *)addr;
*dest = (ngtcp2_addr){
.addr = (ngtcp2_sockaddr *)addr,
.addrlen = addrlen,
};
return dest;
}
@ -58,14 +61,12 @@ int ngtcp2_sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
switch (a->sa_family) {
case NGTCP2_AF_INET: {
const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
*bi = (const ngtcp2_sockaddr_in *)(void *)b;
const ngtcp2_sockaddr_in *ai = (void *)a, *bi = (void *)b;
return ai->sin_port == bi->sin_port &&
memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
}
case NGTCP2_AF_INET6: {
const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
*bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
const ngtcp2_sockaddr_in6 *ai = (void *)a, *bi = (void *)b;
return ai->sin6_port == bi->sin6_port &&
memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
}
@ -89,8 +90,7 @@ uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
switch (a->sa_family) {
case NGTCP2_AF_INET: {
const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
*bi = (const ngtcp2_sockaddr_in *)(void *)b;
const ngtcp2_sockaddr_in *ai = (void *)a, *bi = (void *)b;
if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
flags |= NGTCP2_ADDR_CMP_FLAG_ADDR;
}
@ -100,8 +100,7 @@ uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
return flags;
}
case NGTCP2_AF_INET6: {
const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
*bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
const ngtcp2_sockaddr_in6 *ai = (void *)a, *bi = (void *)b;
if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
flags |= NGTCP2_ADDR_CMP_FLAG_ADDR;
}

View File

@ -71,7 +71,7 @@ int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
return NGTCP2_ERR_NOMEM;
}
hd = (ngtcp2_memblock_hd *)(void *)p;
hd = (void *)p;
hd->next = balloc->head;
balloc->head = hd;
ngtcp2_buf_init(

View File

@ -190,20 +190,20 @@ static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr,
static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat);
static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr);
static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr, const ngtcp2_rs *rs);
static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
const ngtcp2_rs *rs, ngtcp2_tstamp ts);
static void bbr_note_loss(ngtcp2_cc_bbr *bbr);
static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
static uint64_t
bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
const ngtcp2_cc_pkt *pkt);
static uint64_t bbr_inflight_at_loss(ngtcp2_cc_bbr *bbr,
const ngtcp2_cc_pkt *pkt,
const ngtcp2_rs *rs);
static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
@ -334,9 +334,11 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
bbr->max_inflight = 0;
bbr->congestion_recovery_start_ts = UINT64_MAX;
bbr->bdp = 0;
bbr->undo_bw_shortterm = 0;
bbr->undo_inflight_shortterm = 0;
bbr->undo_inflight_longterm = 0;
}
static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) {
@ -384,15 +386,16 @@ static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr,
bbr->full_bw_reached = 1;
ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
"bbr reached full bandwidth, full_bw=%" PRIu64, bbr->full_bw);
ngtcp2_log_infof(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
"bbr reached full bandwidth, full_bw=%" PRIu64,
bbr->full_bw);
}
static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) {
if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 ||
(bbr->in_loss_recovery &&
bbr->round_count <= bbr->round_count_at_recovery) ||
!bbr_is_inflight_too_high(bbr)) {
!bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
return;
}
@ -402,11 +405,12 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) {
}
static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
cstat->pacing_interval_m =
cstat->pacing_interval_m = ngtcp2_max_uint64(
((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS
: cstat->smoothed_rtt)
<< 10) *
100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd;
100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd,
1);
}
static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr,
@ -567,7 +571,8 @@ static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack) {
bbr_update_round(bbr, ack);
if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) {
if (cstat->delivery_rate_sec && (cstat->delivery_rate_sec >= bbr->max_bw ||
!bbr->rst->rs.is_app_limited)) {
ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec,
bbr->cycle_count);
@ -879,7 +884,7 @@ static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr,
}
}
if (!bbr_is_inflight_too_high(bbr)) {
if (!bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
if (bbr->inflight_longterm == UINT64_MAX) {
return;
}
@ -925,17 +930,16 @@ static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd);
}
static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr) {
const ngtcp2_rs *rs = &bbr->rst->rs;
static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr, const ngtcp2_rs *rs) {
(void)bbr;
return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM >
rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER;
}
static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat,
const ngtcp2_rs *rs,
ngtcp2_tstamp ts) {
const ngtcp2_rs *rs = &bbr->rst->rs;
bbr->bw_probe_samples = 0;
if (!rs->is_app_limited) {
@ -959,7 +963,7 @@ static void bbr_note_loss(ngtcp2_cc_bbr *bbr) {
static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
ngtcp2_rs *rs = &bbr->rst->rs;
ngtcp2_rs rs = {0};
bbr_note_loss(bbr);
@ -967,22 +971,21 @@ static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
return;
}
rs->tx_in_flight = pkt->tx_in_flight;
rs.tx_in_flight = pkt->tx_in_flight;
assert(bbr->rst->lost >= pkt->lost);
rs->lost = bbr->rst->lost - pkt->lost;
rs->is_app_limited = pkt->is_app_limited;
rs.lost = bbr->rst->lost - pkt->lost;
rs.is_app_limited = pkt->is_app_limited;
if (bbr_is_inflight_too_high(bbr)) {
rs->tx_in_flight = bbr_inflight_longterm_from_lost_packet(bbr, pkt);
if (bbr_is_inflight_too_high(bbr, &rs)) {
rs.tx_in_flight = bbr_inflight_at_loss(bbr, pkt, &rs);
bbr_handle_inflight_too_high(bbr, cstat, ts);
bbr_handle_inflight_too_high(bbr, cstat, &rs, ts);
}
}
static uint64_t
bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
const ngtcp2_cc_pkt *pkt) {
ngtcp2_rs *rs = &bbr->rst->rs;
static uint64_t bbr_inflight_at_loss(ngtcp2_cc_bbr *bbr,
const ngtcp2_cc_pkt *pkt,
const ngtcp2_rs *rs) {
uint64_t inflight_prev, lost_prev, lost_prefix;
(void)bbr;
@ -1025,8 +1028,8 @@ static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack,
bbr->min_rtt = bbr->probe_rtt_min_delay;
bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp;
ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
"bbr update min_rtt=%" PRIu64, bbr->min_rtt);
ngtcp2_log_infof(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
"bbr update min_rtt=%" PRIu64, bbr->min_rtt);
}
}
@ -1098,13 +1101,8 @@ static void bbr_check_probe_rtt_done(ngtcp2_cc_bbr *bbr,
static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat) {
uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight;
if (app_limited) {
bbr->rst->app_limited = app_limited;
} else {
bbr->rst->app_limited = cstat->max_tx_udp_payload_size;
}
bbr->rst->app_limited =
ngtcp2_max_uint64(bbr->rst->delivered + cstat->bytes_in_flight, 1);
}
static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) {
@ -1279,28 +1277,15 @@ static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
static void bbr_handle_recovery(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack) {
if (bbr->in_loss_recovery) {
if (ack->largest_pkt_sent_ts != UINT64_MAX &&
!in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
bbr->in_loss_recovery = 0;
bbr->round_count_at_recovery = UINT64_MAX;
bbr_restore_cwnd(bbr, cstat);
}
if (!bbr->in_loss_recovery) {
return;
}
if (bbr->congestion_recovery_start_ts != UINT64_MAX) {
bbr->in_loss_recovery = 1;
bbr->round_count_at_recovery =
bbr->round_start ? bbr->round_count : bbr->round_count + 1;
bbr_save_cwnd(bbr, cstat);
cstat->cwnd =
cstat->bytes_in_flight +
ngtcp2_max_uint64(ack->bytes_delivered, cstat->max_tx_udp_payload_size);
cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts;
bbr->congestion_recovery_start_ts = UINT64_MAX;
if (ack->largest_pkt_sent_ts != UINT64_MAX &&
!in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
bbr->in_loss_recovery = 0;
bbr->round_count_at_recovery = UINT64_MAX;
bbr_restore_cwnd(bbr, cstat);
}
}
@ -1312,18 +1297,25 @@ static void bbr_cc_on_pkt_lost(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
}
static void bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts, uint64_t bytes_lost,
ngtcp2_tstamp sent_ts,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
(void)bytes_lost;
(void)ack;
if (bbr->in_loss_recovery ||
bbr->congestion_recovery_start_ts != UINT64_MAX ||
in_congestion_recovery(cstat, sent_ts)) {
if (bbr->in_loss_recovery || in_congestion_recovery(cstat, sent_ts)) {
return;
}
bbr->congestion_recovery_start_ts = ts;
bbr->in_loss_recovery = 1;
bbr->round_count_at_recovery =
bbr->round_start ? bbr->round_count : bbr->round_count + 1;
bbr_save_cwnd(bbr, cstat);
bbr->undo_bw_shortterm = bbr->bw_shortterm;
bbr->undo_inflight_shortterm = bbr->inflight_shortterm;
bbr->undo_inflight_longterm = bbr->inflight_longterm;
cstat->congestion_recovery_start_ts = ts;
}
static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc,
@ -1332,14 +1324,19 @@ static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc,
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
(void)ts;
bbr->congestion_recovery_start_ts = UINT64_MAX;
cstat->congestion_recovery_start_ts = UINT64_MAX;
if (bbr->in_loss_recovery) {
bbr->in_loss_recovery = 0;
bbr->round_count_at_recovery = UINT64_MAX;
bbr_restore_cwnd(bbr, cstat);
}
bbr->in_loss_recovery = 0;
bbr->round_count_at_recovery = UINT64_MAX;
bbr_reset_full_bw(bbr);
bbr->loss_in_round = 0;
bbr_restore_cwnd(bbr, cstat);
bbr->bw_shortterm =
ngtcp2_max_uint64(bbr->bw_shortterm, bbr->undo_bw_shortterm);
bbr->inflight_shortterm =
ngtcp2_max_uint64(bbr->inflight_shortterm, bbr->undo_inflight_shortterm);
bbr->inflight_longterm =
ngtcp2_max_uint64(bbr->inflight_longterm, bbr->undo_inflight_longterm);
}
static void bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
@ -1349,7 +1346,6 @@ static void bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
(void)ts;
cstat->congestion_recovery_start_ts = UINT64_MAX;
bbr->congestion_recovery_start_ts = UINT64_MAX;
bbr->in_loss_recovery = 0;
bbr->round_count_at_recovery = UINT64_MAX;
@ -1364,6 +1360,11 @@ static void bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
bbr_handle_recovery(bbr, cstat, ack);
if (ack->bytes_delivered == 0) {
return;
}
bbr_update_on_ack(bbr, cstat, ack, ts);
}

View File

@ -106,6 +106,11 @@ typedef struct ngtcp2_cc_bbr {
ngtcp2_bbr_state state;
uint64_t cwnd_gain_h;
/* Backup for spurious losses */
uint64_t undo_bw_shortterm;
uint64_t undo_inflight_shortterm;
uint64_t undo_inflight_longterm;
int loss_round_start;
uint64_t loss_round_delivered;
uint64_t rounds_since_bw_probe;
@ -130,7 +135,6 @@ typedef struct ngtcp2_cc_bbr {
int in_loss_recovery;
uint64_t round_count_at_recovery;
uint64_t max_inflight;
ngtcp2_tstamp congestion_recovery_start_ts;
uint64_t bdp;
} ngtcp2_cc_bbr;

View File

@ -26,8 +26,12 @@
#include "ngtcp2_mem.h"
void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
buf->begin = buf->pos = buf->last = begin;
buf->end = begin + len;
*buf = (ngtcp2_buf){
.begin = begin,
.end = begin + len,
.pos = begin,
.last = begin,
};
}
void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; }

View File

@ -41,6 +41,40 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
return ngtcp2_min_uint64(10 * max_udp_payload_size, n);
}
/* 1.25 is the under-utilization avoidance factor described in
https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */
#define NGTCP2_CC_PACING_GAIN_H 125
static void init_pacing_rate(ngtcp2_conn_stat *cstat) {
assert(cstat->cwnd);
cstat->pacing_interval_m = ngtcp2_max_uint64(
(NGTCP2_MILLISECONDS << 10) * 100 / NGTCP2_CC_PACING_GAIN_H / cstat->cwnd,
1);
cstat->send_quantum = 10 * cstat->max_tx_udp_payload_size;
}
static void set_pacing_rate(ngtcp2_conn_stat *cstat) {
size_t send_quantum = 64 * 1024;
assert(cstat->cwnd);
cstat->pacing_interval_m =
((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS
: cstat->smoothed_rtt)
<< 10) *
100 / NGTCP2_CC_PACING_GAIN_H / cstat->cwnd;
cstat->pacing_interval_m = ngtcp2_max_uint64(cstat->pacing_interval_m, 1);
send_quantum =
ngtcp2_min_size(send_quantum, (size_t)((NGTCP2_MILLISECONDS << 10) /
cstat->pacing_interval_m));
cstat->send_quantum =
ngtcp2_max_size(send_quantum, 10 * cstat->max_tx_udp_payload_size);
}
ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
size_t pktlen, ngtcp2_pktns_id pktns_id,
ngtcp2_tstamp sent_ts, uint64_t lost,
@ -56,9 +90,14 @@ ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
return pkt;
}
static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; }
static void reno_cc_reset(ngtcp2_cc_reno *reno, ngtcp2_conn_stat *cstat) {
reno->pending_add = 0;
void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
init_pacing_rate(cstat);
}
void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log,
ngtcp2_conn_stat *cstat) {
*reno = (ngtcp2_cc_reno){
.cc =
{
@ -70,7 +109,7 @@ void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
},
};
reno_cc_reset(reno);
reno_cc_reset(reno, cstat);
}
static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
@ -92,9 +131,12 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
if (cstat->cwnd < cstat->ssthresh) {
cstat->cwnd += pkt->pktlen;
ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
"pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
pkt->pkt_num, cstat->cwnd);
set_pacing_rate(cstat);
ngtcp2_log_infof(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
"pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
pkt->pkt_num, cstat->cwnd);
return;
}
@ -102,14 +144,17 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
reno->pending_add = m % cstat->cwnd;
cstat->cwnd += m / cstat->cwnd;
set_pacing_rate(cstat);
}
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
uint64_t bytes_lost, ngtcp2_tstamp ts) {
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
uint64_t min_cwnd;
(void)bytes_lost;
(void)ack;
if (in_congestion_recovery(cstat, sent_ts)) {
return;
@ -123,9 +168,11 @@ void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
reno->pending_add = 0;
ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
"reduce cwnd because of packet loss cwnd=%" PRIu64,
cstat->cwnd);
set_pacing_rate(cstat);
ngtcp2_log_infof(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
"reduce cwnd because of packet loss cwnd=%" PRIu64,
cstat->cwnd);
}
void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
@ -136,15 +183,16 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
cstat->congestion_recovery_start_ts = UINT64_MAX;
set_pacing_rate(cstat);
}
void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
(void)cstat;
(void)ts;
reno_cc_reset(reno);
reno_cc_reset(reno, cstat);
}
static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
@ -156,11 +204,11 @@ static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
v->app_limited_start_ts = UINT64_MAX;
v->app_limited_duration = 0;
v->pending_bytes_delivered = 0;
v->pending_est_bytes_delivered = 0;
v->pending_bytes_acked = 0;
v->pending_est_bytes_acked = 0;
}
static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
static void cubic_cc_reset(ngtcp2_cc_cubic *cubic, ngtcp2_conn_stat *cstat) {
cubic_vars_reset(&cubic->current);
cubic_vars_reset(&cubic->undo.v);
cubic->undo.cwnd = 0;
@ -174,10 +222,12 @@ static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
cubic->hs.css_round = 0;
cubic->next_round_delivered = 0;
init_pacing_rate(cstat);
}
void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
ngtcp2_rst *rst) {
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst) {
*cubic = (ngtcp2_cc_cubic){
.cc =
{
@ -191,7 +241,7 @@ void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
.rst = rst,
};
cubic_cc_reset(cubic);
cubic_cc_reset(cubic, cstat);
}
uint64_t ngtcp2_cbrt(uint64_t n) {
@ -211,7 +261,6 @@ uint64_t ngtcp2_cbrt(uint64_t n) {
y <<= 1;
b = 3 * y * (y + 1) + 1;
if (n >= b) {
n -= b;
y++;
}
@ -266,12 +315,14 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
uint64_t w_cubic, w_cubic_next;
uint64_t target, m;
uint64_t bytes_acked;
ngtcp2_duration rtt_thresh;
int round_start;
int is_app_limited =
cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited;
if (in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
if (ack->bytes_delivered == 0 ||
in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
return;
}
@ -291,9 +342,11 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cstat->cwnd += ack->bytes_delivered;
}
ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64,
ack->bytes_delivered, cstat->cwnd);
set_pacing_rate(cstat);
ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64,
ack->bytes_delivered, cstat->cwnd);
}
if (round_start) {
@ -379,35 +432,46 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
target = w_cubic_next;
}
m = ack->bytes_delivered * cstat->max_tx_udp_payload_size +
cubic->current.pending_est_bytes_delivered;
cubic->current.pending_est_bytes_delivered = m % cstat->cwnd;
bytes_acked = ack->bytes_delivered * cstat->max_tx_udp_payload_size;
m = (bytes_acked + cubic->current.pending_est_bytes_acked) / cstat->cwnd;
cubic->current.pending_est_bytes_acked += bytes_acked;
cubic->current.pending_est_bytes_acked -= m * cstat->cwnd;
assert(cubic->current.pending_est_bytes_acked < cstat->cwnd);
if (cubic->current.w_est < cubic->current.cwnd_prior) {
cubic->current.w_est += m * 9 / 17 / cstat->cwnd;
cubic->current.w_est += m * 9 / 17;
} else {
cubic->current.w_est += m / cstat->cwnd;
cubic->current.w_est += m;
}
if (cubic->current.w_est > w_cubic) {
cstat->cwnd = cubic->current.w_est;
} else {
m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size +
cubic->current.pending_bytes_delivered;
cubic->current.pending_bytes_delivered = m % cstat->cwnd;
cstat->cwnd += m / cstat->cwnd;
bytes_acked = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size;
m = (bytes_acked + cubic->current.pending_bytes_acked) / cstat->cwnd;
cubic->current.pending_bytes_acked += bytes_acked;
cubic->current.pending_bytes_acked -= m * cstat->cwnd;
assert(cubic->current.pending_bytes_acked < cstat->cwnd);
cstat->cwnd += m;
}
ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
" k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64,
ack->bytes_delivered, cstat->cwnd, cubic->current.k_m, target,
cubic->current.w_est);
set_pacing_rate(cstat);
ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
" k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64,
ack->bytes_delivered, cstat->cwnd, cubic->current.k_m,
target, cubic->current.w_est);
}
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
uint64_t bytes_lost,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
uint64_t flight_size;
@ -428,8 +492,8 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cubic->current.epoch_start = ts;
cubic->current.app_limited_start_ts = UINT64_MAX;
cubic->current.app_limited_duration = 0;
cubic->current.pending_bytes_delivered = 0;
cubic->current.pending_est_bytes_delivered = 0;
cubic->current.pending_bytes_acked = 0;
cubic->current.pending_est_bytes_acked = 0;
if (cstat->cwnd < cubic->current.w_max) {
cubic->current.w_max = cstat->cwnd * 17 / 20;
@ -443,7 +507,7 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cstat->ssthresh = cstat->cwnd * 7 / 10;
if (cubic->rst->rs.delivered * 2 < cstat->cwnd) {
flight_size = cstat->bytes_in_flight + bytes_lost;
flight_size = cstat->bytes_in_flight + ack->bytes_lost;
cstat->ssthresh = ngtcp2_min_uint64(
cstat->ssthresh,
ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size));
@ -464,9 +528,11 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cubic->current.k_m =
ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 / cstat->max_tx_udp_payload_size);
ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"reduce cwnd because of packet loss cwnd=%" PRIu64,
cstat->cwnd);
set_pacing_rate(cstat);
ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"reduce cwnd because of packet loss cwnd=%" PRIu64,
cstat->cwnd);
}
void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc,
@ -482,10 +548,12 @@ void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc,
cstat->cwnd = cubic->undo.cwnd;
cstat->ssthresh = cubic->undo.ssthresh;
ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"spurious congestion is detected and congestion state is "
"restored cwnd=%" PRIu64,
cstat->cwnd);
set_pacing_rate(cstat);
ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"spurious congestion is detected and congestion state is "
"restored cwnd=%" PRIu64,
cstat->cwnd);
}
cubic_vars_reset(&cubic->undo.v);
@ -499,17 +567,18 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
(void)ts;
cubic_cc_reset(cubic);
cubic_cc_reset(cubic, cstat);
cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
cstat->congestion_recovery_start_ts = UINT64_MAX;
set_pacing_rate(cstat);
}
void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
(void)cstat;
(void)ts;
cubic_cc_reset(cubic);
cubic_cc_reset(cubic, cstat);
}

View File

@ -88,11 +88,6 @@ typedef struct ngtcp2_cc_pkt {
* acknowledged and lost bytes.
*/
typedef struct ngtcp2_cc_ack {
/**
* :member:`prior_bytes_in_flight` is the in-flight bytes before
* processing this ACK.
*/
uint64_t prior_bytes_in_flight;
/**
* :member:`bytes_delivered` is the number of bytes acknowledged.
*/
@ -143,13 +138,16 @@ typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
* @functypedef
*
* :type:`ngtcp2_cc_congestion_event` is a callback function which is
* called when congestion event happens (e.g., when packet is lost).
* |bytes_lost| is the number of bytes lost in this congestion event.
* called when congestion event happens (e.g., when packet is lost or
* due to ECN). |ack| contains information after ACK processing.
* This callback may be called from non-ACK processing context. In
* that case, the information only taken from |ack| processing has
* default values, like 0 or UINT64_MAX;
*/
typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
uint64_t bytes_lost,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
/**
@ -186,20 +184,12 @@ typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
* @functypedef
*
* :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is
* called when an ack-eliciting packet is sent.
* called when an ack-eliciting packet is sent. The lost,
* tx_in_flight, and is_app_limited fields in |pkt| are set to 0.
*/
typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt);
/**
* @functypedef
*
* :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is
* called when new RTT sample is obtained.
*/
typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
/**
* @functypedef
*
@ -209,28 +199,6 @@ typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
/**
* @enum
*
* :type:`ngtcp2_cc_event_type` defines congestion control events.
*/
typedef enum ngtcp2_cc_event_type {
/**
* :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet
* is sent and no other ack-eliciting packet is present.
*/
NGTCP2_CC_EVENT_TYPE_TX_START
} ngtcp2_cc_event_type;
/**
* @functypedef
*
* :type:`ngtcp2_cc_event` is a callback function which is called when
* a specific event happens.
*/
typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
/**
* @struct
*
@ -254,7 +222,7 @@ typedef struct ngtcp2_cc {
ngtcp2_cc_on_pkt_lost on_pkt_lost;
/**
* :member:`congestion_event` is a callback function which is called
* when congestion event happens (.e.g, packet is lost).
* when congestion event happens (e.g., packet is lost).
*/
ngtcp2_cc_congestion_event congestion_event;
/**
@ -277,21 +245,11 @@ typedef struct ngtcp2_cc {
* ack-eliciting packet is sent.
*/
ngtcp2_cc_on_pkt_sent on_pkt_sent;
/**
* :member:`new_rtt_sample` is a callback function which is called
* when new RTT sample is obtained.
*/
ngtcp2_cc_new_rtt_sample new_rtt_sample;
/**
* :member:`reset` is a callback function which is called when
* congestion control state must be reset.
*/
ngtcp2_cc_reset reset;
/**
* :member:`event` is a callback function which is called when a
* specific event happens.
*/
ngtcp2_cc_event event;
} ngtcp2_cc;
/*
@ -310,14 +268,16 @@ typedef struct ngtcp2_cc_reno {
uint64_t pending_add;
} ngtcp2_cc_reno;
void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log);
void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log,
ngtcp2_conn_stat *cstat);
void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
uint64_t bytes_lost, ngtcp2_tstamp ts);
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
ngtcp2_conn_stat *cstat,
@ -340,8 +300,8 @@ typedef struct ngtcp2_cubic_vars {
/* app_limited_duration is the cumulative duration where a
connection is under app limited when ACK is received. */
ngtcp2_duration app_limited_duration;
uint64_t pending_bytes_delivered;
uint64_t pending_est_bytes_delivered;
uint64_t pending_bytes_acked;
uint64_t pending_est_bytes_acked;
} ngtcp2_cubic_vars;
/* ngtcp2_cc_cubic is CUBIC congestion controller. */
@ -371,14 +331,15 @@ typedef struct ngtcp2_cc_cubic {
} ngtcp2_cc_cubic;
void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cc, ngtcp2_log *log,
ngtcp2_rst *rst);
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst);
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
uint64_t bytes_lost, ngtcp2_tstamp ts);
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
ngtcp2_conn_stat *cstat,

View File

@ -58,11 +58,13 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) {
int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; }
void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) {
scid->pe.index = NGTCP2_PQ_BAD_INDEX;
scid->seq = seq;
scid->cid = *cid;
scid->retired_ts = UINT64_MAX;
scid->flags = NGTCP2_SCID_FLAG_NONE;
*scid = (ngtcp2_scid){
.pe.index = NGTCP2_PQ_BAD_INDEX,
.seq = seq,
.cid = *cid,
.retired_ts = UINT64_MAX,
.flags = NGTCP2_SCID_FLAG_NONE,
};
}
void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) {

File diff suppressed because it is too large Load Diff

View File

@ -77,10 +77,6 @@ typedef enum {
unreceived data. */
#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536
/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can
accept. */
#define NGTCP2_MAX_RETRIES 3
/* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source
connection ID the local endpoint provides to the remote endpoint.
The chosen value was described in old draft. Now a remote endpoint
@ -116,19 +112,6 @@ typedef enum {
packet as much as possible if the packet is not empty. */
#define NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY 0x08u
/*
* ngtcp2_max_frame is defined so that it covers the largest ACK
* frame.
*/
typedef union ngtcp2_max_frame {
ngtcp2_frame fr;
struct {
ngtcp2_ack ack;
/* ack includes 1 ngtcp2_ack_range. */
ngtcp2_ack_range ranges[NGTCP2_MAX_ACK_RANGES - 1];
} ackfr;
} ngtcp2_max_frame;
typedef struct ngtcp2_path_challenge_entry {
ngtcp2_path_storage ps;
uint8_t data[8];
@ -772,15 +755,9 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm);
* ack_delay included in ACK frame. |ack_delay| is actually tainted
* (sent by peer), so don't assume that |ack_delay| is always smaller
* than, or equals to |rtt|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGTCP2_ERR_INVALID_ARGUMENT
* RTT sample is ignored.
*/
int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);

View File

@ -0,0 +1,50 @@
/*
* ngtcp2
*
* Copyright (c) 2025 ngtcp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ngtcp2_conn_info.h"
#include "ngtcp2_conn_stat.h"
void ngtcp2_conn_info_init_versioned(int conn_info_version,
ngtcp2_conn_info *cinfo,
const ngtcp2_conn_stat *cstat) {
cinfo->latest_rtt = cstat->latest_rtt;
cinfo->min_rtt = cstat->min_rtt;
cinfo->smoothed_rtt = cstat->smoothed_rtt;
cinfo->rttvar = cstat->rttvar;
cinfo->cwnd = cstat->cwnd;
cinfo->ssthresh = cstat->ssthresh;
cinfo->bytes_in_flight = cstat->bytes_in_flight;
switch (conn_info_version) {
case NGTCP2_CONN_INFO_V2:
cinfo->pkt_sent = cstat->pkt_sent;
cinfo->bytes_sent = cstat->bytes_sent;
cinfo->pkt_recv = cstat->pkt_recv;
cinfo->bytes_recv = cstat->bytes_recv;
cinfo->pkt_lost = cstat->pkt_lost;
cinfo->bytes_lost = cstat->bytes_lost;
cinfo->ping_recv = cstat->ping_recv;
cinfo->pkt_discarded = cstat->pkt_discarded;
}
}

View File

@ -0,0 +1,45 @@
/*
* ngtcp2
*
* Copyright (c) 2025 ngtcp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NGTCP2_CONN_INFO_H
#define NGTCP2_CONN_INFO_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* defined(HAVE_CONFIG_H) */
#include <ngtcp2/ngtcp2.h>
typedef struct ngtcp2_conn_stat ngtcp2_conn_stat;
/*
* ngtcp2_conn_info_init_versioned initializes |cinfo| of version
* |conn_info_version| from |cstat|. This function only fills the
* fields of |cinfo| that are available in the specified version.
*/
void ngtcp2_conn_info_init_versioned(int conn_info_version,
ngtcp2_conn_info *cinfo,
const ngtcp2_conn_stat *cstat);
#endif /* !defined(NGTCP2_CONN_INFO_H) */

View File

@ -31,6 +31,8 @@
#include <ngtcp2/ngtcp2.h>
#include "ngtcp2_pktns_id.h"
/**
* @struct
*
@ -128,6 +130,43 @@ typedef struct ngtcp2_conn_stat {
* scheduled and transmitted together.
*/
size_t send_quantum;
/*
* pkt_sent is the number of QUIC packets sent.
*/
uint64_t pkt_sent;
/*
* bytes_sent is the number of bytes (the sum of QUIC packet length)
* sent.
*/
uint64_t bytes_sent;
/*
* pkt_recv is the number of QUIC packets received, excluding
* discarded ones.
*/
uint64_t pkt_recv;
/*
* bytes_recv is the number of bytes (the sum of QUIC packet length)
* received, excluding discarded ones.
*/
uint64_t bytes_recv;
/*
* pkt_lost is the number of QUIC packets that are considered lost,
* excluding PMTUD packets.
*/
uint64_t pkt_lost;
/*
* bytes_lost is the number of bytes (the sum of QUIC packet length)
* lost, excluding PMTUD packets.
*/
uint64_t bytes_lost;
/*
* ping_recv is the number of PING frames received.
*/
uint64_t ping_recv;
/*
* pkt_discarded is the number of QUIC packets discarded.
*/
uint64_t pkt_discarded;
} ngtcp2_conn_stat;
#endif /* !defined(NGTCP2_CONN_STAT_H) */

View File

@ -63,33 +63,33 @@ const uint8_t *ngtcp2_get_uint16(uint16_t *dest, const uint8_t *p) {
}
static const uint8_t *get_uvarint(uint64_t *dest, const uint8_t *p) {
union {
uint8_t n8;
uint16_t n16;
uint32_t n32;
uint64_t n64;
} n;
uint16_t n16;
uint32_t n32;
uint64_t n64;
switch (*p >> 6) {
case 0:
*dest = *p++;
return p;
case 1:
memcpy(&n, p, 2);
n.n8 &= 0x3f;
*dest = ngtcp2_ntohs(n.n16);
memcpy(&n16, p, 2);
n16 = ngtcp2_ntohs(n16);
n16 &= 0x3fff;
*dest = n16;
return p + 2;
case 2:
memcpy(&n, p, 4);
n.n8 &= 0x3f;
*dest = ngtcp2_ntohl(n.n32);
memcpy(&n32, p, 4);
n32 = ngtcp2_ntohl(n32);
n32 &= 0x3fffffff;
*dest = n32;
return p + 4;
case 3:
memcpy(&n, p, 8);
n.n8 &= 0x3f;
*dest = ngtcp2_ntohl64(n.n64);
memcpy(&n64, p, 8);
n64 = ngtcp2_ntohl64(n64);
n64 &= 0x3fffffffffffffff;
*dest = n64;
return p + 8;
default:

View File

@ -65,15 +65,21 @@ int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen,
}
p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km);
(*pckm)->secret.base = p;
(*pckm)->secret.len = secretlen;
p += secretlen;
(*pckm)->iv.base = p;
(*pckm)->iv.len = ivlen;
(*pckm)->aead_ctx.native_handle = NULL;
(*pckm)->pkt_num = -1;
(*pckm)->use_count = 0;
(*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE;
**pckm = (ngtcp2_crypto_km){
.secret =
{
.base = p,
.len = secretlen,
},
.iv =
{
.base = p + secretlen,
.len = ivlen,
},
.pkt_num = -1,
.flags = NGTCP2_CRYPTO_KM_FLAG_NONE,
};
return 0;
}

View File

@ -57,14 +57,26 @@ int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
size_t datacnt,
ngtcp2_objalloc *objalloc,
const ngtcp2_mem *mem) {
int rv;
if (datacnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) {
return ngtcp2_frame_chain_extralen_new(pfrc,
sizeof(ngtcp2_vec) * (datacnt - 1) -
NGTCP2_FRAME_CHAIN_STREAM_AVAIL,
mem);
rv = ngtcp2_frame_chain_extralen_new(
pfrc,
sizeof(ngtcp2_vec) * (datacnt - NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES),
mem);
} else {
rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
}
return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
if (rv != 0) {
return rv;
}
(*pfrc)->fr.stream.data =
(ngtcp2_vec *)(void *)((uint8_t *)*pfrc +
offsetof(ngtcp2_frame_chain, buf));
return 0;
}
int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
@ -87,9 +99,9 @@ int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
}
fr = &(*pfrc)->fr;
fr->type = NGTCP2_FRAME_NEW_TOKEN;
fr->new_token.type = NGTCP2_FRAME_NEW_TOKEN;
p = (uint8_t *)fr + sizeof(ngtcp2_new_token);
p = (uint8_t *)*pfrc + offsetof(ngtcp2_frame_chain, buf);
memcpy(p, token, tokenlen);
fr->new_token.token = p;
@ -122,7 +134,7 @@ void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
return;
}
switch (frc->fr.type) {
switch (frc->fr.hd.type) {
case NGTCP2_FRAME_CRYPTO:
case NGTCP2_FRAME_STREAM:
if (frc->fr.stream.datacnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) {

View File

@ -57,6 +57,19 @@ typedef struct ngtcp2_frame_chain_binder {
int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder,
const ngtcp2_mem *mem);
/* NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES is the number of datacnt
that changes allocation method. If datacnt is more than this
value, ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
Otherwise, it is allocated using ngtcp2_objalloc. */
#define NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES 4
/* NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES is the length of a token that
changes allocation method. If the length is more than this value,
ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
Otherwise, it is allocated using ngtcp2_objalloc. */
#define NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES \
(NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES * sizeof(ngtcp2_vec))
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/*
@ -68,6 +81,7 @@ struct ngtcp2_frame_chain {
ngtcp2_frame_chain *next;
ngtcp2_frame_chain_binder *binder;
ngtcp2_frame fr;
uint8_t buf[sizeof(ngtcp2_vec) * NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES];
};
ngtcp2_opl_entry oplent;
@ -104,26 +118,6 @@ int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
const ngtcp2_mem *mem);
/* NGTCP2_FRAME_CHAIN_STREAM_AVAIL is the number of additional bytes
available after ngtcp2_stream when it is embedded in
ngtcp2_frame. */
#define NGTCP2_FRAME_CHAIN_STREAM_AVAIL \
(sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream))
/* NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES is the number of datacnt
that changes allocation method. If datacnt is more than this
value, ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
Otherwise, it is allocated using ngtcp2_objalloc. */
#define NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES \
(NGTCP2_FRAME_CHAIN_STREAM_AVAIL / sizeof(ngtcp2_vec) + 1)
/* NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES is the length of a token that
changes allocation method. If the length is more than this value,
ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
Otherwise, it is allocated using ngtcp2_objalloc. */
#define NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES \
(sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token))
/*
* ngtcp2_frame_chain_stream_datacnt_objalloc_new allocates enough
* data to store additional |datacnt| - 1 ngtcp2_vec object after

View File

@ -35,11 +35,11 @@ void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
}
static int gaptr_gap_init(ngtcp2_gaptr *gaptr) {
ngtcp2_range range = {
.end = UINT64_MAX,
};
return ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL);
return ngtcp2_ksl_insert(&gaptr->gap, NULL,
&(ngtcp2_range){
.end = UINT64_MAX,
},
NULL);
}
void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
@ -116,10 +116,6 @@ uint64_t ngtcp2_gaptr_first_gap_offset(const ngtcp2_gaptr *gaptr) {
ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr,
uint64_t offset) {
ngtcp2_range q = {
.begin = offset,
.end = offset + 1,
};
ngtcp2_ksl_it it;
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
@ -129,7 +125,11 @@ ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr,
return r;
}
it = ngtcp2_ksl_lower_bound_search(&gaptr->gap, &q,
it = ngtcp2_ksl_lower_bound_search(&gaptr->gap,
&(ngtcp2_range){
.begin = offset,
.end = offset + 1,
},
ngtcp2_ksl_range_exclusive_search);
assert(!ngtcp2_ksl_it_end(&it));

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