From 3252779e37c0ce6262ae29398f6f4efa3774b113 Mon Sep 17 00:00:00 2001 From: Daniel Salzman <daniel.salzman@nic.cz> Date: Mon, 23 May 2022 17:19:55 +0200 Subject: [PATCH] contrib: upgrade embedded library ngtcp2 to v0.5.0 --- src/contrib/Makefile.inc | 10 + src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c | 11 +- src/contrib/libngtcp2/ngtcp2/crypto/shared.c | 370 ++- src/contrib/libngtcp2/ngtcp2/crypto/shared.h | 125 +- .../libngtcp2/ngtcp2/lib/ngtcp2_acktr.c | 56 +- .../libngtcp2/ngtcp2/lib/ngtcp2_acktr.h | 48 +- .../libngtcp2/ngtcp2/lib/ngtcp2_addr.c | 52 +- .../libngtcp2/ngtcp2/lib/ngtcp2_balloc.c | 90 + .../libngtcp2/ngtcp2/lib/ngtcp2_balloc.h | 91 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c | 6 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h | 1 + .../libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c | 16 +- .../libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h | 1 + .../libngtcp2/ngtcp2/lib/ngtcp2_conn.c | 2125 ++++++++++++----- .../libngtcp2/ngtcp2/lib/ngtcp2_conn.h | 251 +- .../libngtcp2/ngtcp2/lib/ngtcp2_conv.c | 9 +- .../libngtcp2/ngtcp2/lib/ngtcp2_conv.h | 107 +- .../libngtcp2/ngtcp2/lib/ngtcp2_crypto.c | 50 + .../libngtcp2/ngtcp2/lib/ngtcp2_crypto.h | 3 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c | 8 + .../libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c | 84 +- .../libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h | 17 +- .../libngtcp2/ngtcp2/lib/ngtcp2_idtr.c | 11 +- .../libngtcp2/ngtcp2/lib/ngtcp2_idtr.h | 8 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c | 137 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h | 52 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c | 107 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c | 60 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h | 8 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c | 70 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h | 31 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h | 136 ++ .../libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c | 40 + .../libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h | 140 ++ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c | 46 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h | 65 + .../libngtcp2/ngtcp2/lib/ngtcp2_path.c | 16 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c | 166 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h | 49 +- .../libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c | 160 ++ .../libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h | 123 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c | 4 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c | 30 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h | 4 +- .../libngtcp2/ngtcp2/lib/ngtcp2_qlog.c | 240 +- .../libngtcp2/ngtcp2/lib/ngtcp2_qlog.h | 19 +- .../libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c | 20 +- .../libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h | 22 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c | 16 +- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c | 335 ++- src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h | 181 +- .../libngtcp2/ngtcp2/lib/ngtcp2_strm.c | 99 +- .../libngtcp2/ngtcp2/lib/ngtcp2_strm.h | 141 +- .../libngtcp2/ngtcp2/lib/ngtcp2_version.c | 39 + src/contrib/libngtcp2/ngtcp2/ngtcp2.h | 995 ++++++-- src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h | 119 +- src/contrib/libngtcp2/ngtcp2/version.h | 4 +- 57 files changed, 5347 insertions(+), 1877 deletions(-) create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc index f107c4e66b..c0dc0fc7d4 100644 --- a/src/contrib/Makefile.inc +++ b/src/contrib/Makefile.inc @@ -178,6 +178,8 @@ libembngtcp2_la_SOURCES = \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c \ @@ -209,10 +211,17 @@ libembngtcp2_la_SOURCES = \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c \ @@ -238,6 +247,7 @@ libembngtcp2_la_SOURCES = \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h \ contrib/libngtcp2/ngtcp2/crypto/gnutls.c \ diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c b/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c index bc7c946b64..1f331b2c52 100644 --- a/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c +++ b/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c @@ -452,9 +452,14 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, int rv; if (datalen > 0) { - if (gnutls_handshake_write( - session, ngtcp2_crypto_gnutls_from_ngtcp2_level(crypto_level), data, - datalen) != 0) { + rv = gnutls_handshake_write( + session, ngtcp2_crypto_gnutls_from_ngtcp2_level(crypto_level), data, + datalen); + if (rv != 0) { + if (!gnutls_error_is_fatal(rv)) { + return 0; + } + gnutls_alert_send_appropriate(session, rv); return -1; } } diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/shared.c b/src/contrib/libngtcp2/ngtcp2/crypto/shared.c index 882e497802..56459f090c 100644 --- a/src/contrib/libngtcp2/ngtcp2/crypto/shared.c +++ b/src/contrib/libngtcp2/ngtcp2/crypto/shared.c @@ -35,6 +35,7 @@ #include <assert.h> #include "ngtcp2_macro.h" +#include "ngtcp2_net.h" ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, void *md_native_handle) { @@ -85,10 +86,16 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, ngtcp2_crypto_ctx_initial(&ctx); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1; saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT; + saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1; + break; + default: salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT; saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1; } @@ -126,27 +133,55 @@ size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) { } int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) { - static const uint8_t KEY_LABEL[] = "quic key"; - static const uint8_t IV_LABEL[] = "quic iv"; - static const uint8_t HP_KEY_LABEL[] = "quic hp"; + uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version, + const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen) { + static const uint8_t KEY_LABEL_V1[] = "quic key"; + static const uint8_t IV_LABEL_V1[] = "quic iv"; + static const uint8_t HP_KEY_LABEL_V1[] = "quic hp"; + static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key"; + static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv"; + static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp"; size_t keylen = ngtcp2_crypto_aead_keylen(aead); size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + const uint8_t *key_label; + size_t key_labellen; + const uint8_t *iv_label; + size_t iv_labellen; + const uint8_t *hp_key_label; + size_t hp_key_labellen; + + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + key_label = KEY_LABEL_V2_DRAFT; + key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1; + iv_label = IV_LABEL_V2_DRAFT; + iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1; + hp_key_label = HP_KEY_LABEL_V2_DRAFT; + hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1; + break; + default: + key_label = KEY_LABEL_V1; + key_labellen = sizeof(KEY_LABEL_V1) - 1; + iv_label = IV_LABEL_V1; + iv_labellen = sizeof(IV_LABEL_V1) - 1; + hp_key_label = HP_KEY_LABEL_V1; + hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1; + } if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, - KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) { + key_label, key_labellen) != 0) { return -1; } if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, - IV_LABEL, sizeof(IV_LABEL) - 1) != 0) { + iv_label, iv_labellen) != 0) { return -1; } - if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label( - hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL, - sizeof(HP_KEY_LABEL) - 1) != 0) { + if (hp_key != NULL && + ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen, + hp_key_label, hp_key_labellen) != 0) { return -1; } @@ -198,11 +233,22 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, hp_key = hp_keybuf; } - if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: ngtcp2_crypto_ctx_tls_early(&cctx, tls); ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); ctx = ngtcp2_conn_get_early_crypto_ctx(conn); - } else { + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + if (ngtcp2_conn_is_server(conn) && + !ngtcp2_conn_get_negotiated_version(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return -1; + } + } + /* fall through */ + default: ctx = ngtcp2_conn_get_crypto_ctx(conn); if (!ctx->aead.native_handle) { @@ -217,8 +263,9 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, hp = &ctx->hp; ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + key, iv, hp_key, ngtcp2_conn_get_negotiated_version(conn), aead, md, + secret, secretlen) != 0) { return -1; } @@ -332,11 +379,22 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, hp_key = hp_keybuf; } - if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: ngtcp2_crypto_ctx_tls_early(&cctx, tls); ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); ctx = ngtcp2_conn_get_early_crypto_ctx(conn); - } else { + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + if (ngtcp2_conn_is_server(conn) && + !ngtcp2_conn_get_negotiated_version(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return -1; + } + } + /* fall through */ + default: ctx = ngtcp2_conn_get_crypto_ctx(conn); if (!ctx->aead.native_handle) { @@ -351,8 +409,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, hp = &ctx->hp; ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + key, iv, hp_key, ngtcp2_conn_get_negotiated_version(conn), aead, md, + secret, secretlen) != 0) { return -1; } @@ -378,15 +437,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, goto fail; } - if (ngtcp2_conn_is_server(conn)) { - rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); - if (rv != 0) { - return rv; - } - - if (crypto_set_local_transport_params(conn, tls) != 0) { - return rv; - } + if (ngtcp2_conn_is_server(conn) && + crypto_set_local_transport_params(conn, tls) != 0) { + goto fail; } break; @@ -415,7 +468,7 @@ int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, - const ngtcp2_cid *client_dcid) { + uint32_t version, const ngtcp2_cid *client_dcid) { uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; @@ -434,7 +487,6 @@ int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_crypto_aead_ctx retry_aead_ctx = {0}; int rv; int server = ngtcp2_conn_is_server(conn); - uint32_t version = ngtcp2_conn_get_negotiated_version(conn); const uint8_t *retry_key; size_t retry_noncelen; @@ -479,13 +531,13 @@ int ngtcp2_crypto_derive_and_install_initial_key( } if (ngtcp2_crypto_derive_packet_protection_key( - rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret, + rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } @@ -513,10 +565,16 @@ int ngtcp2_crypto_derive_and_install_initial_key( if (!server && !ngtcp2_conn_after_retry(conn)) { ngtcp2_crypto_aead_retry(&retry_aead); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT; + retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -550,6 +608,114 @@ fail: return -1; } +int ngtcp2_crypto_derive_and_install_vneg_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, + uint32_t version, const ngtcp2_cid *client_dcid) { + uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn); + ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; + ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; + int rv; + int server = ngtcp2_conn_is_server(conn); + + if (!rx_secret) { + rx_secret = rx_secretbuf; + } + if (!tx_secret) { + tx_secret = tx_secretbuf; + } + if (!initial_secret) { + initial_secret = initial_secretbuf; + } + + if (!rx_key) { + rx_key = rx_keybuf; + } + if (!rx_iv) { + rx_iv = rx_ivbuf; + } + if (!rx_hp_key) { + rx_hp_key = rx_hp_keybuf; + } + if (!tx_key) { + tx_key = tx_keybuf; + } + if (!tx_iv) { + tx_iv = tx_ivbuf; + } + if (!tx_hp_key) { + tx_hp_key = tx_hp_keybuf; + } + + if (ngtcp2_crypto_derive_initial_secrets( + version, rx_secret, tx_secret, initial_secret, client_dcid, + server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != + 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) != + 0) { + goto fail; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) != + 0) { + goto fail; + } + + rv = ngtcp2_conn_install_vneg_initial_key( + conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv, + &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); + ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); + + return -1; +} + int ngtcp2_crypto_update_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, @@ -560,14 +726,15 @@ int ngtcp2_crypto_update_key( const ngtcp2_crypto_aead *aead = &ctx->aead; const ngtcp2_crypto_md *md = &ctx->md; size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + uint32_t version = ngtcp2_conn_get_negotiated_version(conn); if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret, secretlen) != 0) { return -1; } - if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md, - rx_secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) { return -1; } @@ -576,8 +743,8 @@ int ngtcp2_crypto_update_key( return -1; } - if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md, - tx_secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) { return -1; } @@ -711,12 +878,14 @@ static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv, return 0; } -static size_t crypto_generate_retry_token_aad(uint8_t *dest, - const struct sockaddr *sa, - size_t salen, +static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version, + const ngtcp2_sockaddr *sa, + ngtcp2_socklen salen, const ngtcp2_cid *retry_scid) { uint8_t *p = dest; + version = ngtcp2_htonl(version); + memcpy(p, &version, sizeof(version)); memcpy(p, sa, salen); p += salen; memcpy(p, retry_scid->data, retry_scid->datalen); @@ -728,8 +897,8 @@ static size_t crypto_generate_retry_token_aad(uint8_t *dest, static const uint8_t retry_token_info_prefix[] = "retry_token"; ngtcp2_ssize ngtcp2_crypto_generate_retry_token( - uint8_t *token, const uint8_t *secret, size_t secretlen, - const struct sockaddr *remote_addr, size_t remote_addrlen, + uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) { uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN]; uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; @@ -741,9 +910,11 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token( ngtcp2_crypto_md md; ngtcp2_crypto_aead_ctx aead_ctx; size_t plaintextlen; - uint8_t aad[sizeof(struct sockaddr_storage) + NGTCP2_MAX_CIDLEN]; + uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) + + NGTCP2_MAX_CIDLEN]; size_t aadlen; uint8_t *p = plaintext; + ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); int rv; memset(plaintext, 0, sizeof(plaintext)); @@ -751,9 +922,8 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token( *p++ = (uint8_t)odcid->datalen; memcpy(p, odcid->data, odcid->datalen); p += NGTCP2_MAX_CIDLEN; - /* Host byte order */ - memcpy(p, &ts, sizeof(ts)); - p += sizeof(ts); + memcpy(p, &ts_be, sizeof(ts_be)); + p += sizeof(ts_be); plaintextlen = (size_t)(p - plaintext); @@ -777,8 +947,8 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token( return -1; } - aadlen = crypto_generate_retry_token_aad(aad, remote_addr, remote_addrlen, - retry_scid); + aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, + remote_addrlen, retry_scid); p = token; *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY; @@ -805,9 +975,9 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token( int ngtcp2_crypto_verify_retry_token( ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, - const uint8_t *secret, size_t secretlen, const struct sockaddr *remote_addr, - socklen_t remote_addrlen, const ngtcp2_cid *dcid, ngtcp2_duration timeout, - ngtcp2_tstamp ts) { + const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) { uint8_t plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)]; uint8_t key[32]; @@ -817,7 +987,8 @@ int ngtcp2_crypto_verify_retry_token( ngtcp2_crypto_aead_ctx aead_ctx; ngtcp2_crypto_aead aead; ngtcp2_crypto_md md; - uint8_t aad[sizeof(struct sockaddr_storage) + NGTCP2_MAX_CIDLEN]; + uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) + + NGTCP2_MAX_CIDLEN]; size_t aadlen; const uint8_t *rand_data; const uint8_t *ciphertext; @@ -848,8 +1019,8 @@ int ngtcp2_crypto_verify_retry_token( return -1; } - aadlen = - crypto_generate_retry_token_aad(aad, remote_addr, remote_addrlen, dcid); + aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, + remote_addrlen, dcid); if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { return -1; @@ -868,10 +1039,10 @@ int ngtcp2_crypto_verify_retry_token( assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN)); - /* Host byte order */ memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN, sizeof(gen_ts)); + gen_ts = ngtcp2_ntohl64(gen_ts); if (gen_ts + timeout <= ts) { return -1; } @@ -882,19 +1053,19 @@ int ngtcp2_crypto_verify_retry_token( } static size_t crypto_generate_regular_token_aad(uint8_t *dest, - const struct sockaddr *sa) { + const ngtcp2_sockaddr *sa) { const uint8_t *addr; size_t addrlen; switch (sa->sa_family) { case AF_INET: - addr = (const uint8_t *)&((const struct sockaddr_in *)(void *)sa)->sin_addr; - addrlen = sizeof(((const struct sockaddr_in *)(void *)sa)->sin_addr); + addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr; + addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr); break; case AF_INET6: addr = - (const uint8_t *)&((const struct sockaddr_in6 *)(void *)sa)->sin6_addr; - addrlen = sizeof(((const struct sockaddr_in6 *)(void *)sa)->sin6_addr); + (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr; + addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr); break; default: assert(0); @@ -908,11 +1079,10 @@ static size_t crypto_generate_regular_token_aad(uint8_t *dest, static const uint8_t regular_token_info_prefix[] = "regular_token"; -ngtcp2_ssize -ngtcp2_crypto_generate_regular_token(uint8_t *token, const uint8_t *secret, - size_t secretlen, - const struct sockaddr *remote_addr, - size_t remote_addrlen, ngtcp2_tstamp ts) { +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) { uint8_t plaintext[sizeof(ngtcp2_tstamp)]; uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; uint8_t key[32]; @@ -923,15 +1093,15 @@ ngtcp2_crypto_generate_regular_token(uint8_t *token, const uint8_t *secret, ngtcp2_crypto_md md; ngtcp2_crypto_aead_ctx aead_ctx; size_t plaintextlen; - uint8_t aad[sizeof(struct sockaddr_in6)]; + uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; size_t aadlen; uint8_t *p = plaintext; + ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); int rv; (void)remote_addrlen; - /* Host byte order */ - memcpy(p, &ts, sizeof(ts)); - p += sizeof(ts); + memcpy(p, &ts_be, sizeof(ts_be)); + p += sizeof(ts_be); plaintextlen = (size_t)(p - plaintext); @@ -982,8 +1152,8 @@ ngtcp2_crypto_generate_regular_token(uint8_t *token, const uint8_t *secret, int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen, const uint8_t *secret, size_t secretlen, - const struct sockaddr *remote_addr, - socklen_t remote_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts) { uint8_t plaintext[sizeof(ngtcp2_tstamp)]; @@ -994,7 +1164,7 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen, ngtcp2_crypto_aead_ctx aead_ctx; ngtcp2_crypto_aead aead; ngtcp2_crypto_md md; - uint8_t aad[sizeof(struct sockaddr_in6)]; + uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; size_t aadlen; const uint8_t *rand_data; const uint8_t *ciphertext; @@ -1040,9 +1210,9 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen, return -1; } - /* Host byte order */ memcpy(&gen_ts, plaintext, sizeof(gen_ts)); + gen_ts = ngtcp2_ntohl64(gen_ts); if (gen_ts + timeout <= ts) { return -1; } @@ -1074,7 +1244,7 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close( } if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } @@ -1118,10 +1288,16 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, ngtcp2_crypto_aead_retry(&aead); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -1143,22 +1319,14 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, return spktlen; } -/* - * crypto_setup_initial_crypto establishes the initial secrets and - * encryption keys, and prepares local QUIC transport parameters. - */ -static int crypto_setup_initial_crypto(ngtcp2_conn *conn, - const ngtcp2_cid *dcid) { - return ngtcp2_crypto_derive_and_install_initial_key( - conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid); -} - int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) { const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn); void *tls = ngtcp2_conn_get_tls_native_handle(conn); (void)user_data; - if (crypto_setup_initial_crypto(conn, dcid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1178,9 +1346,9 @@ int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, void *user_data) { (void)user_data; - if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &hd->scid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1192,7 +1360,23 @@ int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, void *user_data) { (void)user_data; - if (crypto_setup_initial_crypto(conn, dcid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data) { + (void)user_data; + + if (ngtcp2_crypto_derive_and_install_vneg_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version, + client_dcid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/shared.h b/src/contrib/libngtcp2/ngtcp2/crypto/shared.h index d282fd0057..02b948901a 100644 --- a/src/contrib/libngtcp2/ngtcp2/crypto/shared.h +++ b/src/contrib/libngtcp2/ngtcp2/crypto/shared.h @@ -51,6 +51,16 @@ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \ "\x7f\x0a" +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to + * derive initial secret. It is used for QUIC v2 draft. + */ +#define NGTCP2_INITIAL_SALT_V2_DRAFT \ + "\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3" \ + "\xe5\xd3" + /* Maximum key usage (encryption) limits */ #define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23) #define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62) @@ -62,6 +72,40 @@ #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36) #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL) +/** + * @function + * + * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet + * encryption and decryption. + */ +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_aead_init` initializes |aead| with the provided + * |aead_native_handle| which is an underlying AEAD object. + * + * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be + * a pointer to EVP_CIPHER. + * + * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be + * gnutls_cipher_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must + * be a pointer to EVP_AEAD. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + /** * @function * @@ -84,6 +128,34 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, const ngtcp2_cid *client_dcid, ngtcp2_crypto_side side); +/** + * @function + * + * `ngtcp2_crypto_derive_packet_protection_key` derives packet + * protection key. This function writes packet protection key into + * the buffer pointed by |key|. The length of derived key is + * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>` + * bytes. |key| must have enough capacity to store the key. This + * function writes packet protection IV into |iv|. The length of + * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead) + * <ngtcp2_crypto_packet_protection_ivlen>` bytes. |iv| must have + * enough capacity to store the IV. + * + * If |hp| is not NULL, this function also derives packet header + * protection key and writes the key into the buffer pointed by |hp|. + * The length of derived key is `ngtcp2_crypto_aead_keylen(aead) + * <ngtcp2_crypto_aead_keylen>` bytes. |hp|, if not NULL, must have + * enough capacity to store the key. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv, + uint8_t *hp, uint32_t version, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen); + /** * @function * @@ -183,7 +255,58 @@ int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls); int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, - uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, + const ngtcp2_cid *client_dcid); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial + * keying materials and installs keys to |conn|. This function is + * dedicated to install keys for |version| which is negotiated, or + * being negotiated. + * + * If |rx_secret| is not NULL, the secret for decryption is written to + * the buffer pointed by |rx_secret|. The length of secret is 32 + * bytes, and |rx_secret| must point to the buffer which has enough + * capacity. + * + * If |tx_secret| is not NULL, the secret for encryption is written to + * the buffer pointed by |tx_secret|. The length of secret is 32 + * bytes, and |tx_secret| must point to the buffer which has enough + * capacity. + * + * If |initial_secret| is not NULL, the initial secret is written to + * the buffer pointed by |initial_secret|. The length of secret is 32 + * bytes, and |initial_secret| must point to the buffer which has + * enough capacity. + * + * |client_dcid| is the destination connection ID in first Initial + * packet of client. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|. If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|. If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * The length of packet protection key and header protection key is 16 + * bytes long. The length of packet protection IV is 12 bytes long. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_and_install_vneg_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, const ngtcp2_cid *client_dcid); /** diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c index 6d646517a8..02c45de90d 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c @@ -26,22 +26,31 @@ #include <assert.h> -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) { - *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); +#include "ngtcp2_macro.h" + +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; +} + +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, + ngtcp2_objalloc *objalloc) { + *ent = ngtcp2_objalloc_acktr_entry_get(objalloc); if (*ent == NULL) { return NGTCP2_ERR_NOMEM; } - (*ent)->pkt_num = pkt_num; - (*ent)->len = 1; - (*ent)->tstamp = tstamp; + acktr_entry_init(*ent, pkt_num, tstamp); return 0; } -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, ent); +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, + ngtcp2_objalloc *objalloc) { + ngtcp2_objalloc_acktr_entry_release(objalloc, ent); } static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { @@ -52,17 +61,15 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, const ngtcp2_mem *mem) { int rv; - rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry), + ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem); + + rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry), mem); if (rv != 0) { return rv; } - rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); - if (rv != 0) { - ngtcp2_ringbuf_free(&acktr->acks); - return rv; - } + ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); acktr->log = log; acktr->mem = mem; @@ -74,19 +81,26 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, } void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { +#ifdef NOMEMPOOL ngtcp2_ksl_it it; +#endif /* NOMEMPOOL */ if (acktr == NULL) { return; } +#ifdef NOMEMPOOL for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc); } +#endif /* NOMEMPOOL */ + ngtcp2_ksl_free(&acktr->ents); ngtcp2_ringbuf_free(&acktr->acks); + + ngtcp2_objalloc_free(&acktr->objalloc); } int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, @@ -132,7 +146,7 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { prev_ent->len += ent->len + 1; ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); added = 1; } else { ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); @@ -150,13 +164,13 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, } if (!added) { - rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem); + rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc); if (rv != 0) { return rv; } rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent); if (rv != 0) { - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); return rv; } } @@ -173,7 +187,7 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, ngtcp2_ksl_it_prev(&it); delent = ngtcp2_ksl_it_get(&it); ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num); - ngtcp2_acktr_entry_del(delent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc); } return 0; @@ -188,7 +202,7 @@ void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { for (; !ngtcp2_ksl_it_end(&it);) { ent = ngtcp2_ksl_it_get(&it); ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); } } @@ -220,7 +234,7 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, ngtcp2_acktr_entry *ent) { ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); } static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h index 40a4a4e25e..2fe9dad530 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h @@ -35,6 +35,7 @@ #include "ngtcp2_ringbuf.h" #include "ngtcp2_ksl.h" #include "ngtcp2_pkt.h" +#include "ngtcp2_objalloc.h" /* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry which ngtcp2_acktr stores. */ @@ -46,22 +47,30 @@ typedef struct ngtcp2_log ngtcp2_log; * ngtcp2_acktr_entry is a range of packets which need to be acked. */ typedef struct ngtcp2_acktr_entry { - /* pkt_num is the largest packet number to acknowledge in this - range. */ - int64_t pkt_num; - /* len is the consecutive packets started from pkt_num which - includes pkt_num itself counting in decreasing order. So pkt_num - = 987 and len = 2, this entry includes packet 987 and 986. */ - size_t len; - /* tstamp is the timestamp when a packet denoted by pkt_num is - received. */ - ngtcp2_tstamp tstamp; + union { + struct { + /* pkt_num is the largest packet number to acknowledge in this + range. */ + int64_t pkt_num; + /* len is the consecutive packets started from pkt_num which + includes pkt_num itself counting in decreasing order. So pkt_num + = 987 and len = 2, this entry includes packet 987 and 986. */ + size_t len; + /* tstamp is the timestamp when a packet denoted by pkt_num is + received. */ + ngtcp2_tstamp tstamp; + }; + + ngtcp2_opl_entry oplent; + }; } ngtcp2_acktr_entry; +ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent); + /* - * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it - * with the given parameters. The pointer to the allocated object is - * stored to |*ent|. + * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and + * initializes it with the given parameters. The pointer to the + * allocated object is stored to |*ent|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -69,14 +78,16 @@ typedef struct ngtcp2_acktr_entry { * NGTCP2_ERR_NOMEM * Out of memory. */ -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem); +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, + ngtcp2_objalloc *objalloc); /* - * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It - * deallocates memory pointed by |ent|. + * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for + * |ent|. */ -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem); +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, + ngtcp2_objalloc *objalloc); typedef struct ngtcp2_acktr_ack_entry { /* largest_ack is the largest packet number in outgoing ACK frame */ @@ -101,6 +112,7 @@ typedef struct ngtcp2_acktr_ack_entry { * ngtcp2_acktr tracks received packets which we have to send ack. */ typedef struct ngtcp2_acktr { + ngtcp2_objalloc objalloc; ngtcp2_ringbuf acks; /* ents includes ngtcp2_acktr_entry sorted by decreasing order of packet number. */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c index 27197a694d..e052a9036c 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c @@ -27,22 +27,10 @@ #include <string.h> #include <assert.h> -#ifdef WIN32 -# include <winsock2.h> -# include <ws2tcpip.h> -#else -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> -# include <netinet/ip.h> -# include <netinet/tcp.h> -# include <arpa/inet.h> -#endif - -ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen) { +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen) { dest->addrlen = addrlen; - dest->addr = (struct sockaddr *)addr; + dest->addr = (ngtcp2_sockaddr *)addr; return dest; } @@ -53,27 +41,27 @@ void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) { } } -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen) { +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen) { dest->addrlen = addrlen; if (addrlen) { memcpy(dest->addr, addr, addrlen); } } -static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) { +static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { assert(a->sa_family == b->sa_family); switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; + case NGTCP2_AF_INET: { + const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, + *bi = (const ngtcp2_sockaddr_in *)(void *)b; return ai->sin_port == bi->sin_port && memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0; } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; + case NGTCP2_AF_INET6: { + const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, + *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; return ai->sin6_port == bi->sin6_port && memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0; } @@ -90,17 +78,17 @@ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE; - const struct sockaddr *a = aa->addr; - const struct sockaddr *b = bb->addr; + const ngtcp2_sockaddr *a = aa->addr; + const ngtcp2_sockaddr *b = bb->addr; if (a->sa_family != b->sa_family) { return NGTCP2_ADDR_COMPARE_FLAG_FAMILY; } switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; + case NGTCP2_AF_INET: { + const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, + *bi = (const ngtcp2_sockaddr_in *)(void *)b; if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) { flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; } @@ -109,9 +97,9 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { } return flags; } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; + case NGTCP2_AF_INET6: { + const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, + *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) { flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c new file mode 100644 index 0000000000..5623e56566 --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c @@ -0,0 +1,90 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_balloc.h" + +#include <assert.h> + +#include "ngtcp2_mem.h" + +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, + const ngtcp2_mem *mem) { + assert(((uintptr_t)blklen & 0xfllu) == 0); + + balloc->mem = mem; + balloc->blklen = blklen; + balloc->head = NULL; + ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +void ngtcp2_balloc_free(ngtcp2_balloc *balloc) { + if (balloc == NULL) { + return; + } + + ngtcp2_balloc_clear(balloc); +} + +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) { + ngtcp2_memblock_hd *p, *next; + + for (p = balloc->head; p; p = next) { + next = p->next; + ngtcp2_mem_free(balloc->mem, p); + } + + balloc->head = NULL; + ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) { + uint8_t *p; + ngtcp2_memblock_hd *hd; + + assert(n <= balloc->blklen); + + if (ngtcp2_buf_left(&balloc->buf) < n) { + p = ngtcp2_mem_malloc(balloc->mem, sizeof(ngtcp2_memblock_hd) + 0x10llu + + balloc->blklen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + + hd = (ngtcp2_memblock_hd *)(void *)p; + hd->next = balloc->head; + balloc->head = hd; + ngtcp2_buf_init( + &balloc->buf, + (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfllu) & + ~0xfllu), + balloc->blklen); + } + + assert(((uintptr_t)balloc->buf.last & 0xfllu) == 0); + + *pbuf = balloc->buf.last; + balloc->buf.last += (n + 0xfllu) & ~0xfllu; + + return 0; +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h new file mode 100644 index 0000000000..1fb16325d9 --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h @@ -0,0 +1,91 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_BALLOC_H +#define NGTCP2_BALLOC_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_buf.h" + +typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd; + +/* + * ngtcp2_memblock_hd is the header of memory block. + */ +struct ngtcp2_memblock_hd { + ngtcp2_memblock_hd *next; +}; + +/* + * ngtcp2_balloc is a custom memory allocator. It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct ngtcp2_balloc { + /* mem is the underlying memory allocator. */ + const ngtcp2_mem *mem; + /* blklen is the size of memory block. */ + size_t blklen; + /* head points to the list of memory block allocated so far. */ + ngtcp2_memblock_hd *head; + /* buf wraps the current memory block for allocation requests. */ + ngtcp2_buf buf; +} ngtcp2_balloc; + +/* + * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_balloc_free releases all allocated memory blocks. + */ +void ngtcp2_balloc_free(ngtcp2_balloc *balloc); + +/* + * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n); + +/* + * ngtcp2_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc); + +#endif /* NGTCP2_BALLOC_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c index 082fb740e7..0816d69b81 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -291,7 +291,7 @@ static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) { static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_ack *ack) { if (cc->in_loss_recovery) { - if (cc->round_start) { + if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) { cc->packet_conservation = 0; } @@ -313,6 +313,7 @@ static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts; cc->congestion_recovery_start_ts = UINT64_MAX; cc->packet_conservation = 1; + cc->congestion_recovery_next_round_delivered = cc->rst->delivered; } } @@ -412,7 +413,7 @@ static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc, if (ack->bytes_lost > 0) { if (cstat->cwnd > ack->bytes_lost) { cstat->cwnd -= ack->bytes_lost; - cstat->cwnd = ngtcp2_max(cstat->cwnd, cstat->max_udp_payload_size); + cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size); } else { cstat->cwnd = cstat->max_udp_payload_size; } @@ -487,6 +488,7 @@ static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, cc->probe_rtt_round_done = 0; cc->congestion_recovery_start_ts = UINT64_MAX; + cc->congestion_recovery_next_round_delivered = 0; cc->in_loss_recovery = 0; cstat->send_quantum = cstat->max_udp_payload_size * 10; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h index 2de9acc824..7311f051e1 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -90,6 +90,7 @@ typedef struct ngtcp2_bbr_cc { /* congestion_recovery_start_ts is the time when congestion recovery period started.*/ ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t congestion_recovery_next_round_delivered; size_t full_bw_count; size_t cycle_index; ngtcp2_bbr_state state; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c index 6d09b81997..616e944354 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c @@ -336,6 +336,7 @@ static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, bbr->max_inflight = 0; bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->congestion_recovery_next_round_delivered = 0; bbr->prior_inflight_lo = 0; bbr->prior_inflight_hi = 0; @@ -530,7 +531,7 @@ static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr, static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { - if (bbr_is_in_probe_bw_state(bbr)) { + if (!bbr->filled_pipe || bbr_is_in_probe_bw_state(bbr)) { return; } @@ -672,7 +673,7 @@ static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { bbr_start_round(bbr); bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_DOWN; - bbr->pacing_gain = 0.75; + bbr->pacing_gain = 0.9; bbr->cwnd_gain = 2; } @@ -770,6 +771,8 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr, static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { + (void)ts; + if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) { return 0; } @@ -778,7 +781,7 @@ static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr, return 1; } - return bbr_has_elapsed_in_phase(bbr, bbr->min_rtt, ts); + return 0; } static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr, @@ -1192,7 +1195,7 @@ static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr, if (ack->bytes_lost > 0) { if (cstat->cwnd > ack->bytes_lost) { cstat->cwnd -= ack->bytes_lost; - cstat->cwnd = ngtcp2_max(cstat->cwnd, cstat->max_udp_payload_size); + cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size); } else { cstat->cwnd = cstat->max_udp_payload_size; } @@ -1301,7 +1304,7 @@ static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_ack *ack) { if (bbr->in_loss_recovery) { - if (bbr->round_start) { + if (ack->pkt_delivered >= bbr->congestion_recovery_next_round_delivered) { bbr->packet_conservation = 0; } @@ -1323,6 +1326,7 @@ static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts; bbr->congestion_recovery_start_ts = UINT64_MAX; bbr->packet_conservation = 1; + bbr->congestion_recovery_next_round_delivered = bbr->rst->delivered; bbr->prior_inflight_lo = bbr->inflight_lo; bbr->prior_bw_lo = bbr->bw_lo; } @@ -1362,7 +1366,7 @@ static void bbr2_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); - if (bbr->in_loss_recovery || + if (!bbr->filled_pipe || bbr->in_loss_recovery || bbr->congestion_recovery_start_ts != UINT64_MAX || in_congestion_recovery(cstat, sent_ts)) { return; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h index ebc3cef1bd..50dc05a5f2 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h @@ -131,6 +131,7 @@ typedef struct ngtcp2_bbr2_cc { int packet_conservation; uint64_t max_inflight; ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t congestion_recovery_next_round_delivered; uint64_t prior_inflight_lo; uint64_t prior_inflight_hi; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c index 15c565f2f8..aa959fab60 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -60,6 +60,15 @@ static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) { */ static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; } +/* + * conn_is_handshake_completed returns nonzero if QUIC handshake has + * completed. + */ +static int conn_is_handshake_completed(ngtcp2_conn *conn) { + return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm; +} + static int conn_call_recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid) { int rv; @@ -125,6 +134,8 @@ static int conn_call_recv_crypto_data(ngtcp2_conn *conn, case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: case NGTCP2_ERR_TRANSPORT_PARAM: case NGTCP2_ERR_PROTO: + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + case NGTCP2_ERR_NOMEM: case NGTCP2_ERR_CALLBACK_FAILURE: return rv; default: @@ -545,7 +556,7 @@ static int conn_call_recv_datagram(ngtcp2_conn *conn, datalen = 0; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { flags |= NGTCP2_DATAGRAM_FLAG_EARLY; } @@ -578,6 +589,21 @@ conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, return 0; } +static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *dcid) { + int rv; + + assert(conn->callbacks.version_negotiation); + + rv = + conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int crypto_offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *(int64_t *)lhs < *(int64_t *)rhs; @@ -585,15 +611,13 @@ static int crypto_offset_less(const ngtcp2_ksl_key *lhs, static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { int rv; memset(pktns, 0, sizeof(*pktns)); - rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); - if (rv != 0) { - return rv; - } + ngtcp2_gaptr_init(&pktns->rx.pngap, mem); pktns->tx.last_pkt_num = -1; pktns->rx.max_pkt_num = -1; @@ -604,27 +628,17 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, goto fail_acktr_init; } - rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, - NULL, mem); - if (rv != 0) { - goto fail_crypto_init; - } + ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, + NULL, mem); - rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, - sizeof(uint64_t), mem); - if (rv != 0) { - goto fail_tx_frq_init; - } + ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, sizeof(uint64_t), + mem); ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log, - qlog, mem); + qlog, rtb_entry_objalloc, frc_objalloc, mem); return 0; -fail_tx_frq_init: - ngtcp2_strm_free(&pktns->crypto.strm); -fail_crypto_init: - ngtcp2_acktr_free(&pktns->acktr); fail_acktr_init: ngtcp2_gaptr_free(&pktns->rx.pngap); @@ -633,7 +647,8 @@ fail_acktr_init: static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { int rv; *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); @@ -641,7 +656,8 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, return NGTCP2_ERR_NOMEM; } - rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem); + rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, rtb_entry_objalloc, + frc_objalloc, mem); if (rv != 0) { ngtcp2_mem_free(mem, *ppktns); } @@ -689,7 +705,8 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { delete_buffed_pkts(pktns->rx.buffed_pkts, mem); - ngtcp2_frame_chain_list_del(pktns->tx.frq, mem); + ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc, + mem); ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); @@ -697,7 +714,7 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, mem); + ngtcp2_frame_chain_objalloc_del(frc, pktns->rtb.frc_objalloc, mem); } ngtcp2_ksl_free(&pktns->crypto.tx.frq); @@ -841,7 +858,7 @@ ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, } static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, - uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns, + uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns, const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) { assert(pi); @@ -933,9 +950,44 @@ static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) { pktns->tx.ecn.validation_pkt_lost = 0; } +/* server_default_other_versions is the default other_versions field + sent by server. */ +static uint8_t server_default_other_versions[] = {0, 0, 0, 1}; + +/* + * other_versions_new allocates new buffer, and writes |versions| of + * length |versionslen| in network byte order, suitable for sending in + * other_versions field of version_information QUIC transport + * parameter. The pointer to the allocated buffer is assigned to + * |*pbuf|. + * + * This function returns 0 if it succeeds, or one of the negative + * error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int other_versions_new(uint8_t **pbuf, const uint32_t *versions, + size_t versionslen, const ngtcp2_mem *mem) { + size_t i; + uint8_t *buf = ngtcp2_mem_malloc(mem, sizeof(uint32_t) * versionslen); + + if (buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + *pbuf = buf; + + for (i = 0; i < versionslen; ++i) { + buf = ngtcp2_put_uint32be(buf, versions[i]); + } + + return 0; +} + static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, int callbacks_version, + uint32_t client_chosen_version, int callbacks_version, const ngtcp2_callbacks *callbacks, int settings_version, const ngtcp2_settings *settings, int transport_params_version, @@ -945,6 +997,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_scid *scident; uint8_t *buf; uint8_t fixed_bit_byte; + size_t i; + uint32_t *preferred_versions; (void)callbacks_version; (void)settings_version; (void)transport_params_version; @@ -952,6 +1006,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, assert(settings->max_window <= NGTCP2_MAX_VARINT); assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); assert(settings->max_udp_payload_size); + assert(settings->max_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE); assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); assert(params->initial_max_data <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT); @@ -970,6 +1025,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, assert(callbacks->delete_crypto_aead_ctx); assert(callbacks->delete_crypto_cipher_ctx); assert(callbacks->get_path_challenge_data); + assert(!server || !ngtcp2_is_reserved_version(client_chosen_version)); if (mem == NULL) { mem = ngtcp2_mem_default(); @@ -981,60 +1037,31 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, goto fail_conn; } - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.bound, - NGTCP2_MAX_BOUND_DCID_POOL_SIZE, sizeof(ngtcp2_dcid), - mem); - if (rv != 0) { - goto fail_dcid_bound_init; - } + ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 64, mem); + ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 64, mem); + ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 64, mem); - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_unused_init; - } + ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound); - rv = - ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_retired_init; - } + ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused); - rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); - if (rv != 0) { - goto fail_seqgap_init; - } + ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired); - rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); - if (rv != 0) { - goto fail_scid_set_init; - } + ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); + + ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); - rv = ngtcp2_map_init(&(*pconn)->strms, mem); - if (rv != 0) { - goto fail_strms_init; - } + ngtcp2_map_init(&(*pconn)->strms, mem); ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); - rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_bidi_idtr_init; - } + ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_uni_idtr_init; - } + ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4, - sizeof(ngtcp2_path_challenge_entry), mem); - if (rv != 0) { - goto fail_rx_path_challenge_init; - } + ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge); ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf, settings->initial_ts, user_data); @@ -1043,6 +1070,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, if ((*pconn)->qlog.write) { buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN); if (buf == NULL) { + rv = NGTCP2_ERR_NOMEM; goto fail_qlog_buf; } ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); @@ -1057,6 +1085,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, if (settings->token.len) { buf = ngtcp2_mem_malloc(mem, settings->token.len); if (buf == NULL) { + rv = NGTCP2_ERR_NOMEM; goto fail_token; } memcpy(buf, settings->token.base, settings->token.len); @@ -1066,6 +1095,10 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->local.settings.token.len = 0; } + if (!(*pconn)->local.settings.original_version) { + (*pconn)->local.settings.original_version = client_chosen_version; + } + conn_reset_conn_stat(*pconn, &(*pconn)->cstat); (*pconn)->cstat.initial_rtt = settings->initial_rtt; (*pconn)->cstat.max_udp_payload_size = @@ -1109,19 +1142,22 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, } rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_in_pktns_init; } rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_hs_pktns_init; } rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_pktns_init; } @@ -1151,6 +1187,84 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, goto fail_seqgap_push; } + if (settings->preferred_versionslen) { + if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + for (i = 0; i < settings->preferred_versionslen; ++i) { + if (settings->preferred_versions[i] == client_chosen_version) { + break; + } + } + + assert(i < settings->preferred_versionslen); + } + + preferred_versions = ngtcp2_mem_malloc( + mem, sizeof(uint32_t) * settings->preferred_versionslen); + if (preferred_versions == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_preferred_versions; + } + + for (i = 0; i < settings->preferred_versionslen; ++i) { + assert(ngtcp2_is_supported_version(settings->preferred_versions[i])); + + preferred_versions[i] = settings->preferred_versions[i]; + } + + (*pconn)->vneg.preferred_versions = preferred_versions; + (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen; + } + + if (settings->other_versionslen) { + if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + for (i = 0; i < settings->other_versionslen; ++i) { + if (settings->other_versions[i] == client_chosen_version) { + break; + } + } + + assert(i < settings->other_versionslen); + } + + for (i = 0; i < settings->other_versionslen; ++i) { + assert(ngtcp2_is_reserved_version(settings->other_versions[i]) || + ngtcp2_is_supported_version(settings->other_versions[i])); + } + + rv = other_versions_new(&buf, settings->other_versions, + settings->other_versionslen, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = + sizeof(uint32_t) * settings->other_versionslen; + } else if (server) { + if (settings->preferred_versionslen) { + rv = other_versions_new(&buf, settings->preferred_versions, + settings->preferred_versionslen, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = + sizeof(uint32_t) * settings->preferred_versionslen; + } else { + (*pconn)->vneg.other_versions = server_default_other_versions; + (*pconn)->vneg.other_versionslen = sizeof(server_default_other_versions); + } + } else if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + rv = other_versions_new(&buf, &client_chosen_version, 1, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = sizeof(uint32_t); + } + callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx); if (fixed_bit_byte & 1) { (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT; @@ -1161,7 +1275,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->server = server; (*pconn)->oscid = *scid; (*pconn)->callbacks = *callbacks; - (*pconn)->version = version; + (*pconn)->client_chosen_version = client_chosen_version; (*pconn)->mem = mem; (*pconn)->user_data = user_data; (*pconn)->idle_ts = settings->initial_ts; @@ -1177,12 +1291,13 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, return 0; +fail_other_versions: + ngtcp2_mem_free(mem, (*pconn)->vneg.preferred_versions); +fail_preferred_versions: fail_seqgap_push: fail_scid_set_insert: ngtcp2_mem_free(mem, scident); fail_scident: - ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); -fail_token: pktns_free(&(*pconn)->pktns, mem); fail_pktns_init: pktns_del((*pconn)->hs_pktns, mem); @@ -1191,27 +1306,19 @@ fail_hs_pktns_init: fail_in_pktns_init: cc_del(&(*pconn)->cc, settings->cc_algo, mem); fail_cc_init: + ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); +fail_token: ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); fail_qlog_buf: - ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge); -fail_rx_path_challenge_init: ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr); -fail_remote_uni_idtr_init: ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr); -fail_remote_bidi_idtr_init: ngtcp2_map_free(&(*pconn)->strms); -fail_strms_init: delete_scid(&(*pconn)->scid.set, mem); ngtcp2_ksl_free(&(*pconn)->scid.set); -fail_scid_set_init: ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap); -fail_seqgap_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.retired); -fail_dcid_retired_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.unused); -fail_dcid_unused_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.bound); -fail_dcid_bound_init: + ngtcp2_objalloc_free(&(*pconn)->strm_objalloc); + ngtcp2_objalloc_free(&(*pconn)->rtb_entry_objalloc); + ngtcp2_objalloc_free(&(*pconn)->frc_objalloc); ngtcp2_mem_free(mem, *pconn); fail_conn: return rv; @@ -1219,15 +1326,16 @@ fail_conn: int ngtcp2_conn_client_new_versioned( ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - const ngtcp2_path *path, uint32_t version, int callbacks_version, - const ngtcp2_callbacks *callbacks, int settings_version, - const ngtcp2_settings *settings, int transport_params_version, - const ngtcp2_transport_params *params, const ngtcp2_mem *mem, - void *user_data) { + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks_version, callbacks, - settings_version, settings, transport_params_version, params, - mem, user_data, 0); + + rv = conn_new(pconn, dcid, scid, path, client_chosen_version, + callbacks_version, callbacks, settings_version, settings, + transport_params_version, params, mem, user_data, 0); if (rv != 0) { return rv; } @@ -1247,18 +1355,20 @@ int ngtcp2_conn_client_new_versioned( int ngtcp2_conn_server_new_versioned( ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - const ngtcp2_path *path, uint32_t version, int callbacks_version, - const ngtcp2_callbacks *callbacks, int settings_version, - const ngtcp2_settings *settings, int transport_params_version, - const ngtcp2_transport_params *params, const ngtcp2_mem *mem, - void *user_data) { + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks_version, callbacks, - settings_version, settings, transport_params_version, params, - mem, user_data, 1); + + rv = conn_new(pconn, dcid, scid, path, client_chosen_version, + callbacks_version, callbacks, settings_version, settings, + transport_params_version, params, mem, user_data, 1); if (rv != 0) { return rv; } + (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; (*pconn)->local.bidi.next_stream_id = 1; (*pconn)->local.uni.next_stream_id = 3; @@ -1293,15 +1403,30 @@ static uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, } static int delete_strms_each(void *data, void *ptr) { - const ngtcp2_mem *mem = ptr; + ngtcp2_conn *conn = ptr; ngtcp2_strm *s = data; ngtcp2_strm_free(s); - ngtcp2_mem_free(mem, s); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); return 0; } +static void conn_vneg_crypto_free(ngtcp2_conn *conn) { + if (conn->vneg.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + + if (conn->vneg.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + + ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); + ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); +} + void ngtcp2_conn_del(ngtcp2_conn *conn) { if (conn == NULL) { return; @@ -1368,6 +1493,13 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx); + conn_vneg_crypto_free(conn); + + ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions); + if (conn->vneg.other_versions != server_default_other_versions) { + ngtcp2_mem_free(conn->mem, conn->vneg.other_versions); + } + ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base); ngtcp2_mem_free(conn->mem, conn->local.settings.token.base); @@ -1385,8 +1517,7 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin); - ngtcp2_ringbuf_free(&conn->rx.path_challenge); - + ngtcp2_pmtud_del(conn->pmtud); ngtcp2_pv_del(conn->pv); ngtcp2_mem_free(conn->mem, conn->rx.ccerr.reason); @@ -1395,16 +1526,17 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_idtr_free(&conn->remote.bidi.idtr); ngtcp2_mem_free(conn->mem, conn->tx.ack); ngtcp2_pq_free(&conn->tx.strmq); - ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem); + ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn); ngtcp2_map_free(&conn->strms); ngtcp2_pq_free(&conn->scid.used); delete_scid(&conn->scid.set, conn->mem); ngtcp2_ksl_free(&conn->scid.set); ngtcp2_gaptr_free(&conn->dcid.seqgap); - ngtcp2_ringbuf_free(&conn->dcid.retired); - ngtcp2_ringbuf_free(&conn->dcid.unused); - ngtcp2_ringbuf_free(&conn->dcid.bound); + + ngtcp2_objalloc_free(&conn->strm_objalloc); + ngtcp2_objalloc_free(&conn->rtb_entry_objalloc); + ngtcp2_objalloc_free(&conn->frc_objalloc); ngtcp2_mem_free(conn->mem, conn); } @@ -1543,7 +1675,7 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, ack->first_ack_blklen = 0; } - if (type == NGTCP2_PKT_SHORT) { + if (type == NGTCP2_PKT_1RTT) { ack->ack_delay_unscaled = ts - largest_ack_ts; ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / (1ULL << ack_delay_exponent); @@ -1689,16 +1821,28 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { return 1; } +/* + * conn_get_cwnd returns cwnd for the current path. + */ +static uint64_t conn_get_cwnd(ngtcp2_conn *conn) { + return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) + ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) + : conn->cstat.cwnd; +} + /* * conn_cwnd_is_zero returns nonzero if the number of bytes the local * endpoint can sent at this time is zero. */ static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; - uint64_t cwnd = - conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) - ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) - : conn->cstat.cwnd; + uint64_t cwnd = conn_get_cwnd(conn); + + if (bytes_in_flight >= cwnd) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "cwnd limited bytes_in_flight=%lu cwnd=%lu", + bytes_in_flight, cwnd); + } return bytes_in_flight >= cwnd; } @@ -1744,7 +1888,7 @@ static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); } ngtcp2_ksl_clear(&pktns->crypto.tx.frq); } @@ -1826,7 +1970,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, } if (idx == fr->datacnt) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } @@ -1865,10 +2009,10 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return 0; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx, - conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, fr->datacnt - end_idx, &conn->frc_objalloc, conn->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1887,8 +2031,8 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1949,10 +2093,11 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, assert(acnt > 0); assert(bcnt > 0); - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, bcnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1965,15 +2110,16 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, acnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1982,7 +2128,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); *pfrc = nfrc; @@ -2007,7 +2153,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } if (nfrc == NULL) { @@ -2022,8 +2168,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } break; @@ -2033,7 +2179,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, left -= nmerged; if (nfr->datacnt == 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); continue; } @@ -2041,8 +2187,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -2059,9 +2205,10 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, assert(acnt > fr->datacnt); - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, acnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -2070,7 +2217,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); *pfrc = nfrc; @@ -2132,7 +2279,7 @@ static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, * conn_should_pad_pkt returns nonzero if the packet should be padded. * |type| is the type of packet. |left| is the space left in packet * buffer. |write_datalen| is the number of bytes which will be sent - * in the next, coalesced 0-RTT or Short packet. + * in the next, coalesced 0-RTT or 1RTT packet. */ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, uint64_t write_datalen, int ack_eliciting, @@ -2167,7 +2314,7 @@ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, write_datalen == 0) { return 1; } else { - /* If we have something to send in 0RTT or Short packet, then + /* If we have something to send in 0RTT or 1RTT packet, then add PADDING in that packet. Take maximum in case that write_datalen includes DATAGRAM which cannot be split. */ min_payloadlen = @@ -2192,13 +2339,13 @@ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, return left < /* TODO Assuming that pkt_num is encoded in 1 byte. */ NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + - conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ + - min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD; + conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen + + NGTCP2_MAX_AEAD_OVERHEAD; } static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->idle_ts = ts; - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; } static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -2253,7 +2400,7 @@ static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn, static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->keep_alive.last_ts = ts; - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; } void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, @@ -2270,7 +2417,7 @@ void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, * is involved (which includes other stuff, like reading packets etc * in a typical single threaded use case). */ -#define NGTCP2_PKT_PACING_OVERHEAD (500 * NGTCP2_MICROSECONDS) +#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -2310,16 +2457,6 @@ static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) { : NGTCP2_PKT_FLAG_NONE)); } -/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00 -/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other - than Initial packet should be padded. Initial packet might be - padded based on QUIC requirement regardless of this flag. */ -#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01 -/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come - and it should be encoded into the current packet. */ -#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02 - /* * conn_write_handshake_pkt writes handshake packet in the buffer * pointed by |dest| whose length is |destlen|. |type| specifies long @@ -2327,7 +2464,7 @@ static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) { * NGTCP2_PKT_HANDSHAKE_PKT. * * |write_datalen| is the minimum length of application data ready to - * send in subsequent 0RTT or Short packet. + * send in subsequent 0RTT or 1RTT packet. * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: @@ -2352,13 +2489,14 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, ngtcp2_rtb_entry *rtbent; ngtcp2_pktns *pktns; size_t left; - uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; int pkt_empty = 1; int padded = 0; int hd_logged = 0; uint64_t crypto_offset; ngtcp2_ssize num_reclaimed; + uint32_t version; switch (type) { case NGTCP2_PKT_INITIAL: @@ -2367,12 +2505,26 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, } assert(conn->in_pktns->crypto.tx.ckm); pktns = conn->in_pktns; + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (version == conn->client_chosen_version) { + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + } else { + assert(conn->vneg.version == version); + + cc.ckm = conn->vneg.tx.ckm; + cc.hp_ctx = conn->vneg.tx.hp_ctx; + } break; case NGTCP2_PKT_HANDSHAKE: if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { return 0; } pktns = conn->hs_pktns; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; default: assert(0); @@ -2381,15 +2533,13 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, cc.aead = pktns->crypto.ctx.aead; cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; cc.encrypt = conn->callbacks.encrypt; cc.hp_mask = conn->callbacks.hp_mask; ngtcp2_pkt_hd_init(&hd, conn_pkt_flags_long(conn), type, &conn->dcid.current.cid, &conn->oscid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); + version, 0); if (!conn->server && type == NGTCP2_PKT_INITIAL && conn->local.settings.token.len) { @@ -2412,7 +2562,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, /* ack_delay = */ 0, NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); if (rv != 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return rv; } @@ -2449,7 +2599,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); return rv; } @@ -2467,15 +2618,16 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; } if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); return rv; } if (num_reclaimed) { @@ -2487,21 +2639,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, send any probe packet. Client needs to send probe packets until it knows that server has completed address validation or handshake has been confirmed. */ - if (pktns->rtb.num_retransmittable == 0 && + if (pktns->rtb.num_pto_eliciting == 0 && (conn->server || (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); + /* TODO If packet is empty, we should return now if cwnd is + zero. */ } } - /* Don't send any PING frame if client Initial has not been - acknowledged yet. */ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && - pktns->rtb.probe_pkt_left && - (type != NGTCP2_PKT_INITIAL || - ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { + pktns->rtb.probe_pkt_left) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); @@ -2518,7 +2668,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { /* The intention of smaller limit is get more chance to measure RTT samples in early phase. */ - if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) { + if (pktns->tx.num_non_ack_pkt >= 1) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); @@ -2563,7 +2713,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, spktlen = ngtcp2_ppe_final(&ppe, NULL); if (spktlen < 0) { assert(ngtcp2_err_is_fatal((int)spktlen)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return spktlen; } @@ -2574,17 +2724,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); } - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, - rtb_entry_flags, conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return rv; } rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } @@ -2651,7 +2803,7 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ack_delay = 0; ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; break; - case NGTCP2_PKT_SHORT: + case NGTCP2_PKT_1RTT: pktns = &conn->pktns; ack_delay = conn_compute_ack_delay(conn); ack_delay_exponent = conn->local.transport_params.ack_delay_exponent; @@ -2677,8 +2829,8 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } spktlen = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr, - NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE, + &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (spktlen <= 0) { return spktlen; @@ -2727,6 +2879,11 @@ static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { "discarding Initial packet number space"); conn_discard_pktns(conn, &conn->in_pktns, ts); + + conn_vneg_crypto_free(conn); + + memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx)); + memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx)); } /* @@ -3104,7 +3261,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -3166,8 +3323,8 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, --conn->scid.num_retired; } - for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); + for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); if (dcid->retired_ts + timeout >= ts) { break; } @@ -3177,7 +3334,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.retired); + ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb); } return 0; @@ -3191,10 +3348,33 @@ static size_t conn_min_short_pktlen(ngtcp2_conn *conn) { return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; } +/* + * conn_handle_unconfirmed_key_update_from_remote deals with key + * update which has not been confirmed yet and initiated by the remote + * endpoint. + * + * If key update was initiated by the remote endpoint, acknowledging a + * packet encrypted with the new key completes key update procedure. + */ +static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn, + int64_t largest_ack, + ngtcp2_tstamp ts) { + if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) || + largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) { + return; + } + + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->crypto.key_update.confirmed_ts = ts; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); +} + /* * conn_write_pkt writes a protected packet in the buffer pointed by * |dest| whose length if |destlen|. |type| specifies the type of - * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT. + * packet. It can be NGTCP2_PKT_1RTT or NGTCP2_PKT_0RTT. * * This function can send new stream data. In order to send stream * data, specify the underlying stream and parameters to @@ -3242,7 +3422,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint64_t datalen = 0; ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; size_t datacnt; - uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; int hd_logged = 0; ngtcp2_path_challenge_entry *pcent; uint8_t hd_flags; @@ -3261,6 +3441,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint64_t delta; const ngtcp2_cid *scid; int keep_alive_expired = 0; + uint32_t version; /* Return 0 if destlen is less than minimum packet length which can trigger Stateless Reset */ @@ -3291,7 +3472,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!ppe_pending) { switch (type) { - case NGTCP2_PKT_SHORT: + case NGTCP2_PKT_1RTT: hd_flags = conn_pkt_flags_short(conn); scid = NULL; cc->aead = pktns->crypto.ctx.aead; @@ -3299,11 +3480,14 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, cc->ckm = pktns->crypto.tx.ckm; cc->hp_ctx = pktns->crypto.tx.hp_ctx; + assert(conn->negotiated_version); + + version = conn->negotiated_version; + /* transport parameter is only valid after handshake completion which means we don't know how many connection ID that remote peer can accept before handshake completion. */ - if (conn->oscid.datalen && - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (conn->oscid.datalen && conn_is_handshake_completed(conn)) { rv = conn_enqueue_new_connection_id(conn); if (rv != 0) { return rv; @@ -3322,6 +3506,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, cc->hp = conn->early.ctx.hp; cc->ckm = conn->early.ckm; cc->hp_ctx = conn->early.hp_ctx; + version = conn->client_chosen_version; break; default: /* Unreachable */ @@ -3332,7 +3517,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, cc->hp_mask = conn->callbacks.hp_mask; if (conn_should_send_max_data(conn)) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -3370,7 +3555,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, pktns->tx.last_pkt_num + 1, - pktns_select_pkt_numlen(pktns), conn->version, 0); + pktns_select_pkt_numlen(pktns), version, 0); ngtcp2_ppe_init(ppe, dest, destlen, cc); @@ -3384,8 +3569,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return 0; } - if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + if (ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); /* PATH_RESPONSE is bound to the path that the corresponding PATH_CHALLENGE is received. */ @@ -3398,7 +3583,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (rv != 0) { assert(NGTCP2_ERR_NOBUF == rv); } else { - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; @@ -3425,6 +3610,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_acktr_commit_ack(&pktns->acktr); ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, ackfr->ack.largest_ack); + if (type == NGTCP2_PKT_1RTT) { + conn_handle_unconfirmed_key_update_from_remote( + conn, ackfr->ack.largest_ack, ts); + } pkt_empty = 0; } } @@ -3435,7 +3624,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } @@ -3446,16 +3635,20 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } - rv = conn_call_stream_stop_sending( - conn, (*pfrc)->fr.stop_sending.stream_id, - (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; + if (!(strm->flags & NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED)) { + strm->flags |= NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED; + + rv = conn_call_stream_stop_sending( + conn, (*pfrc)->fr.stop_sending.stream_id, + (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } } break; @@ -3467,7 +3660,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->remote.bidi.max_streams) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3476,7 +3669,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->remote.uni.max_streams) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3487,7 +3680,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3495,13 +3688,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - ++conn->dcid.num_retire_queued; - break; case NGTCP2_FRAME_CRYPTO: assert(0); break; @@ -3515,6 +3705,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3555,6 +3746,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; } } @@ -3570,7 +3762,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3587,6 +3779,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3601,7 +3794,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3619,6 +3812,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3631,7 +3825,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && conn_should_send_max_stream_data(conn, strm)) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3675,6 +3869,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; strm->rx.max_offset = strm->rx.unsent_max_offset = @@ -3690,6 +3885,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (stream_offset == (uint64_t)-1) { ngtcp2_strm_streamfrq_clear(strm); ngtcp2_conn_tx_strmq_pop(conn); + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; continue; } @@ -3723,10 +3920,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; if (ngtcp2_strm_streamfrq_empty(strm)) { ngtcp2_conn_tx_strmq_pop(conn); + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; continue; } @@ -3744,8 +3944,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && pktns->rtb.num_retransmittable && pktns->tx.frq == NULL && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { return rv; } @@ -3757,9 +3956,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, those packets have been acknowledged (i.e., retransmission in another packet). In this case, we don't have to send any probe packet. */ - if (pktns->rtb.num_retransmittable == 0) { + if (pktns->rtb.num_pto_eliciting == 0) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); + /* TODO If packet is empty, we should return now if cwnd is + zero. */ } } } else { @@ -3783,7 +3984,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, assert((datacnt == 0 && datalen == 0) || (datacnt && datalen)); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, datacnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3810,6 +4012,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; vmsg->stream.strm->tx.offset += ndatalen; @@ -3829,7 +4032,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (rv != NGTCP2_ERR_NOBUF && send_datagram && left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) { if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3917,7 +4120,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT || - keep_alive_expired) { + keep_alive_expired || conn->pktns.rtb.probe_pkt_left) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); @@ -3927,6 +4130,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, packet is still empty. */ } else { rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + if (conn->pktns.rtb.probe_pkt_left) { + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE; + } pktns->tx.num_non_ack_pkt = 0; } } else { @@ -3970,8 +4176,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts); } - rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, - rtb_entry_flags, conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal((int)nwrite)); return rv; @@ -3992,7 +4199,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_rtb_entry_del(ent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } @@ -4013,7 +4221,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts); } - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING; if (pktns->rtb.probe_pkt_left && (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { @@ -4038,8 +4246,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, - const ngtcp2_path *path, ngtcp2_tstamp ts) { + uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts) { int rv; ngtcp2_ppe ppe; ngtcp2_pkt_hd hd; @@ -4047,27 +4255,44 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_ssize nwrite; ngtcp2_crypto_cc cc; ngtcp2_pktns *pktns; - uint8_t flags; + uint8_t hd_flags; ngtcp2_rtb_entry *rtbent; int padded = 0; const ngtcp2_cid *scid; + uint32_t version; switch (type) { case NGTCP2_PKT_INITIAL: pktns = conn->in_pktns; - flags = conn_pkt_flags_long(conn); + hd_flags = conn_pkt_flags_long(conn); scid = &conn->oscid; + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (version == conn->client_chosen_version) { + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + } else { + assert(version == conn->vneg.version); + + cc.ckm = conn->vneg.tx.ckm; + cc.hp_ctx = conn->vneg.tx.hp_ctx; + } break; case NGTCP2_PKT_HANDSHAKE: pktns = conn->hs_pktns; - flags = conn_pkt_flags_long(conn); + hd_flags = conn_pkt_flags_long(conn); scid = &conn->oscid; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; - case NGTCP2_PKT_SHORT: - /* 0 means Short packet. */ + case NGTCP2_PKT_1RTT: pktns = &conn->pktns; - flags = conn_pkt_flags_short(conn); + hd_flags = conn_pkt_flags_short(conn); scid = NULL; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; default: /* We don't support 0-RTT packet in this function. */ @@ -4077,13 +4302,12 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( cc.aead = pktns->crypto.ctx.aead; cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; cc.encrypt = conn->callbacks.encrypt; cc.hp_mask = conn->callbacks.hp_mask; - ngtcp2_pkt_hd_init(&hd, flags, type, dcid, scid, pktns->tx.last_pkt_num + 1, - pktns_select_pkt_numlen(pktns), conn->version, 0); + ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid, + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), + version, 0); ngtcp2_ppe_init(&ppe, dest, destlen, &cc); @@ -4107,21 +4331,25 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( } lfr.type = NGTCP2_FRAME_PADDING; - switch (fr->type) { - case NGTCP2_FRAME_PATH_CHALLENGE: - case NGTCP2_FRAME_PATH_RESPONSE: - if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { - lfr.padding.len = ngtcp2_ppe_padding(&ppe); - } else { - lfr.padding.len = 0; - } - break; - default: - if (type == NGTCP2_PKT_SHORT) { - lfr.padding.len = - ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); - } else { - lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + switch (fr->type) { + case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_PATH_RESPONSE: + if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + lfr.padding.len = 0; + } + break; + default: + if (type == NGTCP2_PKT_1RTT) { + lfr.padding.len = + ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + } else { + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } } } if (lfr.padding.len) { @@ -4135,7 +4363,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( return nwrite; } - if (type == NGTCP2_PKT_SHORT) { + if (type == NGTCP2_PKT_1RTT) { ++cc.ckm->use_count; } @@ -4147,28 +4375,34 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( case NGTCP2_FRAME_ACK_ECN: ngtcp2_acktr_commit_ack(&pktns->acktr); ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); + if (type == NGTCP2_PKT_1RTT) { + conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack, + ts); + } break; } - if (((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && + if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) { if (pi) { - conn_handle_tx_ecn(conn, pi, &rtb_flags, pktns, &hd, ts); + conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); } - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags, - conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { return rv; } rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } - if (rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { conn_restart_timer_on_write(conn, ts); } @@ -4199,7 +4433,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( } /* - * conn_process_early_rtb makes any pending 0RTT packet Short packet. + * conn_process_early_rtb makes any pending 0RTT packet 1RTT packet. */ static void conn_process_early_rtb(ngtcp2_conn *conn) { ngtcp2_rtb_entry *ent; @@ -4215,9 +4449,9 @@ static void conn_process_early_rtb(ngtcp2_conn *conn) { continue; } - /* 0-RTT packet is retransmitted as a Short packet. */ + /* 0-RTT packet is retransmitted as a 1RTT packet. */ ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; - ent->hd.type = NGTCP2_PKT_SHORT; + ent->hd.type = NGTCP2_PKT_1RTT; } } @@ -4230,10 +4464,10 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; - return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - (in_pktns && (in_pktns->rtb.num_retransmittable || + return !conn_is_handshake_completed(conn) || + (in_pktns && (in_pktns->rtb.num_pto_eliciting || ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || - (hs_pktns && (hs_pktns->rtb.num_retransmittable || + (hs_pktns && (hs_pktns->rtb.num_pto_eliciting || ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); } @@ -4246,13 +4480,20 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { * * NGTCP2_ERR_NOMEM * Out of memory. + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. */ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { ngtcp2_pktns *pktns = &conn->pktns; ngtcp2_frame_chain *nfrc; int rv; - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -4276,7 +4517,7 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { */ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, ngtcp2_tstamp ts) { - ngtcp2_ringbuf *rb = &conn->dcid.retired; + ngtcp2_ringbuf *rb = &conn->dcid.retired.rb; ngtcp2_dcid *dest, *stale_dcid; int rv; @@ -4325,9 +4566,9 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)); - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (ngtcp2_path_eq(&dcid->ps.path, path)) { *pdcid = dcid; @@ -4336,7 +4577,7 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, } if (conn->dcid.current.cid.datalen == 0) { - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); ngtcp2_cid_zero(&cid); ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL); ngtcp2_dcid_set_path(ndcid, path); @@ -4346,32 +4587,113 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, return 0; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - if (ngtcp2_ringbuf_full(&conn->dcid.bound)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 0); + if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0); rv = conn_retire_dcid(conn, dcid, ts); if (rv != 0) { return rv; } } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); ngtcp2_dcid_copy(ndcid, dcid); ndcid->bound_ts = ts; ngtcp2_dcid_set_path(ndcid, path); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); *pdcid = ndcid; return 0; } +static int conn_start_pmtud(ngtcp2_conn *conn) { + int rv; + size_t hard_max_udp_payload_size; + + assert(!conn->local.settings.no_pmtud); + assert(!conn->pmtud); + assert(conn_is_handshake_completed(conn)); + assert(conn->remote.transport_params.max_udp_payload_size >= + NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + hard_max_udp_payload_size = + ngtcp2_min(conn->remote.transport_params.max_udp_payload_size, + conn->local.settings.max_udp_payload_size); + + rv = ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size, + hard_max_udp_payload_size, + conn->pktns.tx.last_pkt_num + 1, conn->mem); + if (rv != 0) { + return rv; + } + + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + + return 0; +} + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) { + if (!conn->pmtud) { + return; + } + + ngtcp2_pmtud_del(conn->pmtud); + + conn->pmtud = NULL; +} + +static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + size_t probelen; + ngtcp2_ssize nwrite; + ngtcp2_frame lfr; + + assert(conn->pmtud); + assert(!ngtcp2_pmtud_finished(conn->pmtud)); + + if (!ngtcp2_pmtud_require_probe(conn->pmtud)) { + return 0; + } + + probelen = ngtcp2_pmtud_probelen(conn->pmtud); + if (probelen > destlen) { + return 0; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "sending PMTUD probe packet len=%zu", probelen); + + lfr.type = NGTCP2_FRAME_PING; + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, probelen, NGTCP2_PKT_1RTT, + NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, &conn->dcid.current.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE, + NULL, ts); + if (nwrite < 0) { + return nwrite; + } + + assert(nwrite); + + ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns), + ts); + + return nwrite; +} + /* * conn_stop_pv stops the path validation which is currently running. * This function does nothing if no path validation is currently being @@ -4445,26 +4767,23 @@ static int conn_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return conn_stop_pv(conn, ts); } -static void conn_update_dcid_max_udp_payload_size(ngtcp2_conn *conn, - ngtcp2_dcid *dcid, - size_t payloadlen) { - if (!conn->local.settings.assume_symmetric_path) { - return; - } - - dcid->max_udp_payload_size = - ngtcp2_max(dcid->max_udp_payload_size, payloadlen); -} - static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, size_t payloadlen) { - if (conn->local.settings.no_udp_payload_size_shaping) { - return ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size); + if (conn->remote.transport_params.max_udp_payload_size) { + assert(conn->remote.transport_params.max_udp_payload_size >= + NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + payloadlen = ngtcp2_min(payloadlen, + conn->remote.transport_params.max_udp_payload_size); } payloadlen = ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size); + if (conn->local.settings.no_udp_payload_size_shaping) { + return payloadlen; + } + return ngtcp2_min(payloadlen, dcid->max_udp_payload_size); } @@ -4566,7 +4885,7 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt); expiry = ts + timeout * (1ULL << pv->round); - destlen = conn_shape_udp_payload(conn, &pv->dcid, destlen); + destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); if (conn->server) { if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { @@ -4589,8 +4908,10 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts); nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr, - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pv->dcid.ps.path, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &pv->dcid.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING, + &pv->dcid.ps.path, ts); if (nwrite <= 0) { return nwrite; } @@ -4632,8 +4953,8 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, int rv; uint64_t tx_left; - for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { /* Send PATH_RESPONSE from conn_write_pkt. */ @@ -4658,7 +4979,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, /* Client does not expect to respond to path validation against unknown path */ - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); pcent = NULL; } @@ -4680,7 +5001,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, } } - destlen = conn_shape_udp_payload(conn, dcid, destlen); + destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { tx_left = conn_server_tx_left(conn, dcid); @@ -4694,8 +5015,9 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data)); nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &dcid->cid, &lfr, - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, + ts); if (nwrite <= 0) { return nwrite; } @@ -4704,7 +5026,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, ngtcp2_path_copy(path, &pcent->ps.path); } - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); dcid->bytes_sent += (uint64_t)nwrite; @@ -4753,6 +5075,12 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_ARGUMENT; } + /* Version Negotiation packet is ignored if client has reacted upon + Version Negotiation packet. */ + if (conn->local.settings.original_version != conn->client_chosen_version) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (payloadlen > sizeof(sv)) { p = ngtcp2_mem_malloc(conn->mem, payloadlen); if (p == NULL) { @@ -4766,14 +5094,18 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); - for (i = 0; i < nsv; ++i) { - if (p[i] == conn->version) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "ignore Version Negotiation because it contains version " - "selected by client"); + ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv); - rv = NGTCP2_ERR_INVALID_ARGUMENT; - goto fin; + if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) { + for (i = 0; i < nsv; ++i) { + if (p[i] == conn->local.settings.original_version) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "ignore Version Negotiation because it contains the " + "original version"); + + rv = NGTCP2_ERR_INVALID_ARGUMENT; + goto fin; + } } } @@ -4782,10 +5114,6 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, goto fin; } - /* TODO Just move to the terminal state for now in order not to - send CONNECTION_CLOSE frame. */ - conn->state = NGTCP2_CS_DRAINING; - fin: if (p != sv) { ngtcp2_mem_free(conn->mem, p); @@ -4823,6 +5151,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; + int streamfrq_empty; if (*pfrc == NULL) { return 0; @@ -4839,12 +5168,13 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); break; } + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -4854,6 +5184,9 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } break; case NGTCP2_FRAME_CRYPTO: frc = *pfrc; @@ -4865,7 +5198,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, &frc->fr.crypto.offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } break; @@ -4923,7 +5256,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, retry.odcid = conn->dcid.current.cid; rv = ngtcp2_pkt_verify_retry_tag( - conn->version, &retry, pkt, pktlen, conn->callbacks.encrypt, + conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt, &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx); if (rv != 0) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, @@ -4943,7 +5276,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, return 0; } - ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd); + ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry); /* DCID must be updated before invoking callback because client generates new initial keys there. */ @@ -5038,7 +5371,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, if (num_acked < 0) { /* TODO assert this */ assert(ngtcp2_err_is_fatal((int)num_acked)); - ngtcp2_frame_chain_list_del(frc, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return (int)num_acked; } @@ -5130,13 +5463,13 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn, return 0; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -5316,21 +5649,17 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, * User-defined callback function failed; or it does not return * expected result. */ -static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, - const ngtcp2_crypto_cipher *hp, - const uint8_t *pkt, size_t pktlen, - size_t pkt_num_offset, ngtcp2_crypto_km *ckm, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - ngtcp2_hp_mask hp_mask) { +static ngtcp2_ssize +decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset, + const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) { size_t sample_offset; uint8_t *p = dest; uint8_t mask[NGTCP2_HP_SAMPLELEN]; size_t i; int rv; - (void)ckm; assert(hp_mask); - assert(ckm); if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { return NGTCP2_ERR_PROTO; @@ -5461,7 +5790,7 @@ static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path, return; } - ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge); + ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb); ngtcp2_path_challenge_entry_init(ent, path, fr->data); } @@ -5525,10 +5854,21 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, if (rv != 0) { return rv; } - } - if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { - pto = conn_compute_pto(conn, &conn->pktns); + if (!conn->local.settings.no_pmtud) { + ngtcp2_conn_stop_pmtud(conn); + + if (!(pv->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + } + } + + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + pto = conn_compute_pto(conn, &conn->pktns); timeout = 3 * ngtcp2_max(pto, pv->fallback_pto); if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) { @@ -5658,6 +5998,33 @@ static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns, } } +/* + * vneg_other_versions_includes returns nonzero if |other_versions| of + * length |other_versionslen| includes |version|. |other_versions| is + * the wire image of other_versions field of version_information + * transport parameter, and each version is encoded in network byte + * order. + */ +static int vneg_other_versions_includes(const uint8_t *other_versions, + size_t other_versionslen, + uint32_t version) { + size_t i; + + assert(!(other_versionslen & 0x3)); + + if (other_versionslen == 0) { + return 0; + } + + for (i = 0; i < other_versionslen; i += sizeof(uint32_t)) { + if (version == ngtcp2_get_uint32(&other_versions[i])) { + return 1; + } + } + + return 0; +} + static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, ngtcp2_strm *strm, const ngtcp2_crypto *fr); @@ -5734,8 +6101,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { if (conn->state == NGTCP2_CS_SERVER_INITIAL) { - /* Ignore Short packet unless server's first Handshake packet - has been transmitted. */ + /* Ignore 1RTT packet unless server's first Handshake packet has + been transmitted. */ return (ngtcp2_ssize)pktlen; } @@ -5744,7 +6111,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering Short packet len=%zu", pktlen); + "buffering 1RTT packet len=%zu", pktlen); rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen, ts); @@ -5760,8 +6127,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - switch (hd.type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: + if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) { hdpktlen = (size_t)nread; ngtcp2_log_rx_pkt_hd(&conn->log, &hd); @@ -5797,7 +6163,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; - case NGTCP2_PKT_RETRY: + } else if (hd.type == NGTCP2_PKT_RETRY) { hdpktlen = (size_t)nread; ngtcp2_log_rx_pkt_hd(&conn->log, &hd); @@ -5812,6 +6178,10 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } + if (conn->client_chosen_version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts); if (rv != 0) { if (ngtcp2_err_is_fatal(rv)) { @@ -5828,7 +6198,18 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, pktlen = (size_t)nread + hd.len; - if (conn->version != hd.version) { + if (!ngtcp2_is_supported_version(hd.version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + + if (conn->server) { + if (hd.version != conn->client_chosen_version && + (!conn->negotiated_version || hd.version != conn->negotiated_version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + } else if (hd.version != conn->client_chosen_version && + conn->negotiated_version && + hd.version != conn->negotiated_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -5847,6 +6228,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (!conn->server) { return NGTCP2_ERR_DISCARD_PKT; } + + if (hd.version != conn->client_chosen_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { if (conn->early.ckm) { ngtcp2_ssize nread2; @@ -5885,6 +6271,14 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, assert(conn->in_pktns); if (conn->server) { + if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Initial packet was ignored because it is included in UDP datagram " + "less than %zu bytes: %zu bytes", + NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen); + return NGTCP2_ERR_DISCARD_PKT; + } if (conn->local.settings.token.len) { rv = verify_token(&conn->local.settings.token, &hd); if (rv != 0) { @@ -5905,18 +6299,55 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } } - } else if (hd.token.len != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because token is not empty"); - return NGTCP2_ERR_DISCARD_PKT; + } else { + if (hd.token.len != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is not empty"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (hd.version != conn->client_chosen_version && + !conn->negotiated_version && conn->vneg.version != hd.version) { + if (!vneg_other_versions_includes(conn->vneg.other_versions, + conn->vneg.other_versionslen, + hd.version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Install new Initial keys using QUIC version = hd.version */ + rv = conn_call_version_negotiation( + conn, hd.version, + (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) + ? &conn->dcid.current.cid + : &conn->rcid); + if (rv != 0) { + return rv; + } + + assert(conn->vneg.version == hd.version); + } } pktns = conn->in_pktns; crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; + if (hd.version == conn->client_chosen_version) { + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; + } else { + assert(conn->vneg.version == hd.version); + + ckm = conn->vneg.rx.ckm; + hp_ctx = &conn->vneg.rx.hp_ctx; + } + break; case NGTCP2_PKT_HANDSHAKE: + if (hd.version != conn->negotiated_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (!conn->hs_pktns->crypto.rx.ckm) { if (conn->server) { ngtcp2_log_info( @@ -5939,6 +6370,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, pktns = conn->hs_pktns; crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; break; default: @@ -5952,8 +6385,6 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, decrypt = conn->callbacks.decrypt; aead = &pktns->crypto.ctx.aead; hp = &pktns->crypto.ctx.hp; - ckm = pktns->crypto.rx.ckm; - hp_ctx = &pktns->crypto.rx.hp_ctx; assert(ckm); assert(hp_mask); @@ -5965,7 +6396,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); + (size_t)nread, hp_ctx, hp_mask); if (nwrite < 0) { if (ngtcp2_err_is_fatal((int)nwrite)) { return nwrite; @@ -6026,6 +6457,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_PROTO; } + if (!conn->server && hd.version != conn->client_chosen_version && + !conn->negotiated_version) { + conn->negotiated_version = hd.version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } + payload = conn->crypto.decrypt_buf.base; payloadlen = (size_t)nwrite; @@ -6110,6 +6550,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, case NGTCP2_FRAME_PADDING: break; case NGTCP2_FRAME_CRYPTO: + if (!conn->server && !conn->negotiated_version && + ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt)) { + conn->negotiated_version = hd.version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } + rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto); if (rv != 0) { return rv; @@ -6165,18 +6614,17 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - conn_update_dcid_max_udp_payload_size(conn, &conn->dcid.current, dgramlen); - return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING : (ngtcp2_ssize)pktlen; } -static int is_crypto_error(int liberr) { +static int is_unrecoverable_error(int liberr) { switch (liberr) { case NGTCP2_ERR_CRYPTO: case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: case NGTCP2_ERR_TRANSPORT_PARAM: + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: return 1; } @@ -6186,7 +6634,7 @@ static int is_crypto_error(int liberr) { /* * conn_recv_handshake_cpkt processes compound packet during * handshake. The buffer pointed by |pkt| might contain multiple - * packets. The Short packet must be the last one because it does not + * packets. The 1RTT packet must be the last one because it does not * have payload length field. * * This function returns the same error code returned by @@ -6200,6 +6648,7 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn, ngtcp2_ssize nread; size_t dgramlen = pktlen; const uint8_t *origpkt = pkt; + uint32_t version; if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { conn->dcid.current.bytes_recv += dgramlen; @@ -6217,35 +6666,36 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn, return NGTCP2_ERR_DRAINING; } - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && - /* Not a Version Negotiation packet */ - pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 && - ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) { - if (conn->server) { - if (is_crypto_error((int)nread)) { - /* If server gets crypto error from TLS stack, it is + if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) { + /* Not a Version Negotiation packet */ + version = ngtcp2_get_uint32(&pkt[1]); + if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) { + if (conn->server) { + if (is_unrecoverable_error((int)nread)) { + /* If server gets crypto error from TLS stack, it is + unrecoverable, therefore drop connection. */ + return nread; + } + + /* If server discards first Initial, then drop connection + state. This is because SCID in packet might be corrupted + and the current connection state might wrongly discard + valid packet and prevent the handshake from + completing. */ + if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { + return NGTCP2_ERR_DROP_CONN; + } + + return (ngtcp2_ssize)dgramlen; + } + /* client */ + if (is_unrecoverable_error((int)nread)) { + /* If client gets crypto error from TLS stack, it is unrecoverable, therefore drop connection. */ return nread; } - - /* If server discards first Initial, then drop connection - state. This is because SCID in packet might be corrupted - and the current connection state might wrongly discard - valid packet and prevent the handshake from - completing. */ - if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { - return NGTCP2_ERR_DROP_CONN; - } - return (ngtcp2_ssize)dgramlen; } - /* client */ - if (is_crypto_error((int)nread)) { - /* If client gets crypto error from TLS stack, it is - unrecoverable, therefore drop connection. */ - return nread; - } - return (ngtcp2_ssize)dgramlen; } if (nread == NGTCP2_ERR_DISCARD_PKT) { @@ -6298,11 +6748,9 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, max_tx_offset = 0; } - rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, - max_tx_offset, stream_user_data, conn->mem); - if (rv != 0) { - return rv; - } + ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, + max_tx_offset, stream_user_data, &conn->frc_objalloc, + conn->mem); rv = ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id, strm); @@ -6341,7 +6789,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, int rv; uint64_t offset; uint32_t sdflags; - int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; + int handshake_completed = conn_is_handshake_completed(conn); if (!strm->rx.rob) { return 0; @@ -6573,14 +7021,14 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return 0; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } /* TODO Perhaps, call new_stream callback? */ rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -6686,7 +7134,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { if (fin) { sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY; } rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, @@ -6726,7 +7174,7 @@ static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, ngtcp2_frame_chain *frc; ngtcp2_pktns *pktns = &conn->pktns; - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -6759,7 +7207,7 @@ static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm, ngtcp2_frame_chain *frc; ngtcp2_pktns *pktns = &conn->pktns; - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -7006,13 +7454,13 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, /* Frame is received reset before we create ngtcp2_strm object. */ - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -7037,6 +7485,11 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + if (ngtcp2_strm_is_tx_queued(strm) && !ngtcp2_strm_streamfrq_empty(strm)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } + ngtcp2_strm_streamfrq_clear(strm); return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); @@ -7088,18 +7541,18 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) && (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { - len = ngtcp2_ringbuf_len(&conn->dcid.retired); + len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); if (check_stateless_reset(dcid, path, &sr)) { break; } } if (i == len) { - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (check_stateless_reset(dcid, path, &sr)) { break; } @@ -7115,6 +7568,8 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_sr(&conn->log, &sr); + ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr); + return conn_call_recv_stateless_reset(conn, &sr); } @@ -7172,16 +7627,20 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, if (rv != 0) { return rv; } + if (i == 0) { ngtcp2_ringbuf_pop_front(rb); - } else if (i == ngtcp2_ringbuf_len(rb) - 1) { + continue; + } + + if (i == ngtcp2_ringbuf_len(rb) - 1) { ngtcp2_ringbuf_pop_back(rb); break; - } else { - last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); - ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(rb); } + + last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); + ngtcp2_dcid_copy(dcid, last); + ngtcp2_ringbuf_pop_back(rb); } return 0; @@ -7235,10 +7694,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } } - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { @@ -7249,10 +7708,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } } - len = ngtcp2_ringbuf_len(&conn->dcid.unused); + len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i); rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { @@ -7266,13 +7725,13 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, if (conn->dcid.retire_prior_to < fr->retire_prior_to) { conn->dcid.retire_prior_to = fr->retire_prior_to; - rv = - conn_retire_dcid_prior_to(conn, &conn->dcid.bound, fr->retire_prior_to); + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb, + fr->retire_prior_to); if (rv != 0) { return rv; } - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb, conn->dcid.retire_prior_to); if (rv != 0) { return rv; @@ -7286,13 +7745,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, For example, a peer might send seq = 50000 and retire_prior_to = 50000. Then send NEW_CONNECTION_ID frames with seq < 50000. */ - /* TODO we might queue lots of RETIRE_CONNECTION_ID frame here - because conn->dcid.num_retire_queued is incremented when the - frame is serialized. */ - if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { - return conn_retire_dcid_seq(conn, fr->seq); - } - return 0; + return conn_retire_dcid_seq(conn, fr->seq); } if (found) { @@ -7312,7 +7765,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); } - len = ngtcp2_ringbuf_len(&conn->dcid.unused); + len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { ++extra_dcid; @@ -7339,7 +7792,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return 0; } - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); return 0; @@ -7364,7 +7817,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, int rv; if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return 0; } @@ -7373,7 +7826,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); if (pv) { if (conn->dcid.current.seq == pv->dcid.seq) { ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); @@ -7385,7 +7838,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { @@ -7395,13 +7848,13 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, if (pv) { if (pv->dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { rv = conn_retire_dcid(conn, &pv->dcid, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && pv->dcid.seq == pv->fallback_dcid.seq) { @@ -7409,7 +7862,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &pv->dcid); if (rv != 0) { @@ -7425,15 +7878,15 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); if (rv != 0) { @@ -7583,7 +8036,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { ngtcp2_pv *pv; ngtcp2_dcid *dcid; - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return 0; } @@ -7602,7 +8055,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { assert(conn->pv == NULL); - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); ngtcp2_dcid_set_path(dcid, &ps.path); pto = conn_compute_pto(conn, &conn->pktns); @@ -7616,7 +8069,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); conn->pv = pv; return conn_call_activate_dcid(conn, &pv->dcid); @@ -7777,9 +8230,11 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { /* * conn_rotate_keys rotates keys. The current key moves to old key, - * and new key moves to the current key. + * and new key moves to the current key. If the local endpoint + * initiated this key update, pass nonzero as |initiator|. */ -static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { +static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num, + int initiator) { ngtcp2_pktns *pktns = &conn->pktns; assert(conn->crypto.key_update.new_rx_ckm); @@ -7803,6 +8258,9 @@ static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + if (initiator) { + conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR; + } } /* @@ -7857,7 +8315,16 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, conn_reset_congestion_state(conn, ts); conn->dcid.current.bytes_recv += dgramlen; conn_reset_ecn_validation_state(conn); - return conn_abort_pv(conn, ts); + + rv = conn_abort_pv(conn, ts); + if (rv != 0) { + return rv; + } + + /* Run PMTUD just in case if it is prematurely aborted */ + assert(!conn->pmtud); + + return conn_start_pmtud(conn); } remote_addr_cmp = @@ -7891,10 +8358,10 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, initial_pto = conn_compute_initial_pto(conn, &conn->pktns); timeout = 3 * ngtcp2_max(pto, initial_pto); - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_CON, @@ -7902,13 +8369,13 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_dcid_copy(&dcid, bound_dcid); if (i == 0) { - ngtcp2_ringbuf_pop_front(&conn->dcid.bound); - } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) { - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); + } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } else { - last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1); + last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1); ngtcp2_dcid_copy(bound_dcid, last); - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } require_new_cid = 0; @@ -7924,11 +8391,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, if (i == len) { if (require_new_cid) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused, 0)); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0)); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &dcid); if (rv != 0) { @@ -7978,6 +8445,8 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, conn_reset_ecn_validation_state(conn); + ngtcp2_conn_stop_pmtud(conn); + if (conn->pv) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_PTV, @@ -7994,7 +8463,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, } /* - * conn_recv_pkt_from_new_path is called when a Short packet is + * conn_recv_pkt_from_new_path is called when a 1RTT packet is * received from new path (not current path). This packet would be a * packet which only contains probing frame, or reordered packet, or a * path is being validated. @@ -8039,7 +8508,6 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, ngtcp2_dcid_set_path(bound_dcid, path); bound_dcid->bytes_recv += dgramlen; - conn_update_dcid_max_udp_payload_size(conn, bound_dcid, dgramlen); return 0; } @@ -8236,7 +8704,14 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) { + if (pktlen < (size_t)nread + hd.len) { + return NGTCP2_ERR_DISCARD_PKT; + } + + assert(conn->negotiated_version); + + if (hd.version != conn->client_chosen_version && + hd.version != conn->negotiated_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -8257,6 +8732,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, "delayed Initial packet was discarded"); return (ngtcp2_ssize)pktlen; case NGTCP2_PKT_HANDSHAKE: + if (hd.version != conn->negotiated_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (!conn->hs_pktns) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "delayed Handshake packet was discarded"); @@ -8272,7 +8751,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, decrypt = conn->callbacks.decrypt; break; case NGTCP2_PKT_0RTT: - if (!conn->server) { + if (!conn->server || hd.version != conn->client_chosen_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -8317,7 +8796,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); + (size_t)nread, hp_ctx, hp_mask); if (nwrite < 0) { if (ngtcp2_err_is_fatal((int)nwrite)) { return nwrite; @@ -8341,7 +8820,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - if (hd.type == NGTCP2_PKT_SHORT) { + if (hd.type == NGTCP2_PKT_1RTT) { key_phase_bit_changed = conn_key_phase_changed(conn, &hd); } @@ -8351,7 +8830,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } if (key_phase_bit_changed) { - assert(hd.type == NGTCP2_PKT_SHORT); + assert(hd.type == NGTCP2_PKT_1RTT); ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE"); @@ -8394,7 +8873,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, assert(NGTCP2_ERR_DECRYPT == nwrite); - if (hd.type == NGTCP2_PKT_SHORT && + if (hd.type == NGTCP2_PKT_1RTT && ++conn->crypto.decryption_failure_count >= pktns->crypto.ctx.max_decryption_failure) { return NGTCP2_ERR_AEAD_LIMIT_REACHED; @@ -8698,7 +9177,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if (conn->server && hd.type == NGTCP2_PKT_SHORT && + if (conn->server && hd.type == NGTCP2_PKT_1RTT && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num && !conn_path_validation_in_progress(conn, path)) { @@ -8726,10 +9205,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if (hd.type == NGTCP2_PKT_SHORT) { + if (hd.type == NGTCP2_PKT_1RTT) { if (ckm == conn->crypto.key_update.new_rx_ckm) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys"); - conn_rotate_keys(conn, hd.pkt_num); + conn_rotate_keys(conn, hd.pkt_num, /* initiator = */ 0); } else if (ckm->pkt_num > hd.pkt_num) { ckm->pkt_num = hd.pkt_num; } @@ -8767,17 +9246,13 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { - conn_update_dcid_max_udp_payload_size(conn, &conn->dcid.current, dgramlen); - } - return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING : (ngtcp2_ssize)pktlen; } /* - * conn_process_buffered_protected_pkt processes buffered 0RTT or - * Short packets. + * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT + * packets. * * This function returns 0 if it succeeds, or the same negative error * codes from conn_recv_pkt. @@ -8942,7 +9417,7 @@ static int conn_handshake_completed(ngtcp2_conn *conn) { /* * conn_recv_cpkt processes compound packet after handshake. The - * buffer pointed by |pkt| might contain multiple packets. The Short + * buffer pointed by |pkt| might contain multiple packets. The 1RTT * packet must be the last one because it does not have payload length * field. * @@ -9000,11 +9475,11 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, * retired path list. */ static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { - size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired); + size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); ngtcp2_dcid *dcid; for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); if (ngtcp2_path_eq(&dcid->ps.path, path)) { return 1; } @@ -9024,7 +9499,7 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { assert(conn->server); - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -9044,10 +9519,10 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { * |pktlen| is the length of the buffer. |path| is the network path. * * This function returns the number of bytes processed. Unless the - * last packet is Short packet and an application decryption key has + * last packet is 1RTT packet and an application decryption key has * been installed, it returns |pktlen| if it succeeds. If it finds - * Short packet and an application decryption key has been installed, - * it returns the number of bytes just before Short packet begins. + * 1RTT packet and an application decryption key has been installed, + * it returns the number of bytes just before 1RTT packet begins. * * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: (TBD). @@ -9084,9 +9559,8 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, } } - if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED | - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) == - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) { + if (conn_is_handshake_completed(conn) && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { rv = conn_handshake_completed(conn); if (rv != 0) { return rv; @@ -9159,7 +9633,7 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, conn_discard_initial_state(conn, ts); } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { /* If server hits amplification limit, it cancels loss detection timer. If server receives a packet from client, the limit is increased and server can send more. If server has @@ -9183,10 +9657,10 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, } if ((size_t)nread < pktlen) { - /* We have Short packet and application rx key, but the + /* We have 1RTT packet and application rx key, but the handshake has not completed yet. */ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering Short packet len=%zu", + "buffering 1RTT packet len=%zu", pktlen - (size_t)nread); rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread, @@ -9196,7 +9670,7 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, return rv; } - return (ssize_t)pktlen; + return (ngtcp2_ssize)pktlen; } return nread; @@ -9229,6 +9703,13 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, return rv; } + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + conn->pktns.rtb.persistent_congestion_start_ts = ts; /* Re-arm loss detection timer here after handshake has been @@ -9252,6 +9733,7 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts) { int rv = 0; ngtcp2_ssize nread = 0; + const ngtcp2_pkt_info zero_pi = {0}; (void)pkt_info_version; conn->log.last_ts = ts; @@ -9273,6 +9755,10 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } + if (!pi) { + pi = &zero_pi; + } + switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: @@ -9362,9 +9848,10 @@ static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + uint8_t flags, ngtcp2_tstamp ts) { - return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, - NGTCP2_WRITE_PKT_FLAG_NONE, ts); + return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, flags, + ts); } /* @@ -9413,7 +9900,7 @@ static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) { /* * conn_write_handshake writes QUIC handshake packets to the buffer * pointed by |dest| of length |destlen|. |write_datalen| specifies - * the expected length of 0RTT or Short packet payload. Specify 0 to + * the expected length of 0RTT or 1RTT packet payload. Specify 0 to * |write_datalen| if there is no such data. * * This function returns the number of bytes written to the buffer, or @@ -9467,8 +9954,11 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } if (pending_early_datalen) { - early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite, - destlen - (size_t)nwrite, ts); + early_spktlen = conn_retransmit_retry_early( + conn, pi, dest + nwrite, destlen - (size_t)nwrite, + nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE, + ts); if (early_spktlen < 0) { assert(ngtcp2_err_is_fatal((int)early_spktlen)); @@ -9503,9 +9993,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, destlen -= (size_t)nwrite; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { - nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts); + nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, + NGTCP2_WRITE_PKT_FLAG_NONE, ts); if (nwrite < 0) { return nwrite; } @@ -9546,10 +10037,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->state = NGTCP2_CS_POST_HANDSHAKE; if (conn->remote.transport_params.preferred_address_present) { - assert(!ngtcp2_ringbuf_full(&conn->dcid.unused)); + assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb)); paddr = &conn->remote.transport_params.preferred_address; - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); @@ -9573,6 +10064,13 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_process_early_rtb(conn); + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + return res; case NGTCP2_CS_SERVER_INITIAL: nwrite = @@ -9656,6 +10154,7 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, uint64_t write_datalen = 0; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + uint32_t version; assert(!conn->server); @@ -9716,8 +10215,10 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, /* If spktlen > 0, we are making a compound packet. If Initial packet is written, we have to pad bytes to 0-RTT packet. */ + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; if (spktlen > 0 && - ngtcp2_pkt_get_type_long(dest[0]) == NGTCP2_PKT_INITIAL) { + ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) { wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; conn->pkt.require_padding = 1; } else { @@ -9766,7 +10267,7 @@ void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { } int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { - return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + return conn_is_handshake_completed(conn) && (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED); } @@ -9803,12 +10304,6 @@ int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) { return (int)nread; } - if (p->version != NGTCP2_PROTO_VER_V1 && - (p->version < NGTCP2_PROTO_VER_DRAFT_MIN || - NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) { - return NGTCP2_ERR_VERSION_NEGOTIATION; - } - switch (p->type) { case NGTCP2_PKT_INITIAL: break; @@ -9880,6 +10375,57 @@ int ngtcp2_conn_install_initial_key( return 0; } +int ngtcp2_conn_install_vneg_initial_key( + ngtcp2_conn *conn, uint32_t version, + const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, + const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { + int rv; + + assert(ivlen >= 8); + + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + conn->vneg.rx.hp_ctx.native_handle = NULL; + + if (conn->vneg.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); + conn->vneg.rx.ckm = NULL; + } + + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + conn->vneg.tx.hp_ctx.native_handle = NULL; + + if (conn->vneg.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); + conn->vneg.tx.ckm = NULL; + } + + rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + /* Take owner ship after we are sure that no failure occurs, so that + caller can delete these contexts on failure. */ + conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx; + conn->vneg.rx.hp_ctx = *rx_hp_ctx; + conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx; + conn->vneg.tx.hp_ctx = *tx_hp_ctx; + conn->vneg.version = version; + + return 0; +} + int ngtcp2_conn_install_rx_handshake_key( ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { @@ -10029,7 +10575,7 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return NGTCP2_ERR_INVALID_STATE; } - conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM); + conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1); return 0; } @@ -10052,8 +10598,8 @@ static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn, ngtcp2_dcid *dcid, *last; int rv; - for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); assert(dcid->cid.datalen); @@ -10068,19 +10614,19 @@ static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn, } if (i == 0) { - ngtcp2_ringbuf_pop_front(&conn->dcid.bound); + ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); continue; } - if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) { - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); break; } - last = ngtcp2_ringbuf_get(&conn->dcid.bound, - ngtcp2_ringbuf_len(&conn->dcid.bound) - 1); + last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, + ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1); ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } return 0; @@ -10101,27 +10647,34 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { res = ngtcp2_pv_next_expiry(conn->pv); } + if (conn->pmtud) { + res = ngtcp2_min(res, conn->pmtud->expiry); + } + if (!ngtcp2_pq_empty(&conn->scid.used)) { scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); if (scid->retired_ts != UINT64_MAX) { - res = ngtcp2_min(res, scid->retired_ts + pto); + t = scid->retired_ts + pto; + res = ngtcp2_min(res, t); } } - if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - res = ngtcp2_min(res, dcid->retired_ts + pto); + if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); + t = dcid->retired_ts + pto; + res = ngtcp2_min(res, t); } if (conn->dcid.current.cid.datalen) { - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); assert(dcid->cid.datalen); assert(dcid->bound_ts != UINT64_MAX); - res = ngtcp2_min(res, dcid->bound_ts + 3 * pto); + t = dcid->bound_ts + 3 * pto; + res = ngtcp2_min(res, t); } } @@ -10145,7 +10698,7 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { } static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) { - if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + if (conn_is_handshake_completed(conn) || conn->local.settings.handshake_timeout == UINT64_MAX) { return UINT64_MAX; } @@ -10161,11 +10714,13 @@ ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn); ngtcp2_tstamp t5 = conn_keep_alive_expiry(conn); ngtcp2_tstamp t6 = conn_handshake_expiry(conn); + ngtcp2_tstamp t7 = ngtcp2_conn_get_idle_expiry(conn); ngtcp2_tstamp res = ngtcp2_min(t1, t2); res = ngtcp2_min(res, t3); res = ngtcp2_min(res, t4); res = ngtcp2_min(res, t5); res = ngtcp2_min(res, t6); + res = ngtcp2_min(res, t7); return ngtcp2_min(res, conn->tx.pacing.next_ts); } @@ -10173,6 +10728,10 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { int rv; ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + if (ngtcp2_conn_get_idle_expiry(conn) <= ts) { + return NGTCP2_ERR_IDLE_CLOSE; + } + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); conn_cancel_expired_keep_alive_timer(conn, ts); @@ -10185,6 +10744,13 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_pv_cancel_expired_timer(conn->pv, ts); } + if (conn->pmtud) { + ngtcp2_pmtud_handle_expiry(conn->pmtud, ts); + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + } + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); if (rv != 0) { @@ -10211,7 +10777,7 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + if (!conn_is_handshake_completed(conn) && conn->local.settings.handshake_timeout != UINT64_MAX && conn->local.settings.initial_ts + conn->local.settings.handshake_timeout <= @@ -10288,6 +10854,42 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); } +/* + * select_preferred_version selects the most preferred version. + * |fallback_version| is chosen if no preference is made, or + * |preferred_versions| does not include any of |chosen_version| or + * |other_versions|. |chosen_version| is treated as an extra other + * version. + */ +static uint32_t select_preferred_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + uint32_t chosen_version, + const uint8_t *other_versions, + size_t other_versionslen, + uint32_t fallback_version) { + size_t i, j; + + if (!preferred_versionslen || + (!other_versionslen && chosen_version == fallback_version)) { + return fallback_version; + } + + for (i = 0; i < preferred_versionslen; ++i) { + if (preferred_versions[i] == chosen_version) { + return chosen_version; + } + for (j = 0; j < other_versionslen; j += sizeof(uint32_t)) { + if (preferred_versions[i] != ngtcp2_get_uint32(&other_versions[j])) { + continue; + } + + return preferred_versions[i]; + } + } + + return fallback_version; +} + /* * conn_client_validate_transport_params validates |params| as client. * |params| must be sent with Encrypted Extensions. @@ -10295,12 +10897,11 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * NGTCP2_ERR_PROTO - * Validation against either of original_dcid and retry_scid is - * failed. * NGTCP2_ERR_TRANSPORT_PARAM * params contains preferred address but server chose zero-length * connection ID. + * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE + * Validation against version negotiation parameters failed. */ static int conn_client_validate_transport_params(ngtcp2_conn *conn, @@ -10325,9 +10926,64 @@ conn_client_validate_transport_params(ngtcp2_conn *conn, return NGTCP2_ERR_TRANSPORT_PARAM; } + if (params->version_info_present) { + if (conn->negotiated_version != params->version_info.chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + assert(vneg_other_versions_includes(conn->vneg.other_versions, + conn->vneg.other_versionslen, + conn->negotiated_version)); + } else if (conn->client_chosen_version != conn->negotiated_version || + conn->client_chosen_version != + conn->local.settings.original_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + /* When client reacted upon Version Negotiation */ + if (conn->local.settings.original_version != conn->client_chosen_version) { + assert(params->version_info_present); + + /* Server choose original version after Version Negotiation. + Draft does not say this particular case, but this smells like + misbehaved server because server should accept original_version + in the original connection. */ + if (conn->local.settings.original_version == + params->version_info.chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + /* Check version downgrade on incompatible version negotiation. */ + if (params->version_info.other_versionslen == 0) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + if (conn->client_chosen_version != + select_preferred_version(conn->vneg.preferred_versions, + conn->vneg.preferred_versionslen, + params->version_info.chosen_version, + params->version_info.other_versions, + params->version_info.other_versionslen, + /* fallback_version = */ 0)) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + } + return 0; } +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, + const ngtcp2_version_info *version_info) { + assert(conn->server); + assert(conn->client_chosen_version == version_info->chosen_version); + + return select_preferred_version( + conn->vneg.preferred_versions, conn->vneg.preferred_versionslen, + version_info->chosen_version, version_info->other_versions, + version_info->other_versionslen, version_info->chosen_version); +} + int ngtcp2_conn_set_remote_transport_params_versioned( ngtcp2_conn *conn, int transport_params_version, const ngtcp2_transport_params *params) { @@ -10354,7 +11010,28 @@ int ngtcp2_conn_set_remote_transport_params_versioned( return NGTCP2_ERR_TRANSPORT_PARAM; } - if (!conn->server) { + if (conn->server) { + if (params->version_info_present) { + if (params->version_info.chosen_version != conn->client_chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + conn->negotiated_version = + ngtcp2_conn_server_negotiate_version(conn, ¶ms->version_info); + if (conn->negotiated_version != conn->client_chosen_version) { + rv = conn_call_version_negotiation(conn, conn->negotiated_version, + &conn->rcid); + if (rv != 0) { + return rv; + } + } + } else { + conn->negotiated_version = conn->client_chosen_version; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is %08x", conn->negotiated_version); + } else { rv = conn_client_validate_transport_params(conn, params); if (rv != 0) { return rv; @@ -10414,9 +11091,16 @@ void ngtcp2_conn_set_early_remote_transport_params_versioned( params->initial_max_stream_data_bidi_remote; p->initial_max_stream_data_uni = params->initial_max_stream_data_uni; p->initial_max_data = params->initial_max_data; - p->active_connection_id_limit = params->active_connection_id_limit; + p->active_connection_id_limit = + ngtcp2_max(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); p->max_idle_timeout = params->max_idle_timeout; - p->max_udp_payload_size = params->max_udp_payload_size; + if (!params->max_udp_payload_size) { + p->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + } else { + p->max_udp_payload_size = + ngtcp2_max(NGTCP2_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); + } p->disable_active_migration = params->disable_active_migration; p->max_datagram_frame_size = params->max_datagram_frame_size; @@ -10521,6 +11205,16 @@ void ngtcp2_conn_get_local_transport_params_versioned( (void)transport_params_version; *params = conn->local.transport_params; + + if (conn->server) { + params->version_info.chosen_version = conn->negotiated_version; + } else { + params->version_info.chosen_version = conn->client_chosen_version; + } + + params->version_info.other_versions = conn->vneg.other_versions; + params->version_info.other_versionslen = conn->vneg.other_versionslen; + params->version_info_present = 1; } int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, @@ -10532,7 +11226,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, return NGTCP2_ERR_STREAM_ID_BLOCKED; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } @@ -10540,7 +11234,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id, stream_user_data); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -10559,7 +11253,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, return NGTCP2_ERR_STREAM_ID_BLOCKED; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } @@ -10567,7 +11261,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id, stream_user_data); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); @@ -10603,14 +11297,9 @@ static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - ngtcp2_pktns *pktns = &conn->pktns; - int no_retrans = (!in_pktns || in_pktns->rtb.num_retransmittable == 0) && - (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) && - pktns->rtb.num_retransmittable == 0; ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize nwrite; + int undersized; nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest, destlen, vmsg, ts); @@ -10622,11 +11311,20 @@ static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, conn->rst.is_cwnd_limited = 1; } - if (nwrite == 0 && cstat->bytes_in_flight < cstat->cwnd && no_retrans) { - conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight; + if (vmsg == NULL && cstat->bytes_in_flight < cstat->cwnd && + conn->tx.strmq_nretrans == 0) { + if (conn->local.settings.no_udp_payload_size_shaping) { + undersized = (size_t)nwrite < conn->local.settings.max_udp_payload_size; + } else { + undersized = (size_t)nwrite < conn->dcid.current.max_udp_payload_size; + } + + if (undersized) { + conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight; - if (conn->rst.app_limited == 0) { - conn->rst.app_limited = cstat->max_udp_payload_size; + if (conn->rst.app_limited == 0) { + conn->rst.app_limited = cstat->max_udp_payload_size; + } } } @@ -10724,7 +11422,6 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; - ngtcp2_pktns *pktns = &conn->pktns; size_t origlen; size_t origdestlen = destlen; int rv; @@ -10739,7 +11436,6 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_ksl_it it; ngtcp2_rtb_entry *rtbent; (void)pkt_info_version; - (void)pktns; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10764,7 +11460,10 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); - if (nwrite < 0) { + /* We might be unable to write a packet because of depletion of + congestion window budget, perhaps due to packet loss that + shrinks the window drastically. */ + if (nwrite <= 0) { return nwrite; } if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { @@ -10773,17 +11472,19 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, assert(nwrite); assert(dest[0] & NGTCP2_HEADER_FORM_BIT); + assert(conn->negotiated_version); - if (ngtcp2_pkt_get_type_long(dest[0]) == NGTCP2_PKT_INITIAL) { + if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) == + NGTCP2_PKT_INITIAL) { /* We have added padding already, but in that case, there is no - space left to write Short packet. */ + space left to write 1RTT packet. */ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; } res = nwrite; dest += nwrite; destlen -= (size_t)nwrite; - /* Break here so that we can coalesces Short packets. */ + /* Break here so that we can coalesces 1RTT packet. */ break; case NGTCP2_CS_SERVER_INITIAL: case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: @@ -10854,7 +11555,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (rtbent->hd.pkt_num != prev_in_pkt_num && (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { /* We have added padding already, but in that case, there - is no space left to write Short packet. */ + is no space left to write 1RTT packet. */ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; } } @@ -10875,7 +11576,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, return 0; } - assert(pktns->crypto.tx.ckm); + assert(conn->pktns.crypto.tx.ckm); if (conn_check_pkt_num_exhausted(conn)) { return NGTCP2_ERR_PKT_NUM_EXHAUSTED; @@ -10906,7 +11607,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, } /* dest and destlen have already been adjusted in ppe in the first run. They are adjusted for probe packet later. */ - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); goto fin; } else { @@ -10937,6 +11638,15 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, goto fin; } } + + if (conn->pmtud && + (!conn->hs_pktns || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) == 0)) { + nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts); + if (nwrite) { + goto fin; + } + } } if (conn->server && @@ -10979,13 +11689,13 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, "transmit probe pkt left=%zu", conn->pktns.rtb.probe_pkt_left); - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); goto fin; } - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); if (nwrite) { assert(nwrite != NGTCP2_ERR_NOBUF); @@ -10993,7 +11703,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, } if (res == 0) { - nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts); + nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts); } fin: @@ -11021,6 +11731,7 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_ssize res = 0, nwrite; ngtcp2_frame fr; + uint8_t flags = NGTCP2_WRITE_PKT_FLAG_NONE; fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; @@ -11032,8 +11743,9 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_type != NGTCP2_PKT_INITIAL) { if (in_pktns && conn->server) { nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid, - &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, + NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; } @@ -11047,7 +11759,8 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, hs_pktns->crypto.tx.ckm) { nwrite = ngtcp2_conn_write_single_frame_pkt( conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, - &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; } @@ -11058,8 +11771,12 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } + if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) { + flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr, + conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { @@ -11075,15 +11792,15 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return res; } -ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( - ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, - ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t error_code, - const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts) { +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; uint8_t pkt_type; ngtcp2_ssize nwrite; - (void)pkt_info_version; + uint64_t server_tx_left; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -11094,9 +11811,10 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: + return NGTCP2_ERR_INVALID_STATE; case NGTCP2_CS_CLOSING: case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; + return 0; default: break; } @@ -11105,13 +11823,20 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (conn->server) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = ngtcp2_min(destlen, server_tx_left); + } + if (conn->state == NGTCP2_CS_POST_HANDSHAKE || (conn->server && conn->pktns.crypto.tx.ckm)) { - pkt_type = NGTCP2_PKT_SHORT; + pkt_type = NGTCP2_PKT_1RTT; } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { pkt_type = NGTCP2_PKT_HANDSHAKE; } else if (in_pktns && in_pktns->crypto.tx.ckm) { @@ -11133,14 +11858,14 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( return nwrite; } -ngtcp2_ssize ngtcp2_conn_write_application_close_versioned( - ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, - ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t app_error_code, - const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts) { +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t app_error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; ngtcp2_ssize res = 0; ngtcp2_frame fr; - (void)pkt_info_version; + uint64_t server_tx_left; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -11151,9 +11876,10 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned( switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: + return NGTCP2_ERR_INVALID_STATE; case NGTCP2_CS_CLOSING: case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; + return 0; default: break; } @@ -11162,10 +11888,17 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned( ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (conn->server) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = ngtcp2_min(destlen, server_tx_left); + } + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { nwrite = conn_write_connection_close(conn, pi, dest, destlen, conn->hs_pktns->crypto.tx.ckm @@ -11197,8 +11930,8 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned( fr.connection_close.reason = (uint8_t *)reason; nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr, - NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; @@ -11215,6 +11948,92 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned( return res; } +static void +connection_close_error_init(ngtcp2_connection_close_error *ccerr, + ngtcp2_connection_close_error_code_type type, + uint64_t error_code, const uint8_t *reason, + size_t reasonlen) { + ccerr->type = type; + ccerr->error_code = error_code; + ccerr->frame_type = 0; + ccerr->reason = (uint8_t *)reason; + ccerr->reasonlen = reasonlen; +} + +void ngtcp2_connection_close_error_default( + ngtcp2_connection_close_error *ccerr) { + connection_close_error_init(ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + NGTCP2_NO_ERROR, NULL, 0); +} + +void ngtcp2_connection_close_error_set_transport_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen) { + connection_close_error_init(ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + error_code, reason, reasonlen); +} + +void ngtcp2_connection_close_error_set_transport_error_liberr( + ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason, + size_t reasonlen) { + switch (liberr) { + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + connection_close_error_init( + ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + NGTCP2_NO_ERROR, reason, reasonlen); + + return; + case NGTCP2_ERR_IDLE_CLOSE: + connection_close_error_init( + ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE, + NGTCP2_NO_ERROR, reason, reasonlen); + + return; + }; + + ngtcp2_connection_close_error_set_transport_error( + ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason, + reasonlen); +} + +void ngtcp2_connection_close_error_set_transport_error_tls_alert( + ngtcp2_connection_close_error *ccerr, uint8_t tls_alert, + const uint8_t *reason, size_t reasonlen) { + ngtcp2_connection_close_error_set_transport_error( + ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, reason, reasonlen); +} + +void ngtcp2_connection_close_error_set_application_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen) { + connection_close_error_init( + ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, error_code, + reason, reasonlen); +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts) { + (void)pkt_info_version; + + switch (ccerr->type) { + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT: + return ngtcp2_conn_write_connection_close_pkt( + conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, + ccerr->reasonlen, ts); + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION: + return ngtcp2_conn_write_application_close_pkt( + conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, + ccerr->reasonlen, ts); + default: + return 0; + } +} + int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { return conn->state == NGTCP2_CS_CLOSING; } @@ -11239,11 +12058,15 @@ int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) { if (ngtcp2_strm_is_tx_queued(strm)) { ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); + if (!ngtcp2_strm_streamfrq_empty(strm)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } } fin: ngtcp2_strm_free(strm); - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -11444,8 +12267,12 @@ const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) { return &conn->rcid; } +uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) { + return conn->client_chosen_version; +} + uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { - return conn->version; + return conn->negotiated_version; } static int delete_strms_pq_each(void *data, void *ptr) { @@ -11454,10 +12281,14 @@ static int delete_strms_pq_each(void *data, void *ptr) { if (ngtcp2_strm_is_tx_queued(s)) { ngtcp2_pq_remove(&conn->tx.strmq, &s->pe); + if (!ngtcp2_strm_streamfrq_empty(s)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } } ngtcp2_strm_free(s); - ngtcp2_mem_free(conn->mem, s); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); return 0; } @@ -11496,11 +12327,15 @@ static void conn_discard_early_data_state(ngtcp2_conn *conn) { for (pfrc = &conn->pktns.tx.frq; *pfrc;) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); } } int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { + if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { + return 0; + } + conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; conn_discard_early_data_state(conn); @@ -11508,24 +12343,22 @@ int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { return 0; } -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration min_rtt; - - cstat->latest_rtt = rtt; if (cstat->min_rtt == UINT64_MAX) { + cstat->latest_rtt = rtt; cstat->min_rtt = rtt; cstat->smoothed_rtt = rtt; cstat->rttvar = rtt / 2; cstat->first_rtt_sample_ts = ts; } else { - min_rtt = ngtcp2_min(cstat->min_rtt, rtt); if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { ack_delay = ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay); - } else if (ack_delay > 0 && rtt < cstat->min_rtt + ack_delay) { + } else if (ack_delay > 0 && rtt >= cstat->min_rtt && + rtt < cstat->min_rtt + ack_delay) { /* Ignore RTT sample if adjusting ack_delay causes the sample less than min_rtt before handshake confirmation. */ ngtcp2_log_info( @@ -11535,14 +12368,16 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, (uint64_t)(rtt / NGTCP2_MILLISECONDS), (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); - return; + return NGTCP2_ERR_INVALID_ARGUMENT; } - if (rtt > min_rtt + ack_delay) { + cstat->latest_rtt = rtt; + cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt); + + if (rtt >= cstat->min_rtt + ack_delay) { rtt -= ack_delay; } - cstat->min_rtt = min_rtt; cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt ? rtt - cstat->smoothed_rtt : cstat->smoothed_rtt - rtt)) / @@ -11559,6 +12394,8 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, cstat->smoothed_rtt / NGTCP2_MILLISECONDS, cstat->rttvar / NGTCP2_MILLISECONDS, (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); + + return 0; } void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn, @@ -11578,7 +12415,7 @@ static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn, ngtcp2_tstamp earliest_ts = UINT64_MAX; for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) { - if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 || + if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || (times[i] == UINT64_MAX || (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) || (i == NGTCP2_PKTNS_ID_APPLICATION && @@ -11617,7 +12454,7 @@ static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn, (1ULL << pto_count); for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) { - if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 || + if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || (times[i] == UINT64_MAX || (i == NGTCP2_PKTNS_ID_APPLICATION && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { @@ -11661,9 +12498,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) && - (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) && - (pktns->rtb.num_retransmittable == 0 || + if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) && + (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) && + (pktns->rtb.num_pto_eliciting == 0 || !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) && (conn->server || (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | @@ -11727,23 +12564,23 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } - if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn->server && !conn_is_handshake_completed(conn)) { if (hs_pktns->crypto.tx.ckm) { hs_pktns->rtb.probe_pkt_left = 1; } else { in_pktns->rtb.probe_pkt_left = 1; } } else { - if (in_pktns && in_pktns->rtb.num_retransmittable) { + if (in_pktns && in_pktns->rtb.num_pto_eliciting) { in_pktns->rtb.probe_pkt_left = 1; assert(hs_pktns); - if (conn->server && hs_pktns->rtb.num_retransmittable) { + if (conn->server && hs_pktns->rtb.num_pto_eliciting) { /* let server coalesce packets */ hs_pktns->rtb.probe_pkt_left = 1; } - } else if (hs_pktns && hs_pktns->rtb.num_retransmittable) { + } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) { hs_pktns->rtb.probe_pkt_left = 1; } else { conn->pktns.rtb.probe_pkt_left = 2; @@ -11821,7 +12658,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, return rv; } - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -11836,7 +12673,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -11856,7 +12693,8 @@ int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token, assert(token); assert(tokenlen); - rv = ngtcp2_frame_chain_new_token_new(&nfrc, &tokenv, conn->mem); + rv = ngtcp2_frame_chain_new_token_objalloc_new( + &nfrc, &tokenv, &conn->frc_objalloc, conn->mem); if (rv != 0) { return rv; } @@ -11932,7 +12770,7 @@ size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { } } - n += ngtcp2_ringbuf_len(&conn->dcid.retired); + n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb); return n; } @@ -11974,9 +12812,9 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { } } - len = ngtcp2_ringbuf_len(&conn->dcid.retired); + len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); copy_dcid_to_cid_token(dest, dcid); ++dest; } @@ -11995,17 +12833,20 @@ void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) { conn->dcid.current.ps.path.user_data = path_user_data; } +const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) { + return &conn->dcid.current.ps.path; +} + +size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn) { + return conn->local.settings.max_udp_payload_size; +} + size_t ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn) { if (conn->local.settings.no_udp_payload_size_shaping) { - return conn->local.settings.max_udp_payload_size; + return ngtcp2_conn_get_max_udp_payload_size(conn); } - return ngtcp2_min(conn->local.settings.max_udp_payload_size, - conn->dcid.current.max_udp_payload_size); -} - -const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) { - return &conn->dcid.current.ps.path; + return conn->dcid.current.max_udp_payload_size; } static int conn_initiate_migration_precheck(ngtcp2_conn *conn, @@ -12017,7 +12858,7 @@ static int conn_initiate_migration_precheck(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_STATE; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } @@ -12044,6 +12885,8 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, return rv; } + ngtcp2_conn_stop_pmtud(conn); + if (conn->pv) { rv = conn_abort_pv(conn, ts); if (rv != 0) { @@ -12056,11 +12899,11 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); ngtcp2_dcid_set_path(dcid, path); ngtcp2_dcid_copy(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { @@ -12070,6 +12913,13 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + return 0; } @@ -12097,7 +12947,7 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, } } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); ngtcp2_dcid_set_path(dcid, path); pto = conn_compute_pto(conn, &conn->pktns); @@ -12110,7 +12960,7 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); conn->pv = pv; return conn_call_activate_dcid(conn, &pv->dcid); @@ -12139,6 +12989,17 @@ uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { : conn->local.uni.max_streams - n + 1; } +uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) { + uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; + uint64_t cwnd = conn_get_cwnd(conn); + + if (cwnd > bytes_in_flight) { + return cwnd - bytes_in_flight; + } + + return 0; +} + ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { ngtcp2_duration trpto; ngtcp2_duration idle_timeout; @@ -12146,7 +13007,7 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { /* TODO Remote max_idle_timeout becomes effective after handshake completion. */ - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + if (!conn_is_handshake_completed(conn) || conn->remote.transport_params.max_idle_timeout == 0 || (conn->local.transport_params.max_idle_timeout && conn->local.transport_params.max_idle_timeout < @@ -12160,19 +13021,16 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { return UINT64_MAX; } - trpto = 3 * conn_compute_pto( - conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); + trpto = 3 * conn_compute_pto(conn, conn_is_handshake_completed(conn) + ? &conn->pktns + : conn->hs_pktns); return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); } ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { - return conn_compute_pto(conn, - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); + return conn_compute_pto( + conn, conn_is_handshake_completed(conn) ? &conn->pktns : conn->hs_pktns); } void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, @@ -12275,6 +13133,46 @@ size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) { return conn->cstat.send_quantum; } +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + size_t i; + + if (conn->dcid.retire_unacked.len >= + sizeof(conn->dcid.retire_unacked.seqs) / + sizeof(conn->dcid.retire_unacked.seqs[0])) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + /* Make sure that we do not have a duplicate */ + for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { + if (conn->dcid.retire_unacked.seqs[i] == seq) { + assert(0); + } + } + + conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq; + + return 0; +} + +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + size_t i; + + for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { + if (conn->dcid.retire_unacked.seqs[i] != seq) { + continue; + } + + if (i != conn->dcid.retire_unacked.len - 1) { + conn->dcid.retire_unacked.seqs[i] = + conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1]; + } + + --conn->dcid.retire_unacked.len; + + return; + } +} + void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const ngtcp2_path *path, const uint8_t *data) { @@ -12290,7 +13188,7 @@ void ngtcp2_settings_default_versioned(int settings_version, settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; settings->ack_thresh = 2; - settings->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE; + settings->max_udp_payload_size = 1500 - 48; settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT; } @@ -12368,3 +13266,26 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( } int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); } + +uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + const uint32_t *offered_versions, + size_t offered_versionslen) { + size_t i, j; + + if (!preferred_versionslen || !offered_versionslen) { + return 0; + } + + for (i = 0; i < preferred_versionslen; ++i) { + assert(ngtcp2_is_supported_version(preferred_versions[i])); + + for (j = 0; j < offered_versionslen; ++j) { + if (preferred_versions[i] == offered_versions[j]) { + return preferred_versions[i]; + } + } + } + + return 0; +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h index 6944876130..bd7e641463 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -45,6 +45,7 @@ #include "ngtcp2_bbr.h" #include "ngtcp2_bbr2.h" #include "ngtcp2_pv.h" +#include "ngtcp2_pmtud.h" #include "ngtcp2_cid.h" #include "ngtcp2_buf.h" #include "ngtcp2_ppe.h" @@ -123,6 +124,16 @@ typedef enum { longer than this value, it is truncated. */ #define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024 +/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00 +/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other + than Initial packet should be padded. Initial packet might be + padded based on QUIC requirement regardless of this flag. */ +#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01 +/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come + and it should be encoded into the current packet. */ +#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02 + /* * ngtcp2_max_frame is defined so that it covers the largest ACK * frame. @@ -147,8 +158,13 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, /* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */ #define NGTCP2_CONN_FLAG_NONE 0x00 -/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake - completed. */ +/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares + that TLS handshake has completed. The condition of this + declaration varies between TLS implementations and this flag does + not indicate the completion of QUIC handshake. Some + implementations declare TLS handshake completion as server when + they write off Server Finished and before deriving application rx + secret. */ #define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01 /* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is negotiated. This is only used for client. */ @@ -201,6 +217,9 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, installed. conn->early.ckm cannot be used for this purpose because it might be discarded when a certain condition is met. */ #define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000 +/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local + endpoint has initiated key update. */ +#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000 typedef struct ngtcp2_crypto_data { ngtcp2_buf buf; @@ -325,7 +344,21 @@ typedef enum ngtcp2_ecn_state { NGTCP2_ECN_STATE_CAPABLE, } ngtcp2_ecn_state; +ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(path_challenge, 4, + sizeof(ngtcp2_path_challenge_entry)); + +ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent); + struct ngtcp2_conn { + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + ngtcp2_objalloc strm_objalloc; ngtcp2_conn_state state; ngtcp2_callbacks callbacks; /* rcid is a connection ID present in Initial or 0-RTT packet from @@ -350,21 +383,25 @@ struct ngtcp2_conn { ngtcp2_dcid current; /* bound is a set of destination connection IDs which are bound to particular paths. These paths are not validated yet. */ - ngtcp2_ringbuf bound; + ngtcp2_static_ringbuf_dcid_bound bound; /* unused is a set of unused CID received from peer. */ - ngtcp2_ringbuf unused; + ngtcp2_static_ringbuf_dcid_unused unused; /* retired is a set of CID retired by local endpoint. Keep them in 3*PTO to catch packets in flight along the old path. */ - ngtcp2_ringbuf retired; + ngtcp2_static_ringbuf_dcid_retired retired; /* seqgap tracks received sequence numbers in order to ignore retransmitted duplicated NEW_CONNECTION_ID frame. */ ngtcp2_gaptr seqgap; /* retire_prior_to is the largest retire_prior_to received so far. */ uint64_t retire_prior_to; - /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames - queued for transmission. */ - size_t num_retire_queued; + struct { + /* seqs contains sequence number of Connection ID whose + retirement is not acknowledged by the remote endpoint yet. */ + uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2]; + /* len is the number of sequence numbers that seq contains. */ + size_t len; + } retire_unacked; /* zerolen_seq is a pseudo sequence number of zero-length Destination Connection ID in order to distinguish between them. */ @@ -389,6 +426,9 @@ struct ngtcp2_conn { struct { /* strmq contains ngtcp2_strm which has frames to send. */ ngtcp2_pq strmq; + /* strmq_nretrans is the number of entries in strmq which has + stream data to resent. */ + size_t strmq_nretrans; /* ack is ACK frame. The underlying buffer is reused. */ ngtcp2_frame *ack; /* max_ack_blks is the number of additional ngtcp2_ack_blk which @@ -440,7 +480,7 @@ struct ngtcp2_conn { /* window is the connection-level flow control window size. */ uint64_t window; /* path_challenge stores received PATH_CHALLENGE data. */ - ngtcp2_ringbuf path_challenge; + ngtcp2_static_ringbuf_path_challenge path_challenge; /* ccerr is the received connection close error. */ ngtcp2_connection_close_error ccerr; } rx; @@ -573,7 +613,9 @@ struct ngtcp2_conn { ngtcp2_frame_chain **pfrc; int pkt_empty; int hd_logged; - uint8_t rtb_entry_flags; + /* flags is bitwise OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint16_t rtb_entry_flags; ngtcp2_ssize hs_spktlen; int require_padding; } pkt; @@ -589,9 +631,45 @@ struct ngtcp2_conn { ngtcp2_duration timeout; } keep_alive; + struct { + /* Initial keys for negotiated version. If original version == + negotiated version, these fields are not used. */ + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + } rx; + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + } tx; + /* version is QUIC version that the above Initial keys are created + for. */ + uint32_t version; + /* preferred_versions is the array of versions that are preferred + by the local endpoint. Server negotiates one of those versions + in this array if a client initially selects a less preferred + version. Client uses this field and original_version field to + prevent version downgrade attack if it reacted upon Version + Negotiation packet. */ + uint32_t *preferred_versions; + /* preferred_versionslen is the number of versions stored in the + array pointed by preferred_versions. This field is only used + by server. */ + size_t preferred_versionslen; + /* other_versions is the versions that the local endpoint sends in + version_information transport parameter. This is the wire + image of other_versions field of version_information transport + parameter. */ + uint8_t *other_versions; + /* other_versionslen is the length of data pointed by + other_versions field. */ + size_t other_versionslen; + } vneg; + ngtcp2_map strms; ngtcp2_conn_stat cstat; ngtcp2_pv *pv; + ngtcp2_pmtud *pmtud; ngtcp2_log log; ngtcp2_qlog qlog; ngtcp2_rst rst; @@ -601,9 +679,10 @@ struct ngtcp2_conn { /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; void *user_data; - uint32_t version; + uint32_t client_chosen_version; + uint32_t negotiated_version; /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */ - uint16_t flags; + uint32_t flags; int server; }; @@ -724,9 +803,15 @@ 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. */ -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay, ngtcp2_tstamp ts); +int 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); @@ -779,11 +864,12 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); /* - * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr| - * frame only in the buffer pointed by |dest| whose length if + * ngtcp2_conn_write_single_frame_pkt writes a packet which contains + * |fr| frame only in the buffer pointed by |dest| whose length if * |destlen|. |type| is a long packet type to send. If |type| is 0, * Short packet is used. |dcid| is used as a destination connection - * ID. + * ID. |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*. Only + * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized. * * The packet written by this function will not be retransmitted. * @@ -795,8 +881,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, */ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, - const ngtcp2_path *path, ngtcp2_tstamp ts); + uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts); /* * ngtcp2_conn_commit_local_transport_params commits the local @@ -872,6 +958,133 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, */ ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_idle_expiry` returns the time when a connection + * should be closed if it continues to be idle. If idle timeout is + * disabled, this function returns ``UINT64_MAX``. + */ +ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); + ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns); +/* + * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq| + * of unacknowledged retiring Destination Connection ID. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. + */ +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number + * |seq| of unacknowledged retiring Destination Connection ID. It is + * fine if such sequence number is not found. + */ +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_server_negotiate_version negotiates QUIC version. It + * is compatible version negotiation. It returns the negotiated QUIC + * version. This function must not be called by client. + */ +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, + const ngtcp2_version_info *version_info); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_application_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If handshake has not been confirmed yet, CONNECTION_CLOSE (type + * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written + * instead. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t app_error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts); + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn); + #endif /* NGTCP2_CONN_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c index 9064218dac..dcf72e4d0e 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c @@ -29,6 +29,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_pkt.h" +#include "ngtcp2_net.h" uint64_t ngtcp2_get_uint64(const uint8_t *p) { uint64_t n; @@ -155,13 +156,13 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) { return rv; } -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { +uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n) { uint8_t *rv; - assert(n < 16384); + assert(n < 1073741824); - rv = ngtcp2_put_uint16be(p, n); - *p |= 0x40; + rv = ngtcp2_put_uint32be(p, n); + *p |= 0x80; return rv; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h index dcff0fdb8c..99746fdb4c 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h @@ -29,107 +29,8 @@ # include <config.h> #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_ARPA_INET_H -# include <arpa/inet.h> -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif /* HAVE_NETINET_IN_H */ - -#ifdef HAVE_BYTESWAP_H -# include <byteswap.h> -#endif /* HAVE_BYTESWAP_H */ - -#ifdef HAVE_ENDIAN_H -# include <endian.h> -#endif /* HAVE_ENDIAN_H */ - -#ifdef HAVE_SYS_ENDIAN_H -# include <sys/endian.h> -#endif /* HAVE_SYS_ENDIAN_H */ - #include <ngtcp2/ngtcp2.h> -#if defined(HAVE_BSWAP_64) || \ - (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) -# define ngtcp2_bswap64 bswap_64 -#else /* !HAVE_BSWAP_64 */ -# define ngtcp2_bswap64(N) \ - ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ - ngtcp2_ntohl((uint32_t)((N) >> 32))) -#endif /* !HAVE_BSWAP_64 */ - -#if defined(HAVE_BE64TOH) || \ - (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) -# define ngtcp2_ntohl64(N) be64toh(N) -# define ngtcp2_htonl64(N) htobe64(N) -#else /* !HAVE_BE64TOH */ -# if defined(WORDS_BIGENDIAN) -# define ngtcp2_ntohl64(N) (N) -# define ngtcp2_htonl64(N) (N) -# else /* !WORDS_BIGENDIAN */ -# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) -# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) -# endif /* !WORDS_BIGENDIAN */ -#endif /* !HAVE_BE64TOH */ - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family functions. We - define inline functions for those function so that we don't have - dependeny on that lib. */ - -# ifdef _MSC_VER -# define STIN static __inline -# else -# define STIN static inline -# endif - -STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t ngtcp2_htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#else /* !WIN32 */ - -# define ngtcp2_htonl htonl -# define ngtcp2_htons htons -# define ngtcp2_ntohl ntohl -# define ngtcp2_ntohs ntohs - -#endif /* !WIN32 */ - /* * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned * integer encoded as network byte order, and returns it in host byte @@ -220,12 +121,12 @@ uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n); uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); /* - * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer - * encoding. |n| must be strictly less than 16384. The function - * always encodes |n| in 2 bytes. It returns the one beyond of the + * ngtcp2_put_varint30 writes |n| in |p| using variable-length integer + * encoding. |n| must be strictly less than 1073741824. The function + * always encodes |n| in 4 bytes. It returns the one beyond of the * last written position. */ -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); +uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n); /* * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c index 562328daa8..648b8b429b 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c @@ -30,6 +30,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_conv.h" #include "ngtcp2_conn.h" +#include "ngtcp2_net.h" int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, size_t secretlen, @@ -157,6 +158,7 @@ ngtcp2_ssize ngtcp2_encode_transport_params_versioned( size_t len = 0; /* For some reason, gcc 7.3.0 requires this initialization. */ size_t preferred_addrlen = 0; + size_t version_infolen = 0; (void)transport_params_version; switch (exttype) { @@ -258,6 +260,12 @@ ngtcp2_ssize ngtcp2_encode_transport_params_versioned( len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) + ngtcp2_put_varint_len(0); } + if (params->version_info_present) { + version_infolen = sizeof(uint32_t) + params->version_info.other_versionslen; + len += ngtcp2_put_varint_len( + NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) + + ngtcp2_put_varint_len(version_infolen) + version_infolen; + } if (destlen < len) { return NGTCP2_ERR_NOBUF; @@ -394,6 +402,16 @@ ngtcp2_ssize ngtcp2_encode_transport_params_versioned( p = ngtcp2_put_varint(p, 0); } + if (params->version_info_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT); + p = ngtcp2_put_varint(p, version_infolen); + p = ngtcp2_put_uint32be(p, params->version_info.chosen_version); + if (params->version_info.other_versionslen) { + p = ngtcp2_cpymem(p, params->version_info.other_versions, + params->version_info.other_versionslen); + } + } + assert((size_t)(p - dest) == len); return (ngtcp2_ssize)len; @@ -504,6 +522,7 @@ int ngtcp2_decode_transport_params_versioned( ngtcp2_ssize nread; int initial_scid_present = 0; int original_dcid_present = 0; + size_t i; (void)transport_params_version; if (datalen == 0) { @@ -531,6 +550,7 @@ int ngtcp2_decode_transport_params_versioned( memset(¶ms->retry_scid, 0, sizeof(params->retry_scid)); memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); memset(¶ms->original_dcid, 0, sizeof(params->original_dcid)); + params->version_info_present = 0; p = data; end = data + datalen; @@ -768,6 +788,36 @@ int ngtcp2_decode_transport_params_versioned( p += nread; params->grease_quic_bit = 1; break; + case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT: + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->version_info.chosen_version = ngtcp2_get_uint32(p); + if (params->version_info.chosen_version == 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += sizeof(uint32_t); + if (valuelen > sizeof(uint32_t)) { + params->version_info.other_versions = (uint8_t *)p; + params->version_info.other_versionslen = valuelen - sizeof(uint32_t); + + for (i = sizeof(uint32_t); i < valuelen; + i += sizeof(uint32_t), p += sizeof(uint32_t)) { + if (ngtcp2_get_uint32(p) == 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + } + } + params->version_info_present = 1; + break; default: /* Ignore unknown parameter */ nread = decode_varint(&valuelen, p, end); diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h index c787cf07cd..8735ff8cec 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h @@ -61,8 +61,11 @@ typedef enum ngtcp2_transport_param_id { NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, + /* https://datatracker.ietf.org/doc/html/rfc9221 */ NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2, + /* https://quicwg.org/quic-v2/draft-ietf-quic-v2.html */ + NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT = 0xff73db, } ngtcp2_transport_param_id; /* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c index ca007d6b07..8f676da3ef 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c @@ -104,6 +104,10 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_VERSION_NEGOTIATION"; case NGTCP2_ERR_HANDSHAKE_TIMEOUT: return "ERR_HANDSHAKE_TIMEOUT"; + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return "ERR_VERSION_NEGOTIATION_FAILURE"; + case NGTCP2_ERR_IDLE_CLOSE: + return "ERR_IDLE_CLOSE"; default: return "(unknown)"; } @@ -131,6 +135,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { case NGTCP2_ERR_TRANSPORT_PARAM: return NGTCP2_TRANSPORT_PARAMETER_ERROR; case NGTCP2_ERR_INVALID_ARGUMENT: + case NGTCP2_ERR_NOMEM: + case NGTCP2_ERR_CALLBACK_FAILURE: return NGTCP2_INTERNAL_ERROR; case NGTCP2_ERR_STREAM_STATE: return NGTCP2_STREAM_STATE_ERROR; @@ -140,6 +146,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { return NGTCP2_AEAD_LIMIT_REACHED; case NGTCP2_ERR_NO_VIABLE_PATH: return NGTCP2_NO_VIABLE_PATH; + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT; default: return NGTCP2_PROTOCOL_VIOLATION; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c index 905d247cc9..87c23898e8 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c @@ -23,29 +23,26 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_gaptr.h" -#include "ngtcp2_range.h" #include <string.h> #include <assert.h> -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { - int rv; - ngtcp2_range range = {0, UINT64_MAX}; +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { + ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); - rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - return rv; - } + gaptr->mem = mem; +} + +static int gaptr_gap_init(ngtcp2_gaptr *gaptr) { + ngtcp2_range range = {0, UINT64_MAX}; + int rv; rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); if (rv != 0) { - ngtcp2_ksl_free(&gaptr->gap); return rv; } - gaptr->mem = mem; - return 0; } @@ -62,6 +59,13 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; ngtcp2_ksl_it it; + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + rv = gaptr_gap_init(gaptr); + if (rv != 0) { + return rv; + } + } + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); @@ -95,32 +99,66 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { } uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); - ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + ngtcp2_ksl_it it; + ngtcp2_range r; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = ngtcp2_ksl_begin(&gaptr->gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + return r.begin; } -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset) { +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset) { ngtcp2_range q = {offset, offset + 1}; - return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, - ngtcp2_ksl_range_exclusive_compar); + ngtcp2_ksl_it it; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + ngtcp2_range r = {0, UINT64_MAX}; + return r; + } + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + + assert(!ngtcp2_ksl_it_end(&it)); + + return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); } int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { ngtcp2_range q = {offset, offset + datalen}; - ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar( - &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); - ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - ngtcp2_range m = ngtcp2_range_intersect(&q, &k); + ngtcp2_ksl_it it; + ngtcp2_range k; + ngtcp2_range m; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + m = ngtcp2_range_intersect(&q, &k); + return ngtcp2_range_len(&m) == 0; } void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); + ngtcp2_ksl_it it; ngtcp2_range r; + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return; + } + + it = ngtcp2_ksl_begin(&gaptr->gap); + assert(!ngtcp2_ksl_it_end(&it)); r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h index 3b3190c9c3..0f100a81c4 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h @@ -33,6 +33,7 @@ #include "ngtcp2_mem.h" #include "ngtcp2_ksl.h" +#include "ngtcp2_range.h" /* * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). @@ -47,14 +48,8 @@ typedef struct ngtcp2_gaptr { /* * ngtcp2_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); /* * ngtcp2_gaptr_free frees resources allocated for |gaptr|. @@ -80,11 +75,11 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen); uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr); /* - * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. + * ngtcp2_gaptr_get_first_gap_after returns the first gap which + * overlaps or comes after |offset|. */ -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset); +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset); /* * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset + diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c index f04806b4a8..d988022769 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c @@ -26,17 +26,10 @@ #include <assert.h> -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { - int rv; - - rv = ngtcp2_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { + ngtcp2_gaptr_init(&idtr->gap, mem); idtr->server = server; - - return 0; } void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h index 1be64dc16e..edb8c68c8d 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h @@ -51,14 +51,8 @@ typedef struct ngtcp2_idtr { * * If this object records server initiated ID (even number), set * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); /* * ngtcp2_idtr_free frees resources allocated for |idtr|. diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c index caef7f762a..f4ad04cbc9 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c @@ -33,9 +33,11 @@ #include "ngtcp2_mem.h" #include "ngtcp2_range.h" +static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + static size_t ksl_nodelen(size_t keylen) { - return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; + return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfllu) & + (size_t)~0xfllu; } static size_t ksl_blklen(size_t nodelen) { @@ -51,31 +53,47 @@ static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node, memcpy(node->key, key, ksl->keylen); } -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem) { +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem) { size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - ngtcp2_ksl_blk *head; - ksl->head = ngtcp2_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGTCP2_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; + ngtcp2_objalloc_init(&ksl->blkalloc, + ((ksl_blklen(nodelen) + 0xfllu) & ~0xfllu) * 8, mem); + + ksl->head = NULL; + ksl->front = ksl->back = NULL; ksl->compar = compar; ksl->keylen = keylen; ksl->nodelen = nodelen; ksl->n = 0; - ksl->mem = mem; +} + +static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) { + return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc, + ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(ngtcp2_ksl *ksl) { + ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl); + if (!head) { + return NGTCP2_ERR_NOMEM; + } - head = ksl->head; head->next = head->prev = NULL; head->n = 0; head->leaf = 1; + ksl->head = head; + ksl->front = ksl->back = head; + return 0; } +#ifdef NOMEMPOOL /* * ksl_free_blk frees |blk| recursively. */ @@ -88,15 +106,20 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { } } - ngtcp2_mem_free(ksl->mem, blk); + ksl_blk_objalloc_del(ksl, blk); } +#endif /* NOMEMPOOL */ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { - if (!ksl) { + if (!ksl || !ksl->head) { return; } +#ifdef NOMEMPOOL ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ + + ngtcp2_objalloc_free(&ksl->blkalloc); } /* @@ -110,7 +133,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { ngtcp2_ksl_blk *rblk; - rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + rblk = ksl_blk_objalloc_new(ksl); if (rblk == NULL) { return NULL; } @@ -194,9 +217,9 @@ static int ksl_split_head(ngtcp2_ksl *ksl) { lblk = ksl->head; - nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + nhead = ksl_blk_objalloc_new(ksl); if (nhead == NULL) { - ngtcp2_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); return NGTCP2_ERR_NOMEM; } nhead->next = nhead->prev = NULL; @@ -253,11 +276,20 @@ static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key, void *data) { - ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_blk *blk; ngtcp2_ksl_node *node; size_t i; int rv; + if (!ksl->head) { + rv = ksl_head_init(ksl); + if (rv != 0) { + return rv; + } + } + + blk = ksl->head; + if (blk->n == NGTCP2_KSL_MAX_NBLK) { rv = ksl_split_head(ksl); if (rv != 0) { @@ -369,10 +401,10 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, ksl->back = lblk; } - ngtcp2_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); if (ksl->head == blk && blk->n == 2) { - ngtcp2_mem_free(ksl->mem, ksl->head); + ksl_blk_objalloc_del(ksl, ksl->head); ksl->head = lblk; } else { ksl_remove_node(ksl, blk, i + 1); @@ -469,6 +501,8 @@ int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key) { ngtcp2_ksl_blk *blk = hint->blk; + assert(ksl->head); + if (blk->n <= NGTCP2_KSL_MIN_NBLK) { return ngtcp2_ksl_remove(ksl, it, key); } @@ -494,6 +528,10 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, ngtcp2_ksl_node *node; size_t i; + if (!ksl->head) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (!blk->leaf && blk->n == 2 && ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { @@ -569,6 +607,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, ngtcp2_ksl_it it; size_t i; + if (!blk) { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, ksl->compar); @@ -606,6 +649,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, ngtcp2_ksl_it it; size_t i; + if (!blk) { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, compar); @@ -642,6 +690,8 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, ngtcp2_ksl_node *node; size_t i; + assert(ksl->head); + for (;;) { i = ksl_bsearch(ksl, blk, old_key, ksl->compar); @@ -686,36 +736,49 @@ static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; } void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { - size_t i; - ngtcp2_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk); - } + if (!ksl->head) { + return; } - ksl->front = ksl->back = ksl->head; - ksl->n = 0; +#ifdef NOMEMPOOL + ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ - head = ksl->head; + ksl->front = ksl->back = ksl->head = NULL; + ksl->n = 0; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; + ngtcp2_objalloc_clear(&ksl->blkalloc); } -void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } +void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { + if (!ksl->head) { + return; + } + + ksl_print(ksl, ksl->head, 0); +} ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + + if (ksl->head) { + ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + } else { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + + if (ksl->head) { + ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + } else { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h index f3716a02b3..312a151d4a 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h @@ -33,6 +33,8 @@ #include <ngtcp2/ngtcp2.h> +#include "ngtcp2_objalloc.h" + /* * Skip List using single key instead of range. */ @@ -79,25 +81,33 @@ struct ngtcp2_ksl_node { * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects. */ struct ngtcp2_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - ngtcp2_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - ngtcp2_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - uint32_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - uint32_t leaf; union { - uint64_t align; - /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK - ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until ngtcp2_ksl_init is - called. */ - uint8_t nodes[1]; + struct { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + ngtcp2_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK + ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until ngtcp2_ksl_init is + called. */ + uint8_t nodes[1]; + }; + }; + + ngtcp2_opl_entry oplent; }; }; +ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent); + /* * ngtcp2_ksl_compar is a function type which returns nonzero if key * |lhs| should be placed before |rhs|. It returns 0 otherwise. @@ -122,6 +132,7 @@ struct ngtcp2_ksl_it { * ngtcp2_ksl is a deterministic paged skip list. */ struct ngtcp2_ksl { + ngtcp2_objalloc blkalloc; /* head points to the root block. */ ngtcp2_ksl_blk *head; /* front points to the first leaf block. */ @@ -135,21 +146,14 @@ struct ngtcp2_ksl { /* nodelen is the actual size of ngtcp2_ksl_node including key storage. */ size_t nodelen; - const ngtcp2_mem *mem; }; /* * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem); +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem); /* * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c index 269835a443..ee37ff3517 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c @@ -34,6 +34,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_vec.h" #include "ngtcp2_macro.h" +#include "ngtcp2_conv.h" void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, ngtcp2_printf log_printf, ngtcp2_tstamp ts, @@ -69,7 +70,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, * * # Frame event * - * <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>) + * <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>) * * <DIR>: * Flow direction. tx=transmission, rx=reception @@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, * Packet number. * * <PKTNAME>: - * Packet name. (e.g., Initial, Handshake, S01) - * - * <PKTTYPE>: - * Packet type in hex string. + * Packet name. (e.g., Initial, Handshake, 1RTT) * * <FRAMENAME>: * Frame name. (e.g., STREAM, ACK, PING) @@ -94,16 +92,16 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, /* TODO Split second and remaining fraction with comma */ #define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" -#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)" +#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s" #define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters" #define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type + (DIR), hd->pkt_num, strpkttype(hd) #define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type + (DIR), hd->pkt_num, strpkttype(hd) #define NGTCP2_LOG_TP_HD_FIELDS \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" @@ -140,6 +138,8 @@ static const char *strerrorcode(uint64_t error_code) { return "CRYPTO_BUFFER_EXCEEDED"; case NGTCP2_KEY_UPDATE_ERROR: return "KEY_UPDATE_ERROR"; + case NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT: + return "VERSION_NEGOTIATION_ERROR"; default: if (0x100u <= error_code && error_code <= 0x1ffu) { return "CRYPTO_ERROR"; @@ -155,10 +155,6 @@ static const char *strapperrorcode(uint64_t app_error_code) { static const char *strpkttype_long(uint8_t type) { switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - return "VN"; - case NGTCP2_PKT_STATELESS_RESET: - return "SR"; case NGTCP2_PKT_INITIAL: return "Initial"; case NGTCP2_PKT_RETRY: @@ -176,7 +172,26 @@ static const char *strpkttype(const ngtcp2_pkt_hd *hd) { if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { return strpkttype_long(hd->type); } - return "Short"; + + switch (hd->type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return "VN"; + case NGTCP2_PKT_STATELESS_RESET: + return "SR"; + case NGTCP2_PKT_1RTT: + return "1RTT"; + default: + return "(unknown)"; + } +} + +static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) { + ngtcp2_pkt_hd hd = {0}; + + hd.type = type; + hd.flags = flags; + + return strpkttype(&hd); } static const char *strevent(ngtcp2_log_event ev) { @@ -398,17 +413,11 @@ static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_crypto *fr, const char *dir) { - size_t datalen = 0; - size_t i; - - for (i = 0; i < fr->datacnt; ++i) { - datalen += fr->data[i].len; - } - log->log_printf( log->user_data, (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen); + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, + ngtcp2_vec_len(fr->data, fr->datacnt)); } static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -573,7 +582,6 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { memset(&shd, 0, sizeof(shd)); - shd.flags = NGTCP2_PKT_FLAG_LONG_FORM; shd.type = NGTCP2_PKT_STATELESS_RESET; log->log_printf( @@ -589,6 +597,7 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; uint8_t addr[16 * 2 + 7 + 1]; uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; + size_t i; if (!log->log_printf) { return; @@ -702,6 +711,24 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size); log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"), NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit); + + if (params->version_info_present) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"), + NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version); + + assert(!(params->version_info.other_versionslen & 0x3)); + + for (i = 0; i < params->version_info.other_versionslen; + i += sizeof(uint32_t)) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " version_information.other_versions[%zu]=0x%08x"), + NGTCP2_LOG_TP_HD_FIELDS, i >> 2, + ngtcp2_get_uint32(¶ms->version_info.other_versions[i])); + } + } } void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, @@ -710,11 +737,9 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, return; } - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num, - (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short", - type, sent_ts); + ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num, + strpkttype_type_flags(type, flags), sent_ts); } static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -726,15 +751,21 @@ static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, return; } - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_PKT, - "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d", - dir, hd->pkt_num, - (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), - (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), - (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) - : "Short", - hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); + if (hd->type == NGTCP2_PKT_1RTT) { + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); + } else { + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, + "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), + hd->version, strpkttype(hd), hd->len); + } } void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { @@ -770,6 +801,6 @@ void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT, - "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num, - strpkttype(hd), hd->type); + "cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num, + strpkttype(hd)); } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c index 9ea0da871e..12bc6e84bd 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -31,20 +31,14 @@ #include "ngtcp2_conv.h" -#define NGTCP2_INITIAL_TABLE_LENBITS 8 +#define NGTCP2_INITIAL_TABLE_LENBITS 4 -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { map->mem = mem; - map->tablelen = 1 << NGTCP2_INITIAL_TABLE_LENBITS; - map->tablelenbits = NGTCP2_INITIAL_TABLE_LENBITS; - map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket)); - if (map->table == NULL) { - return NGTCP2_ERR_NOMEM; - } - + map->tablelen = 0; + map->tablelenbits = 0; + map->table = NULL; map->size = 0; - - return 0; } void ngtcp2_map_free(ngtcp2_map *map) { @@ -77,6 +71,10 @@ int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr), uint32_t i; ngtcp2_map_bucket *bkt; + if (map->size == 0) { + return 0; + } + for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; @@ -222,9 +220,17 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); - if (rv != 0) { - return rv; + if (map->tablelen) { + rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); + if (rv != 0) { + return rv; + } + } else { + rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS, + NGTCP2_INITIAL_TABLE_LENBITS); + if (rv != 0) { + return rv; + } } } @@ -238,11 +244,18 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { } void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) { - uint32_t h = hash(key); - size_t idx = h2idx(h, map->tablelenbits); + uint32_t h; + size_t idx; ngtcp2_map_bucket *bkt; size_t d = 0; + if (map->size == 0) { + return NULL; + } + + h = hash(key); + idx = h2idx(h, map->tablelenbits); + for (;;) { bkt = &map->table[idx]; @@ -261,11 +274,18 @@ void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) { } int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { - uint32_t h = hash(key); - size_t idx = h2idx(h, map->tablelenbits), didx; + uint32_t h; + size_t idx, didx; ngtcp2_map_bucket *bkt; size_t d = 0; + if (map->size == 0) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + h = hash(key); + idx = h2idx(h, map->tablelenbits); + for (;;) { bkt = &map->table[idx]; @@ -305,6 +325,10 @@ int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { } void ngtcp2_map_clear(ngtcp2_map *map) { + if (map->tablelen == 0) { + return; + } + memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h index 96275f1e97..a64344a9a3 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h @@ -54,14 +54,8 @@ typedef struct ngtcp2_map { /* * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory */ -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); /* * Deallocates any resources allocated for |map|. The stored entries diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c index 2c036ad163..bcce0b5cdf 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c @@ -25,26 +25,28 @@ */ #include "ngtcp2_mem.h" -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; +#include <stdio.h> + +static void *default_malloc(size_t size, void *user_data) { + (void)user_data; return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; +static void default_free(void *ptr, void *user_data) { + (void)user_data; free(ptr); } -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { + (void)user_data; return calloc(nmemb, size); } -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_realloc(void *ptr, size_t size, void *user_data) { + (void)user_data; return realloc(ptr, size); } @@ -54,22 +56,58 @@ static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } +#ifndef MEMDEBUG void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); + return mem->malloc(size, mem->user_data); } void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) { - free_func(ptr, mem_user_data); + mem->free(ptr, mem->user_data); } void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); + return mem->calloc(nmemb, size, mem->user_data); } void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); + return mem->realloc(ptr, size, mem->user_data); +} +#else /* MEMDEBUG */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, + const char *func, const char *file, size_t line) { + void *nptr = mem->malloc(size, mem->user_data); + + fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, + file, line); + + return nptr; +} + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + mem->free(ptr, mem->user_data); +} + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, + const char *func, const char *file, size_t line) { + void *nptr = mem->calloc(nmemb, size, mem->user_data); + + fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, + size, func, file, line); + + return nptr; +} + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->realloc(ptr, size, mem->user_data); + + fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, + size, func, file, line); + + return nptr; } +#endif /* MEMDEBUG */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h index cdecf8763a..c99b6c5972 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h @@ -34,10 +34,39 @@ /* Convenient wrapper functions to call allocator function in |mem|. */ +#ifndef MEMDEBUG void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); + void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data); + void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size); + void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size); +#else /* MEMDEBUG */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_malloc(MEM, SIZE) \ + ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, + const char *file, size_t line); + +# define ngtcp2_mem_free(MEM, PTR) \ + ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_calloc(MEM, NMEMB, SIZE) \ + ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \ + __LINE__) + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_realloc(MEM, PTR, SIZE) \ + ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__) +#endif /* MEMDEBUG */ #endif /* NGTCP2_MEM_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h new file mode 100644 index 0000000000..abd8cfdc0c --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h @@ -0,0 +1,136 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_NET_H +#define NGTCP2_NET_H + +/* This header file is explicitly allowed to be shared with + ngtcp2_crypto library. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_BYTESWAP_H +# include <byteswap.h> +#endif /* HAVE_BYTESWAP_H */ + +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include <sys/endian.h> +#endif /* HAVE_SYS_ENDIAN_H */ + +#include <ngtcp2/ngtcp2.h> + +#if defined(HAVE_BSWAP_64) || \ + (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) +# define ngtcp2_bswap64 bswap_64 +#else /* !HAVE_BSWAP_64 */ +# define ngtcp2_bswap64(N) \ + ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ + ngtcp2_ntohl((uint32_t)((N) >> 32))) +#endif /* !HAVE_BSWAP_64 */ + +#if defined(HAVE_BE64TOH) || \ + (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) +# define ngtcp2_ntohl64(N) be64toh(N) +# define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# if defined(WORDS_BIGENDIAN) +# define ngtcp2_ntohl64(N) (N) +# define ngtcp2_htonl64(N) (N) +# else /* !WORDS_BIGENDIAN */ +# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +# endif /* !WORDS_BIGENDIAN */ +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t ngtcp2_htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#else /* !WIN32 */ + +# define ngtcp2_htonl htonl +# define ngtcp2_htons htons +# define ngtcp2_ntohl ntohl +# define ngtcp2_ntohs ntohs + +#endif /* !WIN32 */ + +#endif /* NGTCP2_NET_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c new file mode 100644 index 0000000000..8b06cdd5de --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c @@ -0,0 +1,40 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_objalloc.h" + +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, + const ngtcp2_mem *mem) { + ngtcp2_balloc_init(&objalloc->balloc, blklen, mem); + ngtcp2_opl_init(&objalloc->opl); +} + +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) { + ngtcp2_balloc_free(&objalloc->balloc); +} + +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) { + ngtcp2_opl_clear(&objalloc->opl); + ngtcp2_balloc_clear(&objalloc->balloc); +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h new file mode 100644 index 0000000000..fc11da531a --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h @@ -0,0 +1,140 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_OBJALLOC_H +#define NGTCP2_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_balloc.h" +#include "ngtcp2_opl.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +/* + * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides + * an object pool with the custom allocator to reduce the allocation + * and deallocation overheads for small objects. + */ +typedef struct ngtcp2_objalloc { + ngtcp2_balloc balloc; + ngtcp2_opl opl; +} ngtcp2_objalloc; + +/* + * ngtcp2_objalloc_init initializes |objalloc|. |blklen| is directly + * passed to ngtcp2_balloc_init. + */ +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_objalloc_free releases all allocated resources. + */ +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc); + +/* + * ngtcp2_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc); + +#ifndef NOMEMPOOL +# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void ngtcp2_objalloc_##NAME##_init( \ + ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \ + ngtcp2_objalloc_init(objalloc, \ + ((sizeof(TYPE) + 0xfllu) & ~0xfllu) * nmemb, mem); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_get( \ + ngtcp2_objalloc *objalloc) { \ + ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = \ + ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \ + ngtcp2_objalloc *objalloc, size_t len) { \ + ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static void ngtcp2_objalloc_##NAME##_release( \ + ngtcp2_objalloc *objalloc, TYPE *obj) { \ + ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \ + } +#else /* NOMEMPOOL */ +# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void ngtcp2_objalloc_##NAME##_init( \ + ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \ + ngtcp2_objalloc_init(objalloc, \ + ((sizeof(TYPE) + 0xfllu) & ~0xfllu) * nmemb, mem); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_get( \ + ngtcp2_objalloc *objalloc) { \ + return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \ + ngtcp2_objalloc *objalloc, size_t len) { \ + return ngtcp2_mem_malloc(objalloc->balloc.mem, len); \ + } \ + \ + inline static void ngtcp2_objalloc_##NAME##_release( \ + ngtcp2_objalloc *objalloc, TYPE *obj) { \ + ngtcp2_mem_free(objalloc->balloc.mem, obj); \ + } +#endif /* NOMEMPOOL */ + +#endif /* NGTCP2_OBJALLOC_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c new file mode 100644 index 0000000000..a29361c634 --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c @@ -0,0 +1,46 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_opl.h" + +void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; } + +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) { + ent->next = opl->head; + opl->head = ent; +} + +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) { + ngtcp2_opl_entry *ent = opl->head; + + if (!ent) { + return NULL; + } + + opl->head = ent->next; + + return ent; +} + +void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h new file mode 100644 index 0000000000..714aa36630 --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_OPL_H +#define NGTCP2_OPL_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_opl_entry ngtcp2_opl_entry; + +struct ngtcp2_opl_entry { + ngtcp2_opl_entry *next; +}; + +/* + * ngtcp2_opl is an object memory pool. + */ +typedef struct ngtcp2_opl { + ngtcp2_opl_entry *head; +} ngtcp2_opl; + +/* + * ngtcp2_opl_init initializes |opl|. + */ +void ngtcp2_opl_init(ngtcp2_opl *opl); + +/* + * ngtcp2_opl_push inserts |ent| to |opl| head. + */ +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent); + +/* + * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and + * returns it. If |opl| does not have any entry, it returns NULL. + */ +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl); + +void ngtcp2_opl_clear(ngtcp2_opl *opl); + +#endif /* NGTCP2_OPL_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c index 7a51e4da5c..8323873003 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c @@ -46,14 +46,14 @@ int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { } void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, - const struct sockaddr *remote_addr, - size_t remote_addrlen, void *user_data) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, + const ngtcp2_sockaddr *local_addr, + ngtcp2_socklen local_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, void *user_data) { + ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, 0); ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0); + (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); @@ -69,9 +69,9 @@ void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, } void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, + ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, 0); ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0); + (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); ps->path.user_data = NULL; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c index 2fb4e34ee0..2ec782b239 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -26,7 +26,6 @@ #include <assert.h> #include <string.h> -#include <stdio.h> #include "ngtcp2_conv.h" #include "ngtcp2_str.h" @@ -68,6 +67,7 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, size_t len; uint32_t version; size_t dcidlen, scidlen; + int supported_version; assert(datalen); @@ -95,22 +95,30 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, version = ngtcp2_get_uint32(&data[1]); - if ((version == 0 || version == NGTCP2_PROTO_VER_V1 || - (NGTCP2_PROTO_VER_DRAFT_MIN <= version && - version <= NGTCP2_PROTO_VER_DRAFT_MAX)) && + supported_version = ngtcp2_is_supported_version(version); + + if (supported_version && (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { return NGTCP2_ERR_INVALID_ARGUMENT; } + if (version && !supported_version && + datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + *pversion = version; *pdcid = &data[6]; *pdcidlen = dcidlen; *pscid = &data[6 + dcidlen + 1]; *pscidlen = scidlen; - if (version && version != NGTCP2_PROTO_VER_V1 && - (version < NGTCP2_PROTO_VER_DRAFT_MIN || - NGTCP2_PROTO_VER_DRAFT_MAX < version)) { + if (!version) { + /* VN */ + return 0; + } + + if (!supported_version) { return NGTCP2_ERR_VERSION_NEGOTIATION; } return 0; @@ -184,6 +192,8 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, if (version == 0) { type = NGTCP2_PKT_VERSION_NEGOTIATION; + /* Version Negotiation is not a long header packet. */ + flags = NGTCP2_PKT_FLAG_NONE; /* This must be Version Negotiation packet which lacks packet number and payload length fields. */ len = 5 + 2; @@ -192,8 +202,10 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; } - type = ngtcp2_pkt_get_type_long(pkt[0]); + type = ngtcp2_pkt_get_type_long(version, pkt[0]); switch (type) { + case 0: + return NGTCP2_ERR_INVALID_ARGUMENT; case NGTCP2_PKT_INITIAL: len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ @@ -268,10 +280,15 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: case NGTCP2_PKT_RETRY: break; default: + if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { + assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); + /* Version Negotiation is not a long header packet. */ + break; + } + /* Length */ n = ngtcp2_get_varint_len(p); len += n - 1; @@ -298,11 +315,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, p += ntokenlen + tokenlen; switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: case NGTCP2_PKT_RETRY: dest->len = 0; break; default: + if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { + assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); + /* Version Negotiation is not a long header packet. */ + dest->len = 0; + break; + } + vi = ngtcp2_get_varint(&n, p); if (vi > SIZE_MAX) { return NGTCP2_ERR_INVALID_ARGUMENT; @@ -338,7 +361,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, p = &pkt[1]; - dest->type = NGTCP2_PKT_SHORT; + dest->type = NGTCP2_PKT_1RTT; ngtcp2_cid_init(&dest->dcid, p, dcidlen); p += dcidlen; @@ -366,7 +389,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, len and 1 byte for packet number. */ if (hd->type != NGTCP2_PKT_RETRY) { - len += 2 /* Length */ + hd->pkt_numlen; + len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen; } if (hd->type == NGTCP2_PKT_INITIAL) { @@ -379,7 +402,8 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, p = out; - *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT | (hd->type << 4) | + *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT | + (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) | (uint8_t)(hd->pkt_numlen - 1)); if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { *p |= NGTCP2_FIXED_BIT_MASK; @@ -405,7 +429,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, } if (hd->type != NGTCP2_PKT_RETRY) { - p = ngtcp2_put_varint14(p, (uint16_t)hd->len); + p = ngtcp2_put_varint30(p, (uint32_t)hd->len); p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); } @@ -2259,10 +2283,16 @@ ngtcp2_ssize ngtcp2_pkt_write_retry( return pseudo_retrylen; } - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -2345,10 +2375,16 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, pseudo_retrylen = (size_t)(p - pseudo_retry); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -2435,8 +2471,98 @@ size_t ngtcp2_pkt_datagram_framelen(size_t len) { return 1 /* type */ + ngtcp2_put_varint_len(len) + len; } -uint8_t ngtcp2_pkt_get_type_long(uint8_t c) { - return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); +int ngtcp2_is_supported_version(uint32_t version) { + switch (version) { + case NGTCP2_PROTO_VER_V1: + case NGTCP2_PROTO_VER_V2_DRAFT: + return 1; + default: + return NGTCP2_PROTO_VER_DRAFT_MIN <= version && + version <= NGTCP2_PROTO_VER_DRAFT_MAX; + } +} + +int ngtcp2_is_reserved_version(uint32_t version) { + return (version & NGTCP2_RESERVED_VERSION_MASK) == + NGTCP2_RESERVED_VERSION_MASK; +} + +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) { + uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); + + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + switch (pkt_type) { + case NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT: + return NGTCP2_PKT_INITIAL; + case NGTCP2_PKT_TYPE_0RTT_V2_DRAFT: + return NGTCP2_PKT_0RTT; + case NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT: + return NGTCP2_PKT_HANDSHAKE; + case NGTCP2_PKT_TYPE_RETRY_V2_DRAFT: + return NGTCP2_PKT_RETRY; + default: + return 0; + } + default: + if (!ngtcp2_is_supported_version(version)) { + return 0; + } + + /* QUIC v1 and draft versions share the same numeric packet + types. */ + switch (pkt_type) { + case NGTCP2_PKT_TYPE_INITIAL_V1: + return NGTCP2_PKT_INITIAL; + case NGTCP2_PKT_TYPE_0RTT_V1: + return NGTCP2_PKT_0RTT; + case NGTCP2_PKT_TYPE_HANDSHAKE_V1: + return NGTCP2_PKT_HANDSHAKE; + case NGTCP2_PKT_TYPE_RETRY_V1: + return NGTCP2_PKT_RETRY; + default: + return 0; + } + } +} + +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) { + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + return NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT; + case NGTCP2_PKT_0RTT: + return NGTCP2_PKT_TYPE_0RTT_V2_DRAFT; + case NGTCP2_PKT_HANDSHAKE: + return NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT; + case NGTCP2_PKT_RETRY: + return NGTCP2_PKT_TYPE_RETRY_V2_DRAFT; + default: + assert(0); + abort(); + } + default: + /* Assume that unsupported versions share the numeric long packet + types with QUIC v1 in order to send a packet to elicit Version + Negotiation packet. */ + + /* QUIC v1 and draft versions share the same numeric packet + types. */ + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + return NGTCP2_PKT_TYPE_INITIAL_V1; + case NGTCP2_PKT_0RTT: + return NGTCP2_PKT_TYPE_0RTT_V1; + case NGTCP2_PKT_HANDSHAKE: + return NGTCP2_PKT_TYPE_HANDSHAKE_V1; + case NGTCP2_PKT_RETRY: + return NGTCP2_PKT_TYPE_RETRY_V1; + default: + assert(0); + abort(); + } + } } int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h index 021f334591..2f7838a08a 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -103,6 +103,40 @@ /* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ #define NGTCP2_RETRY_TAGLEN 16 +/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size + that this library can write. */ +#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1) + +/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by + Length field in Long packet header. */ +#define NGTCP2_PKT_LENGTHLEN 4 + +/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for + QUIC v1. */ +#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0 +/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC + v1. */ +#define NGTCP2_PKT_TYPE_0RTT_V1 0x1 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type + for QUIC v1. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2 +/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC + v1. */ +#define NGTCP2_PKT_TYPE_RETRY_V1 0x3 + +/* NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT is Initial long header packet type + for QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT 0x1 +/* NGTCP2_PKT_TYPE_0RTT_V2_DRAFT is 0RTT long header packet type for + QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_0RTT_V2_DRAFT 0x2 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT is Handshake long header packet + type for QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT 0x3 +/* NGTCP2_PKT_TYPE_RETRY_V2_DRAFT is Retry long header packet type for + QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_RETRY_V2_DRAFT 0x0 + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; ngtcp2_vec token; @@ -1186,12 +1220,21 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx); +/* + * ngtcp2_pkt_versioned_type returns versioned packet type for a + * version |version| that corresponds to the version-independent + * |pkt_type|. + */ +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type); + /** * @function * - * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is - * the first byte of Long packet header. + * `ngtcp2_pkt_get_type_long` returns the version-independent long + * packet type. |version| is the QUIC version. |c| is the first byte + * of Long packet header. If |version| is not supported by the + * library, it returns 0. */ -uint8_t ngtcp2_pkt_get_type_long(uint8_t c); +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c); #endif /* NGTCP2_PKT_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c new file mode 100644 index 0000000000..26318bb1c8 --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c @@ -0,0 +1,160 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_pmtud.h" + +#include <assert.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_macro.h" + +/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent + for each probe. */ +#define NGTCP2_PMTUD_PROBE_NUM_MAX 3 + +static size_t mtu_probes[] = { + 1454 - 48, /* The well known MTU used by a domestic optic fiber + service in Japan. */ + 1390 - 48, /* Typical Tunneled MTU */ + 1280 - 48, /* IPv6 minimum MTU */ + 1492 - 48, /* PPPoE */ +}; + +static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]); + +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, + size_t hard_max_udp_payload_size, int64_t tx_pkt_num, + const ngtcp2_mem *mem) { + ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud)); + + if (pmtud == NULL) { + return NGTCP2_ERR_NOMEM; + } + + pmtud->mem = mem; + pmtud->mtu_idx = 0; + pmtud->num_pkts_sent = 0; + pmtud->expiry = UINT64_MAX; + pmtud->tx_pkt_num = tx_pkt_num; + pmtud->max_udp_payload_size = max_udp_payload_size; + pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size; + pmtud->min_fail_udp_payload_size = SIZE_MAX; + + for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) { + if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { + continue; + } + if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { + break; + } + } + + *ppmtud = pmtud; + + return 0; +} + +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) { + if (!pmtud) { + return; + } + + ngtcp2_mem_free(pmtud->mem, pmtud); +} + +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) { + assert(pmtud->mtu_idx < mtu_probeslen); + + return mtu_probes[pmtud->mtu_idx]; +} + +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_tstamp timeout; + + if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { + timeout = pto; + } else { + timeout = 3 * pto; + } + + pmtud->expiry = ts + timeout; +} + +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) { + return pmtud->expiry == UINT64_MAX; +} + +static void pmtud_next_probe(ngtcp2_pmtud *pmtud) { + assert(pmtud->mtu_idx < mtu_probeslen); + + ++pmtud->mtu_idx; + pmtud->num_pkts_sent = 0; + pmtud->expiry = UINT64_MAX; + + for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) { + if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size || + mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { + continue; + } + + if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) { + break; + } + } +} + +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) { + pmtud->max_udp_payload_size = + ngtcp2_max(pmtud->max_udp_payload_size, payloadlen); + + assert(pmtud->mtu_idx < mtu_probeslen); + + if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { + return; + } + + pmtud_next_probe(pmtud); +} + +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) { + if (ts < pmtud->expiry) { + return; + } + + pmtud->expiry = UINT64_MAX; + + if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { + return; + } + + pmtud->min_fail_udp_payload_size = + ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]); + + pmtud_next_probe(pmtud); +} + +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) { + return pmtud->mtu_idx >= mtu_probeslen; +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h new file mode 100644 index 0000000000..6b2e691cfc --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h @@ -0,0 +1,123 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_PMTUD_H +#define NGTCP2_PMTUD_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_pmtud { + const ngtcp2_mem *mem; + /* mtu_idx is the index of UDP payload size candidates to try + out. */ + size_t mtu_idx; + /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload + sent */ + size_t num_pkts_sent; + /* expiry is the expired, if it is reached, send out the next UDP + datagram. UINT64_MAX means no expiry, or expiration is canceled. + In either case, new probe packet should be sent unless we have + done all attempts. */ + ngtcp2_tstamp expiry; + /* tx_pkt_num is the smallest outgoing packet number where the + current discovery is performed. In other words, acknowledging + packet whose packet number lower than that does not indicate the + success of Path MTU Discovery. */ + int64_t tx_pkt_num; + /* max_udp_payload_size is the maximum UDP payload size which is + known to work. */ + size_t max_udp_payload_size; + /* hard_max_udp_payload_size is the maximum UDP payload size that is + going to be probed. */ + size_t hard_max_udp_payload_size; + /* min_fail_udp_payload_size is the minimum UDP payload size that is + known to fail. */ + size_t min_fail_udp_payload_size; +} ngtcp2_pmtud; + +/* + * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its + * pointer to |*ppmtud|. |max_udp_payload_size| is the maximum UDP + * payload size that is known to work for the current path. + * |tx_pkt_num| should be the next packet number to send, which is + * used to differentiate the PMTUD probe packet sent by the previous + * PMTUD. PMTUD might finish immediately if |max_udp_payload_size| is + * larger than or equal to all UDP payload probe candidates. + * Therefore, call ngtcp2_pmtud_finished to check this situation. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, + size_t hard_max_udp_payload_size, int64_t tx_pkt_num, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pmtud_del deletes |pmtud|. + */ +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probelen returns the length of UDP payload size for a + * PMTUD probe packet. + */ +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is + * sent. + */ +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet + * should be sent. + */ +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe + * UDP datagram sized |payloadlen| is acknowledged. + */ +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen); + +/* + * ngtcp2_pmtud_handle_expiry handles expiry. + */ +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished. + */ +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud); + +#endif /* NGTCP2_PMTUD_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c index 47f4f10a29..5376246bd4 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c @@ -57,7 +57,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { if (hd->type == NGTCP2_PKT_INITIAL) { ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; } - ppe->pkt_num_offset = ppe->len_offset + 2; + ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN; rv = ngtcp2_pkt_encode_hd_long( buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); } else { @@ -115,7 +115,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { assert(cc->hp_mask); if (ppe->len_offset) { - ngtcp2_put_varint14( + ngtcp2_put_varint30( buf->begin + ppe->len_offset, (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead)); } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c index d5f7759d6c..314e005293 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c @@ -42,19 +42,12 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, const ngtcp2_mem *mem) { - int rv; - (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); if (*ppv == NULL) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES, - sizeof(ngtcp2_pv_entry), mem); - if (rv != 0) { - ngtcp2_mem_free(mem, *ppv); - return 0; - } + ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents); ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); @@ -74,7 +67,6 @@ void ngtcp2_pv_del(ngtcp2_pv *pv) { if (pv == NULL) { return; } - ngtcp2_ringbuf_free(&pv->ents); ngtcp2_mem_free(pv->mem, pv); } @@ -85,11 +77,11 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, assert(pv->probe_pkt_left); - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { pv->started_ts = ts; } - ent = ngtcp2_ringbuf_push_back(&pv->ents); + ent = ngtcp2_ringbuf_push_back(&pv->ents.rb); ngtcp2_pv_entry_init(ent, data, expiry, flags); pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER; @@ -97,7 +89,7 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, } int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { - size_t len = ngtcp2_ringbuf_len(&pv->ents); + size_t len = ngtcp2_ringbuf_len(&pv->ents.rb); size_t i; ngtcp2_pv_entry *ent; @@ -106,7 +98,7 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { } for (i = 0; i < len; ++i) { - ent = ngtcp2_ringbuf_get(&pv->ents, i); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, i); if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { *pflags = ent->flags; ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated"); @@ -120,11 +112,11 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) { ngtcp2_pv_entry *ent; - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { return; } - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); if (ent->expiry > ts) { return; @@ -146,9 +138,9 @@ int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { return 0; } - assert(ngtcp2_ringbuf_len(&pv->ents)); + assert(ngtcp2_ringbuf_len(&pv->ents.rb)); - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); t = pv->started_ts + pv->timeout; t = ngtcp2_max(t, ent->expiry); @@ -160,11 +152,11 @@ ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { ngtcp2_pv_entry *ent; if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) || - ngtcp2_ringbuf_len(&pv->ents) == 0) { + ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { return UINT64_MAX; } - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); return ent->expiry; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h index effbc4daee..4c31562847 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h @@ -86,6 +86,8 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, typedef struct ngtcp2_pv ngtcp2_pv; +ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES, + sizeof(ngtcp2_pv_entry)); /* * ngtcp2_pv is the context of a single path validation. */ @@ -98,7 +100,7 @@ struct ngtcp2_pv { fallback if this path validation fails. */ ngtcp2_dcid fallback_dcid; /* ents is the ring buffer of ngtcp2_pv_entry */ - ngtcp2_ringbuf ents; + ngtcp2_static_ringbuf_pv_ents ents; /* timeout is the duration within which this path validation should succeed. */ ngtcp2_duration timeout; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c index 5bfe62d60c..69eaeb7367 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c @@ -28,6 +28,8 @@ #include "ngtcp2_str.h" #include "ngtcp2_vec.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_net.h" void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, ngtcp2_tstamp ts, void *user_data) { @@ -238,6 +240,10 @@ static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake"); static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT"); static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT"); static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry"); +static ngtcp2_vec vec_pkt_type_version_negotiation = + ngtcp2_make_vec_lit("version_negotiation"); +static ngtcp2_vec vec_pkt_type_stateless_reset = + ngtcp2_make_vec_lit("stateless_reset"); static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown"); static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { @@ -256,19 +262,33 @@ static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { } } - return &vec_pkt_type_1rtt; + switch (hd->type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return &vec_pkt_type_version_negotiation; + case NGTCP2_PKT_STATELESS_RESET: + return &vec_pkt_type_stateless_reset; + case NGTCP2_PKT_1RTT: + return &vec_pkt_type_1rtt; + default: + return &vec_pkt_type_unknown; + } } static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) { /* - * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"} + * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}} */ -#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75 +#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95 *p++ = '{'; p = write_pair(p, "packet_type", qlog_pkt_type(hd)); *p++ = ','; p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num); + if (hd->type == NGTCP2_PKT_INITIAL && hd->token.len) { + p = write_verbatim(p, ",\"token\":{"); + p = write_pair_hex(p, "data", hd->token.base, hd->token.len); + *p++ = '}'; + } /* TODO Write DCIL and DCID */ /* TODO Write SCIL and SCID */ *p++ = '}'; @@ -313,10 +333,7 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { p = write_verbatim(p, "{\"frame_type\":\"ack\","); p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled); - *p++ = ','; - p = write_string(p, "acked_ranges"); - *p++ = ':'; - *p++ = '['; + p = write_verbatim(p, ",\"acked_ranges\":["); largest_ack = fr->largest_ack; min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; @@ -410,14 +427,15 @@ static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) { static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) { /* - * {"frame_type":"new_token","length":0000000000000000000,"token":""} + * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}} */ -#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66 +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75 p = write_verbatim(p, "{\"frame_type\":\"new_token\","); p = write_pair_number(p, "length", fr->token.len); - *p++ = ','; - p = write_pair_hex(p, "token", fr->token.base, fr->token.len); + p = write_verbatim(p, ",\"token\":{"); + p = write_pair_hex(p, "data", fr->token.base, fr->token.len); + *p++ = '}'; *p++ = '}'; return p; @@ -480,9 +498,7 @@ static uint8_t *write_max_streams_frame(uint8_t *p, */ #define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89 - p = write_verbatim(p, "{\"frame_type\":\"max_streams\","); - p = write_string(p, "stream_type"); - *p++ = ':'; + p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":"); if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { p = write_string(p, "bidirectional"); } else { @@ -541,9 +557,9 @@ static uint8_t *write_streams_blocked_frame(uint8_t *p, static uint8_t * write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { /* - * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} + * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}} */ -#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271 +#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280 p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\","); p = write_pair_number(p, "sequence_number", fr->seq); @@ -553,10 +569,11 @@ write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { p = write_pair_number(p, "connection_id_length", fr->cid.datalen); *p++ = ','; p = write_pair_cid(p, "connection_id", &fr->cid); - *p++ = ','; - p = write_pair_hex(p, "stateless_reset_token", fr->stateless_reset_token, + p = write_verbatim(p, ",\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", fr->stateless_reset_token, sizeof(fr->stateless_reset_token)); *p++ = '}'; + *p++ = '}'; return p; } @@ -611,9 +628,8 @@ write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { */ #define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131 - p = write_verbatim(p, "{\"frame_type\":\"connection_close\","); - p = write_string(p, "error_space"); - *p++ = ':'; + p = write_verbatim(p, + "{\"frame_type\":\"connection_close\",\"error_space\":"); if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { p = write_string(p, "transport"); } else { @@ -698,7 +714,11 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, #define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \ (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD) - assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD); + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->token.len * 2) { + return; + } + assert(ngtcp2_buf_len(&qlog->buf)); /* Eat last ',' */ @@ -727,15 +747,13 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { switch (fr->type) { case NGTCP2_FRAME_PADDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) { return; } p = write_padding_frame(p, &fr->padding); break; case NGTCP2_FRAME_PING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1) { return; } p = write_ping_frame(p, &fr->ping); @@ -747,86 +765,75 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD : 0) + - NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1) { return; } p = write_ack_frame(p, &fr->ack); break; case NGTCP2_FRAME_RESET_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + 1) { return; } p = write_reset_stream_frame(p, &fr->reset_stream); break; case NGTCP2_FRAME_STOP_SENDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + 1) { return; } p = write_stop_sending_frame(p, &fr->stop_sending); break; case NGTCP2_FRAME_CRYPTO: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1) { return; } p = write_crypto_frame(p, &fr->crypto); break; case NGTCP2_FRAME_NEW_TOKEN: if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + - fr->new_token.token.len * 2 + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + fr->new_token.token.len * 2 + 1) { return; } p = write_new_token_frame(p, &fr->new_token); break; case NGTCP2_FRAME_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1) { return; } p = write_stream_frame(p, &fr->stream); break; case NGTCP2_FRAME_MAX_DATA: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1) { return; } p = write_max_data_frame(p, &fr->max_data); break; case NGTCP2_FRAME_MAX_STREAM_DATA: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1) { return; } p = write_max_stream_data_frame(p, &fr->max_stream_data); break; case NGTCP2_FRAME_MAX_STREAMS_BIDI: case NGTCP2_FRAME_MAX_STREAMS_UNI: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + 1) { return; } p = write_max_streams_frame(p, &fr->max_streams); break; case NGTCP2_FRAME_DATA_BLOCKED: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_data_blocked_frame(p, &fr->data_blocked); break; case NGTCP2_FRAME_STREAM_DATA_BLOCKED: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked); @@ -834,40 +841,35 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_streams_blocked_frame(p, &fr->streams_blocked); break; case NGTCP2_FRAME_NEW_CONNECTION_ID: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1) { return; } p = write_new_connection_id_frame(p, &fr->new_connection_id); break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1) { return; } p = write_retire_connection_id_frame(p, &fr->retire_connection_id); break; case NGTCP2_FRAME_PATH_CHALLENGE: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1) { return; } p = write_path_challenge_frame(p, &fr->path_challenge); break; case NGTCP2_FRAME_PATH_RESPONSE: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + 1) { return; } p = write_path_response_frame(p, &fr->path_response); @@ -875,24 +877,21 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { case NGTCP2_FRAME_CONNECTION_CLOSE: case NGTCP2_FRAME_CONNECTION_CLOSE_APP: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1) { return; } p = write_connection_close_frame(p, &fr->connection_close); break; case NGTCP2_FRAME_HANDSHAKE_DONE: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1) { return; } p = write_handshake_done_frame(p, &fr->handshake_done); break; case NGTCP2_FRAME_DATAGRAM: case NGTCP2_FRAME_DATAGRAM_LEN: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) { return; } p = write_datagram_frame(p, &fr->datagram); @@ -960,9 +959,10 @@ void ngtcp2_qlog_parameters_set_transport_params( *p++ = ','; } if (params->stateless_reset_token_present) { - p = write_pair_hex(p, "stateless_reset_token", - params->stateless_reset_token, + p = write_verbatim(p, "\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", params->stateless_reset_token, sizeof(params->stateless_reset_token)); + *p++ = '}'; *p++ = ','; } p = write_pair_bool(p, "disable_active_migration", @@ -1011,10 +1011,11 @@ void ngtcp2_qlog_parameters_set_transport_params( p = write_pair_number(p, "port_v6", paddr->ipv6_port); *p++ = ','; p = write_pair_cid(p, "connection_id", &paddr->cid); - *p++ = ','; - p = write_pair_hex(p, "stateless_reset_token", paddr->stateless_reset_token, + p = write_verbatim(p, ",\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", paddr->stateless_reset_token, sizeof(paddr->stateless_reset_token)); *p++ = '}'; + *p++ = '}'; } *p++ = ','; p = write_pair_number(p, "max_datagram_frame_size", @@ -1093,23 +1094,110 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { (size_t)(p - buf)); } -void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, - const ngtcp2_pkt_hd *hd) { +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry) { + uint8_t rawbuf[1024]; + ngtcp2_buf buf; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + + *buf.last++ = '\x1e'; + *buf.last++ = '{'; + buf.last = qlog_write_time(qlog, buf.last); + buf.last = write_verbatim( + buf.last, + ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + + if (ngtcp2_buf_left(&buf) < + NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->token.len * 2 + + sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - 1 + + retry->token.len * 2) { + return; + } + + buf.last = write_pkt_hd(buf.last, hd); + buf.last = write_verbatim(buf.last, ",\"retry_token\":{"); + buf.last = + write_pair_hex(buf.last, "data", retry->token.base, retry->token.len); + buf.last = write_verbatim(buf.last, "}}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, + ngtcp2_buf_len(&buf)); +} + +void ngtcp2_qlog_stateless_reset_pkt_received( + ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) { uint8_t buf[256]; uint8_t *p = buf; + ngtcp2_pkt_hd hd = {0}; if (!qlog->write) { return; } + hd.type = NGTCP2_PKT_STATELESS_RESET; + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); - p = write_pkt_hd(p, hd); + p = write_pkt_hd(p, &hd); + *p++ = ','; + p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token, + NGTCP2_STATELESS_RESET_TOKENLEN); p = write_verbatim(p, "}}\n"); qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, (size_t)(p - buf)); } + +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, + size_t nsv) { + uint8_t rawbuf[512]; + ngtcp2_buf buf; + size_t i; + uint32_t v; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + + *buf.last++ = '\x1e'; + *buf.last++ = '{'; + buf.last = qlog_write_time(qlog, buf.last); + buf.last = write_verbatim( + buf.last, + ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + buf.last = write_pkt_hd(buf.last, hd); + buf.last = write_verbatim(buf.last, ",\"supported_versions\":["); + + if (nsv) { + if (ngtcp2_buf_left(&buf) < + (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) { + return; + } + + v = ngtcp2_htonl(sv[0]); + buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + + for (i = 1; i < nsv; ++i) { + *buf.last++ = ','; + v = ngtcp2_htonl(sv[i]); + buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + } + } + + buf.last = write_verbatim(buf.last, "]}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, + ngtcp2_buf_len(&buf)); +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h index cb3c2063f7..b9107c0e5c 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h @@ -139,6 +139,23 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); * ngtcp2_qlog_retry_pkt_received writes packet_received event for a * received Retry packet. */ -void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry); + +/* + * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received + * event for a received Stateless Reset packet. + */ +void ngtcp2_qlog_stateless_reset_pkt_received( + ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr); + +/* + * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received + * event for a received Version Negotiation packet. + */ +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, + size_t nsv); #endif /* NGTCP2_QLOG_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c index e4deab1ff7..a6b3f73e73 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c @@ -43,24 +43,30 @@ unsigned int __popcnt(unsigned int x) { int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, const ngtcp2_mem *mem) { + uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size); + if (buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem); + + return 0; +} + +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + uint8_t *buf, const ngtcp2_mem *mem) { #ifdef WIN32 assert(1 == __popcnt((unsigned int)nmemb)); #else assert(1 == __builtin_popcount((unsigned int)nmemb)); #endif - rb->buf = ngtcp2_mem_malloc(mem, nmemb * size); - if (rb->buf == NULL) { - return NGTCP2_ERR_NOMEM; - } - + rb->buf = buf; rb->mem = mem; rb->nmemb = nmemb; rb->size = size; rb->first = 0; rb->len = 0; - - return 0; } void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) { diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h index c6e1421518..16635c9410 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -62,6 +62,13 @@ typedef struct ngtcp2_ringbuf { int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, const ngtcp2_mem *mem); +/* + * ngtcp2_ringbuf_buf_init initializes |rb| with given buffer and + * size. + */ +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + uint8_t *buf, const ngtcp2_mem *mem); + /* * ngtcp2_ringbuf_free frees resources allocated for |rb|. This * function does not free the memory pointed by |rb|. @@ -107,4 +114,19 @@ void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset); /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); +/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper + which uses a statically allocated buffer that is suitable for a + usage that does not change buffer size with ngtcp2_ringbuf_resize. + ngtcp2_ringbuf_free should never be called for rb field. */ +#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE) \ + typedef struct ngtcp2_static_ringbuf_##NAME { \ + ngtcp2_ringbuf rb; \ + uint8_t buf[(NMEMB) * (SIZE)]; \ + } ngtcp2_static_ringbuf_##NAME; \ + \ + static inline void ngtcp2_static_ringbuf_##NAME##_init( \ + ngtcp2_static_ringbuf_##NAME *srb) { \ + ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL); \ + } + #endif /* NGTCP2_RINGBUF_H */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c index 94028d148b..9c3d75dc33 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c @@ -69,11 +69,8 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { int rv; ngtcp2_rob_gap *g; - rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_gapksl_ksl_init; - } + ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); if (rv != 0) { @@ -85,23 +82,18 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { goto fail_gapksl_ksl_insert; } - rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_dataksl_ksl_init; - } + ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); rob->chunk = chunk; rob->mem = mem; return 0; -fail_dataksl_ksl_init: fail_gapksl_ksl_insert: ngtcp2_rob_gap_del(g, mem); fail_rob_gap_new: ngtcp2_ksl_free(&rob->gapksl); -fail_gapksl_ksl_init: return rv; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c index 4c8af11322..76beb46921 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -46,6 +46,18 @@ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) { return 0; } +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, + ngtcp2_objalloc *objalloc) { + *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc); + if (*pfrc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_frame_chain_init(*pfrc); + + return 0; +} + int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem) { *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen); @@ -58,9 +70,10 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, return 0; } -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); if (datacnt > 1) { @@ -71,12 +84,13 @@ int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, } } - return ngtcp2_frame_chain_new(pfrc, mem); + return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { +int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); if (datacnt > 1) { @@ -87,12 +101,13 @@ int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, } } - return ngtcp2_frame_chain_new(pfrc, mem); + return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem) { +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); int rv; uint8_t *p; @@ -101,7 +116,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, if (token->len > avail) { rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem); } else { - rv = ngtcp2_frame_chain_new(pfrc, mem); + rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } if (rv != 0) { return rv; @@ -133,19 +148,71 @@ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, frc); } +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_binder *binder; + + if (frc == NULL) { + return; + } + + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + if (frc->fr.stream.datacnt && + sizeof(ngtcp2_vec) * (frc->fr.stream.datacnt - 1) > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + case NGTCP2_FRAME_CRYPTO: + if (frc->fr.crypto.datacnt && + sizeof(ngtcp2_vec) * (frc->fr.crypto.datacnt - 1) > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + case NGTCP2_FRAME_NEW_TOKEN: + if (frc->fr.new_token.token.len > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + } + + binder = frc->binder; + if (binder && --binder->refcount == 0) { + ngtcp2_mem_free(mem, binder); + } + + frc->binder = NULL; + + ngtcp2_objalloc_frame_chain_release(objalloc, frc); +} + void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { frc->next = NULL; frc->binder = NULL; } -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem) { +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { ngtcp2_frame_chain *next; - for (; frc;) { + for (; frc; frc = next) { next = frc->next; - ngtcp2_frame_chain_del(frc, mem); - frc = next; + + ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem); } } @@ -182,35 +249,46 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, return 0; } -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) { - (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry)); +static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags) { + memset(ent, 0, sizeof(*ent)); + + ent->hd.pkt_num = hd->pkt_num; + ent->hd.type = hd->type; + ent->hd.flags = hd->flags; + ent->frc = frc; + ent->ts = ts; + ent->lost_ts = UINT64_MAX; + ent->pktlen = pktlen; + ent->flags = flags; + ent->next = NULL; +} + +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, + const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags, + ngtcp2_objalloc *objalloc) { + *pent = ngtcp2_objalloc_rtb_entry_get(objalloc); if (*pent == NULL) { return NGTCP2_ERR_NOMEM; } - (*pent)->hd.pkt_num = hd->pkt_num; - (*pent)->hd.type = hd->type; - (*pent)->hd.flags = hd->flags; - (*pent)->frc = frc; - (*pent)->ts = ts; - (*pent)->lost_ts = UINT64_MAX; - (*pent)->pktlen = pktlen; - (*pent)->flags = flags; - (*pent)->next = NULL; + rtb_entry_init(*pent, hd, frc, ts, pktlen, flags); return 0; } -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) { - if (ent == NULL) { - return; - } +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, + ngtcp2_objalloc *objalloc, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem); - ngtcp2_frame_chain_list_del(ent->frc, mem); + ent->frc = NULL; - ngtcp2_mem_free(mem, ent); + ngtcp2_objalloc_rtb_entry_release(objalloc, ent); } static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { @@ -220,7 +298,10 @@ static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, ngtcp2_qlog *qlog, - const ngtcp2_mem *mem) { + ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { + rtb->rtb_entry_objalloc = rtb_entry_objalloc; + rtb->frc_objalloc = frc_objalloc; ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); rtb->crypto = crypto; rtb->rst = rst; @@ -231,6 +312,7 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, rtb->largest_acked_tx_pkt_num = -1; rtb->num_ack_eliciting = 0; rtb->num_retransmittable = 0; + rtb->num_pto_eliciting = 0; rtb->probe_pkt_left = 0; rtb->pktns_id = pktns_id; rtb->cc_pkt_num = 0; @@ -249,7 +331,9 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { it = ngtcp2_ksl_begin(&rtb->ents); for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it), + rtb->rtb_entry_objalloc, rtb->frc_objalloc, + rtb->mem); } ngtcp2_ksl_free(&rtb->ents); @@ -272,6 +356,9 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { ++rtb->num_retransmittable; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + ++rtb->num_pto_eliciting; + } } static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, @@ -293,6 +380,11 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, --rtb->num_retransmittable; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + assert(rtb->num_pto_eliciting); + --rtb->num_pto_eliciting; + } + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { assert(cstat->bytes_in_flight >= ent->pktlen); cstat->bytes_in_flight -= ent->pktlen; @@ -316,6 +408,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_range gap, range; size_t num_reclaimed = 0; int rv; + int streamfrq_empty; assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE); @@ -354,8 +447,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, } } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt, - rtb->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -364,9 +457,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, fr->stream.datacnt); + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -376,6 +470,9 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } ++num_reclaimed; @@ -393,8 +490,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, continue; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt, - rtb->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, fr->crypto.datacnt, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -407,7 +504,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, &nfrc->fr.crypto.offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); return rv; } @@ -415,8 +512,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, continue; case NGTCP2_FRAME_NEW_TOKEN: - rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token, - rtb->mem); + rv = ngtcp2_frame_chain_new_token_objalloc_new( + &nfrc, &fr->new_token.token, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -431,7 +528,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, case NGTCP2_FRAME_DATAGRAM_LEN: continue; default: - rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc); if (rv != 0) { return rv; } @@ -508,42 +605,12 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ts); } - if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)) { - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); - - ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; - - ++rtb->num_lost_pkts; - - ngtcp2_ksl_it_next(it); - - return 0; - } - - if (conn->callbacks.lost_datagram && - (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) { - rv = conn_process_lost_datagram(conn, ent); - if (rv != 0) { - return rv; - } - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { - assert(ent->frc); - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); - - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); - if (reclaimed < 0) { - return (int)reclaimed; - } - } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; ent->lost_ts = ts; @@ -555,15 +622,31 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, return 0; } - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); + if (conn->callbacks.lost_datagram && + (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) { + rv = conn_process_lost_datagram(conn, ent); + if (rv != 0) { + return rv; + } + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { + assert(ent->frc); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); - rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num); - assert(0 == rv); + reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); + if (reclaimed < 0) { + return (int)reclaimed; + } + } + + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ++rtb->num_lost_pkts; + + ngtcp2_ksl_it_next(it); return 0; } @@ -640,6 +723,18 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_strm *crypto = rtb->crypto; ngtcp2_pktns *pktns; + if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud && + conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) { + ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen); + + conn->dcid.current.max_udp_payload_size = + ngtcp2_max(conn->dcid.current.max_udp_payload_size, ent->pktlen); + + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + } + for (frc = ent->frc; frc; frc = frc->next) { if (frc->binder) { frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK; @@ -730,8 +825,8 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, } break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - assert(conn->dcid.num_retire_queued); - --conn->dcid.num_retire_queued; + ngtcp2_conn_untrack_retired_dcid_seq(conn, + frc->fr.retire_connection_id.seq); break; case NGTCP2_FRAME_DATAGRAM: case NGTCP2_FRAME_DATAGRAM_LEN: @@ -840,8 +935,10 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, cc_ack.rtt = UINT64_MAX; if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) && largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED | + NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); conn->crypto.key_update.confirmed_ts = ts; ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); @@ -917,8 +1014,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) { cc_ack.rtt = pkt_ts - largest_pkt_sent_ts; - ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); - if (cc->new_rtt_sample) { + rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); + if (rv == 0 && cc->new_rtt_sample) { cc->new_rtt_sample(cc, cstat, ts); } } @@ -949,7 +1046,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, rtb_on_pkt_acked(rtb, ent, cstat, ts); acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } if (verify_ecn) { @@ -961,7 +1059,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, for (ent = acked_ent; ent; ent = acked_ent) { rtb_on_pkt_acked(rtb, ent, cstat, ts); acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } } @@ -989,7 +1088,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, fail: for (ent = acked_ent; ent; ent = acked_ent) { acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } return rv; @@ -1217,7 +1317,8 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { --rtb->num_lost_pkts; rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } } @@ -1251,7 +1352,8 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, --rtb->num_lost_pkts; rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); if (ngtcp2_ksl_len(&rtb->ents) == 0) { return; @@ -1285,6 +1387,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; + int streamfrq_empty; ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, ent->ts); @@ -1301,6 +1404,14 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 + " is a PMTUD probe packet, no retransmission is necessary", + ent->hd.pkt_num); + return 0; + } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { --rtb->num_lost_pkts; @@ -1339,12 +1450,13 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); break; } + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -1354,6 +1466,9 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } break; case NGTCP2_FRAME_CRYPTO: frc = *pfrc; @@ -1365,7 +1480,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, &frc->fr.crypto.offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); return rv; } break; @@ -1383,7 +1498,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); break; default: pfrc = &(*pfrc)->next; @@ -1413,7 +1528,8 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, assert(0 == rv); rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -1442,7 +1558,8 @@ void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) { rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } } @@ -1487,6 +1604,12 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, assert(rtb->num_retransmittable); --rtb->num_retransmittable; + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING; + assert(rtb->num_pto_eliciting); + --rtb->num_pto_eliciting; + } + if (reclaimed) { --num_pkts; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h index 25cf5211ab..d02a01bb1c 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -34,6 +34,7 @@ #include "ngtcp2_pkt.h" #include "ngtcp2_ksl.h" #include "ngtcp2_pq.h" +#include "ngtcp2_objalloc.h" typedef struct ngtcp2_conn ngtcp2_conn; typedef struct ngtcp2_pktns ngtcp2_pktns; @@ -72,11 +73,19 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; * ngtcp2_frame_chain chains frames in a single packet. */ struct ngtcp2_frame_chain { - ngtcp2_frame_chain *next; - ngtcp2_frame_chain_binder *binder; - ngtcp2_frame fr; + union { + struct { + ngtcp2_frame_chain *next; + ngtcp2_frame_chain_binder *binder; + ngtcp2_frame fr; + }; + + ngtcp2_opl_entry oplent; + }; }; +ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent); + /* * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL @@ -111,6 +120,13 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, */ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_objalloc_new behaves like + * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object. + */ +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, + ngtcp2_objalloc *objalloc); + /* * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new, * but it allocates extra memory |extralen| in order to extend @@ -120,30 +136,33 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem); /* - * ngtcp2_frame_chain_stream_datacnt_new works like + * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like * ngtcp2_frame_chain_new, but it allocates enough data to store * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. + * object. If no additional space is required, + * ngtcp2_frame_chain_objalloc_new is called internally. */ -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* - * ngtcp2_frame_chain_crypto_datacnt_new works like + * ngtcp2_frame_chain_crypto_datacnt_objalloc_new works like * ngtcp2_frame_chain_new, but it allocates enough data to store * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. + * object. If no additional space is required, + * ngtcp2_frame_chain_objalloc_new is called internally. */ -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the @@ -151,17 +170,28 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, */ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse. + * It might just delete |frc| depending on the frame type and the size + * of |frc|. + */ +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); + /* * ngtcp2_frame_chain_init initializes |frc|. */ void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); /* - * ngtcp2_frame_chain_list_del deletes |frc|, and all objects - * connected by next field. + * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain + * linked from |frc| to |objalloc| for reuse. Depending on the frame type + * and its size, ngtcp2_frame_chain might be deleted instead. */ -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem); +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */ #define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00 @@ -171,9 +201,8 @@ void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, /* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry includes a frame which must be retransmitted until it is acknowledged. In most cases, this flag is used along with - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING. We have these 2 flags because - NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE triggers PTO, but just - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING does not. */ + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */ #define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02 /* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry elicits acknowledgement. */ @@ -191,6 +220,12 @@ void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, /* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes DATAGRAM frame. */ #define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40 +/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes + a PMTUD probe packet. */ +#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80 +/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry + includes a packet which elicits PTO probe packets. */ +#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100 typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; @@ -199,60 +234,69 @@ typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; * to the one packet which is waiting for its ACK. */ struct ngtcp2_rtb_entry { - ngtcp2_rtb_entry *next; - - struct { - int64_t pkt_num; - uint8_t type; - uint8_t flags; - } hd; - ngtcp2_frame_chain *frc; - /* ts is the time point when a packet included in this entry is sent - to a peer. */ - ngtcp2_tstamp ts; - /* lost_ts is the time when this entry is marked lost. */ - ngtcp2_tstamp lost_ts; - /* pktlen is the length of QUIC packet */ - size_t pktlen; - struct { - uint64_t delivered; - ngtcp2_tstamp delivered_ts; - ngtcp2_tstamp first_sent_ts; - uint64_t tx_in_flight; - uint64_t lost; - int is_app_limited; - } rst; - /* flags is bitwise-OR of zero or more of - NGTCP2_RTB_ENTRY_FLAG_*. */ - uint8_t flags; + union { + struct { + ngtcp2_rtb_entry *next; + + struct { + int64_t pkt_num; + uint8_t type; + uint8_t flags; + } hd; + ngtcp2_frame_chain *frc; + /* ts is the time point when a packet included in this entry is sent + to a peer. */ + ngtcp2_tstamp ts; + /* lost_ts is the time when this entry is marked lost. */ + ngtcp2_tstamp lost_ts; + /* pktlen is the length of QUIC packet */ + size_t pktlen; + struct { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + uint64_t tx_in_flight; + uint64_t lost; + int is_app_limited; + } rst; + /* flags is bitwise-OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint16_t flags; + }; + + ngtcp2_opl_entry oplent; + }; }; +ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent); + /* * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns - * its pointer to |*pent|. On success, |*pent| takes ownership of - * |frc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. + * its pointer to |*pent|. */ -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem); +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, + const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags, + ngtcp2_objalloc *objalloc); /* - * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory - * pointed by |ent|. + * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse. + * ngtcp2_frame_chain linked from ent->frc are also added to + * |frc_objalloc| depending on their frame type and size. */ -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem); +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, + ngtcp2_objalloc *objalloc, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_rtb tracks sent packets, and its ACK timeout for * retransmission. */ typedef struct ngtcp2_rtb { + ngtcp2_objalloc *frc_objalloc; + ngtcp2_objalloc *rtb_entry_objalloc; /* ents includes ngtcp2_rtb_entry sorted by decreasing order of packet number. */ ngtcp2_ksl ents; @@ -271,6 +315,9 @@ typedef struct ngtcp2_rtb { /* num_retransmittable is the number of packets which contain frames that must be retransmitted on loss. */ size_t num_retransmittable; + /* num_pto_eliciting is the number of packets that elicit PTO probe + packets. */ + size_t num_pto_eliciting; /* probe_pkt_left is the number of probe packet to send */ size_t probe_pkt_left; /* pktns_id is the identifier of packet number space. */ @@ -296,7 +343,9 @@ typedef struct ngtcp2_rtb { */ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, - ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem); + ngtcp2_log *log, ngtcp2_qlog *qlog, + ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem); /* * ngtcp2_rtb_free deallocates resources allocated for |rtb|. diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c index b601fbcc90..6d4a95196a 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -35,9 +35,11 @@ static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *(int64_t *)lhs < *(int64_t *)rhs; } -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem) { +void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + strm->frc_objalloc = frc_objalloc; strm->cycle = 0; strm->tx.acked_offset = NULL; strm->tx.cont_acked_offset = 0; @@ -56,8 +58,6 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, strm->pe.index = NGTCP2_PQ_BAD_INDEX; strm->mem = mem; strm->app_error_code = 0; - - return 0; } void ngtcp2_strm_free(ngtcp2_strm *strm) { @@ -70,17 +70,23 @@ void ngtcp2_strm_free(ngtcp2_strm *strm) { if (strm->tx.streamfrq) { for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem); + ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it), + strm->frc_objalloc, strm->mem); } ngtcp2_ksl_free(strm->tx.streamfrq); ngtcp2_mem_free(strm->mem, strm->tx.streamfrq); } - ngtcp2_rob_free(strm->rx.rob); - ngtcp2_mem_free(strm->mem, strm->rx.rob); - ngtcp2_gaptr_free(strm->tx.acked_offset); - ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); + if (strm->rx.rob) { + ngtcp2_rob_free(strm->rx.rob); + ngtcp2_mem_free(strm->mem, strm->rx.rob); + } + + if (strm->tx.acked_offset) { + ngtcp2_gaptr_free(strm->tx.acked_offset); + ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); + } } static int strm_rob_init(ngtcp2_strm *strm) { @@ -154,17 +160,12 @@ void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { } static int strm_streamfrq_init(ngtcp2_strm *strm) { - int rv; ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq)); if (streamfrq == NULL) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, streamfrq); - return rv; - } + ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); strm->tx.streamfrq = streamfrq; @@ -233,7 +234,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, if (idx == fr->datacnt) { if (fr->fin) { if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0); return 0; } @@ -253,7 +254,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, return 0; } - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); continue; } @@ -292,10 +293,10 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, return 0; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx, - strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -317,8 +318,8 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -382,7 +383,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } *pfrc = NULL; @@ -400,10 +401,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, assert(acnt > 0); assert(bcnt > 0); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, bcnt, strm->frc_objalloc, strm->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -419,15 +421,16 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, acnt, strm->frc_objalloc, strm->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -437,7 +440,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); *pfrc = nfrc; @@ -459,7 +462,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = strm_streamfrq_unacked_pop(strm, &nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } if (nfrc == NULL) { @@ -470,7 +473,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, if (nfr->fin && nfr->datacnt == 0) { fr->fin = 1; - ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); break; } @@ -480,8 +483,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } break; @@ -492,7 +495,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, if (nfr->datacnt == 0) { fr->fin = nfr->fin; - ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); continue; } @@ -500,8 +503,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -519,9 +522,10 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, assert(acnt > fr->datacnt); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, acnt, strm->frc_objalloc, strm->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -530,7 +534,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); *pfrc = nfrc; @@ -596,7 +600,7 @@ void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); } ngtcp2_ksl_clear(strm->tx.streamfrq); } @@ -616,7 +620,6 @@ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) { ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, uint64_t offset) { - ngtcp2_ksl_it gapit; ngtcp2_range gap; if (strm->tx.acked_offset == NULL) { @@ -625,8 +628,7 @@ ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, return gap; } - gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); - return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit); + return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); } uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { @@ -638,7 +640,6 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { } static int strm_acked_offset_init(ngtcp2_strm *strm) { - int rv; ngtcp2_gaptr *acked_offset = ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset)); @@ -646,11 +647,7 @@ static int strm_acked_offset_init(ngtcp2_strm *strm) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_gaptr_init(acked_offset, strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, acked_offset); - return rv; - } + ngtcp2_gaptr_init(acked_offset, strm->mem); strm->tx.acked_offset = acked_offset; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h index 0fb3cc84ea..c9319268e5 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h @@ -75,86 +75,91 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; In this case, without this flag, we are unable to distinguish assigned value from unassigned one. */ #define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100 +/* NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED is set when + stream_stop_sending callback is called. */ +#define NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED 0x200 typedef struct ngtcp2_strm ngtcp2_strm; struct ngtcp2_strm { - ngtcp2_pq_entry pe; - uint64_t cycle; + union { + struct { + ngtcp2_pq_entry pe; + uint64_t cycle; + ngtcp2_objalloc *frc_objalloc; - struct { - /* acked_offset tracks acknowledged outgoing data. */ - ngtcp2_gaptr *acked_offset; - /* cont_acked_offset is the offset that all data up to this offset - is acknowledged by a remote endpoint. It is used until the - remote endpoint acknowledges data in out-of-order. After that, - acked_offset is used instead. */ - uint64_t cont_acked_offset; - /* streamfrq contains STREAM frame for retransmission. The flow - control credits have been paid when they are transmitted first - time. There are no restriction regarding flow control for - retransmission. */ - ngtcp2_ksl *streamfrq; - /* offset is the next offset of outgoing data. In other words, it - is the number of bytes sent in this stream without - duplication. */ - uint64_t offset; - /* max_tx_offset is the maximum offset that local endpoint can - send for this stream. */ - uint64_t max_offset; - /* last_max_stream_data_ts is the timestamp when last - MAX_STREAM_DATA frame is sent. */ - ngtcp2_tstamp last_max_stream_data_ts; - } tx; + struct { + /* acked_offset tracks acknowledged outgoing data. */ + ngtcp2_gaptr *acked_offset; + /* cont_acked_offset is the offset that all data up to this offset + is acknowledged by a remote endpoint. It is used until the + remote endpoint acknowledges data in out-of-order. After that, + acked_offset is used instead. */ + uint64_t cont_acked_offset; + /* streamfrq contains STREAM frame for retransmission. The flow + control credits have been paid when they are transmitted first + time. There are no restriction regarding flow control for + retransmission. */ + ngtcp2_ksl *streamfrq; + /* offset is the next offset of outgoing data. In other words, it + is the number of bytes sent in this stream without + duplication. */ + uint64_t offset; + /* max_tx_offset is the maximum offset that local endpoint can + send for this stream. */ + uint64_t max_offset; + /* last_max_stream_data_ts is the timestamp when last + MAX_STREAM_DATA frame is sent. */ + ngtcp2_tstamp last_max_stream_data_ts; + } tx; - struct { - /* rob is the reorder buffer for incoming stream data. The data - received in out of order is buffered and sorted by its offset - in this object. */ - ngtcp2_rob *rob; - /* cont_offset is the largest offset of consecutive data. It is - used until the endpoint receives out-of-order data. After - that, rob is used to track the offset and data. */ - uint64_t cont_offset; - /* last_offset is the largest offset of stream data received for - this stream. */ - uint64_t last_offset; - /* max_offset is the maximum offset that remote endpoint can send - to this stream. */ - uint64_t max_offset; - /* unsent_max_offset is the maximum offset that remote endpoint - can send to this stream, and it is not notified to the remote - endpoint. unsent_max_offset >= max_offset must be hold. */ - uint64_t unsent_max_offset; - /* window is the stream-level flow control window size. */ - uint64_t window; - } rx; + struct { + /* rob is the reorder buffer for incoming stream data. The data + received in out of order is buffered and sorted by its offset + in this object. */ + ngtcp2_rob *rob; + /* cont_offset is the largest offset of consecutive data. It is + used until the endpoint receives out-of-order data. After + that, rob is used to track the offset and data. */ + uint64_t cont_offset; + /* last_offset is the largest offset of stream data received for + this stream. */ + uint64_t last_offset; + /* max_offset is the maximum offset that remote endpoint can send + to this stream. */ + uint64_t max_offset; + /* unsent_max_offset is the maximum offset that remote endpoint + can send to this stream, and it is not notified to the remote + endpoint. unsent_max_offset >= max_offset must be hold. */ + uint64_t unsent_max_offset; + /* window is the stream-level flow control window size. */ + uint64_t window; + } rx; - const ngtcp2_mem *mem; - int64_t stream_id; - void *stream_user_data; - /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ - uint32_t flags; - /* app_error_code is an error code the local endpoint sent in - RESET_STREAM or STOP_SENDING, or received from a remote endpoint - in RESET_STREAM or STOP_SENDING. First application error code is - chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is - set in flags field. */ - uint64_t app_error_code; + const ngtcp2_mem *mem; + int64_t stream_id; + void *stream_user_data; + /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ + uint32_t flags; + /* app_error_code is an error code the local endpoint sent in + RESET_STREAM or STOP_SENDING, or received from a remote endpoint + in RESET_STREAM or STOP_SENDING. First application error code is + chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is + set in flags field. */ + uint64_t app_error_code; + }; + + ngtcp2_opl_entry oplent; + }; }; /* * ngtcp2_strm_init initializes |strm|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory */ -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem); +void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_strm_free deallocates memory allocated for |strm|. This diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c new file mode 100644 index 0000000000..b31162c3eb --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c @@ -0,0 +1,39 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, + NGTCP2_VERSION}; + +const ngtcp2_info *ngtcp2_version(int least_version) { + if (least_version > NGTCP2_VERSION_NUM) { + return NULL; + } + return &version; +} diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h index 4012fed065..5f5946d7fc 100644 --- a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h +++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h @@ -32,6 +32,11 @@ # define WIN32 #endif +#ifdef WIN32 +# pragma warning(push) +# pragma warning(disable : 4324) +#endif + #include <stdlib.h> #if defined(_MSC_VER) && (_MSC_VER < 1800) /* MSVC < 2013 does not have inttypes.h because it is not C99 @@ -45,10 +50,26 @@ #include <stdarg.h> #include <stddef.h> -#ifdef WIN32 -# include <winsock2.h> +#ifndef NGTCP2_USE_GENERIC_SOCKADDR +# ifdef WIN32 +# include <ws2tcpip.h> +# else +# include <sys/socket.h> +# include <netinet/in.h> +# endif +#endif + +#ifdef AF_INET +# define NGTCP2_AF_INET AF_INET #else -# include <sys/socket.h> +# define NGTCP2_AF_INET 2 +#endif + +#ifdef AF_INET6 +# define NGTCP2_AF_INET6 AF_INET6 +#else +# define NGTCP2_AF_INET6 23 +# define NGTCP2_USE_GENERIC_IPV6_SOCKADDR #endif #include <ngtcp2/version.h> @@ -70,11 +91,9 @@ #endif /* !defined(WIN32) */ #ifdef WIN32 -# define NGTCP2_ALIGN_BEFORE(N) __declspec(align(N)) -# define NGTCP2_ALIGN_AFTER(N) +# define NGTCP2_ALIGN(N) __declspec(align(N)) #else /* !WIN32 */ -# define NGTCP2_ALIGN_BEFORE(N) -# define NGTCP2_ALIGN_AFTER(N) __attribute__((aligned(N))) +# define NGTCP2_ALIGN(N) __attribute__((aligned(N))) #endif /* !WIN32 */ #ifdef __cplusplus @@ -92,61 +111,68 @@ typedef ptrdiff_t ngtcp2_ssize; * @functypedef * * :type:`ngtcp2_malloc` is a custom memory allocator to replace - * :manpage:`malloc(3)`. The |mem_user_data| is the mem_user_data - * member of :type:`ngtcp2_mem` structure. + * :manpage:`malloc(3)`. The |user_data| is + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data); +typedef void *(*ngtcp2_malloc)(size_t size, void *user_data); /** * @functypedef * * :type:`ngtcp2_free` is a custom memory allocator to replace - * :manpage:`free(3)`. The |mem_user_data| is the mem_user_data - * member of :type:`ngtcp2_mem` structure. + * :manpage:`free(3)`. The |user_data| is + * :member:`ngtcp2_mem.user_data`. */ -typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data); +typedef void (*ngtcp2_free)(void *ptr, void *user_data); /** * @functypedef * * :type:`ngtcp2_calloc` is a custom memory allocator to replace - * :manpage:`calloc(3)`. The |mem_user_data| is the mem_user_data - * member of :type:`ngtcp2_mem` structure. + * :manpage:`calloc(3)`. The |user_data| is the + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data); /** * @functypedef * * :type:`ngtcp2_realloc` is a custom memory allocator to replace - * :manpage:`realloc(3)`. The |mem_user_data| is the mem_user_data - * member of :type:`ngtcp2_mem` structure. + * :manpage:`realloc(3)`. The |user_data| is the + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); +typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data); /** * @struct * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-connection memory pool. + * :type:`ngtcp2_mem` is a custom memory allocator. The + * :member:`user_data` field is passed to each allocator function. + * This can be used, for example, to achieve per-connection memory + * pool. * * In the following example code, ``my_malloc``, ``my_free``, * ``my_calloc`` and ``my_realloc`` are the replacement of the * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`, * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: * - * void *my_malloc_cb(size_t size, void *mem_user_data) { + * void *my_malloc_cb(size_t size, void *user_data) { + * (void)user_data; * return my_malloc(size); * } * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * void my_free_cb(void *ptr, void *user_data) { + * (void)user_data; + * my_free(ptr); + * } * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + * (void)user_data; * return my_calloc(nmemb, size); * } * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + * (void)user_data; * return my_realloc(ptr, size); * } * @@ -159,10 +185,10 @@ typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); */ typedef struct ngtcp2_mem { /** - * :member:`mem_user_data` is an arbitrary user supplied data. This + * :member:`user_data` is an arbitrary user supplied data. This * is passed to each allocator function. */ - void *mem_user_data; + void *user_data; /** * :member:`malloc` is a custom allocator function to replace * :manpage:`malloc(3)`. @@ -233,7 +259,17 @@ typedef struct ngtcp2_mem { * * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1. */ -#define NGTCP2_PROTO_VER_V1 0x00000001u +#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u) + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_V2_DRAFT` is the provisional version + * number for QUIC version 2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_PROTO_VER_V2_DRAFT ((uint32_t)0x709a50c4u) /** * @macro @@ -267,6 +303,14 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN +/** + * @macro + * + * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved + * version. + */ +#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au + /** * @macrosection * @@ -354,6 +398,28 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_V2_DRAFT` is an encryption key to create + * integrity tag of Retry packet. It is used for QUIC v2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_RETRY_KEY_V2_DRAFT \ + "\xba\x85\x8d\xc7\xb4\x3d\xe5\xdb\xf8\x76\x17\xff\x4a\xb2\x53\xdb" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_V2_DRAFT` is nonce used when generating + * integrity tag of Retry packet. It is used for QUIC v2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_RETRY_NONCE_V2_DRAFT \ + "\x14\x1b\x99\xc2\x39\xb0\x3e\x78\x5d\x6a\x2e\x9f" + /** * @macro * @@ -456,7 +522,7 @@ typedef struct ngtcp2_mem { * * :type:`ngtcp2_pkt_info` is a packet metadata. */ -typedef NGTCP2_ALIGN_BEFORE(8) struct NGTCP2_ALIGN_AFTER(8) ngtcp2_pkt_info { +typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info { /** * :member:`ecn` is ECN marking and when passing * `ngtcp2_conn_read_pkt()`, and it should be either @@ -721,6 +787,20 @@ typedef NGTCP2_ALIGN_BEFORE(8) struct NGTCP2_ALIGN_AFTER(8) ngtcp2_pkt_info { * connection is not established before the specified deadline. */ #define NGTCP2_ERR_HANDSHAKE_TIMEOUT -246 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the + * version negotiation failed. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -247 +/** + * @macro + * + * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be + * closed silently because of idle timeout. + */ +#define NGTCP2_ERR_IDLE_CLOSE -248 /** * @macro * @@ -782,7 +862,8 @@ typedef NGTCP2_ALIGN_BEFORE(8) struct NGTCP2_ALIGN_AFTER(8) ngtcp2_pkt_info { /** * @enum * - * :type:`ngtcp2_pkt_type` defines QUIC packet types. + * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC + * packet types. */ typedef enum ngtcp2_pkt_type { /** @@ -798,23 +879,23 @@ typedef enum ngtcp2_pkt_type { /** * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet. */ - NGTCP2_PKT_INITIAL = 0x0, + NGTCP2_PKT_INITIAL = 0x10, /** * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet. */ - NGTCP2_PKT_0RTT = 0x1, + NGTCP2_PKT_0RTT = 0x11, /** * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet. */ - NGTCP2_PKT_HANDSHAKE = 0x2, + NGTCP2_PKT_HANDSHAKE = 0x12, /** * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet. */ - NGTCP2_PKT_RETRY = 0x3, + NGTCP2_PKT_RETRY = 0x13, /** - * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience. + * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience. */ - NGTCP2_PKT_SHORT = 0x40 + NGTCP2_PKT_1RTT = 0x40 } ngtcp2_pkt_type; /** @@ -966,6 +1047,16 @@ typedef enum ngtcp2_pkt_type { */ #define NGTCP2_CRYPTO_ERROR 0x100u +/** + * @macro + * + * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT` is QUIC transport + * error code ``VERSION_NEGOTIATION_ERROR``. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT 0x53f8u + /** * @enum * @@ -1100,8 +1191,8 @@ typedef struct ngtcp2_pkt_hd { */ uint8_t type; /** - * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*. See - * :macro:`NGTCP2_PKT_FLAG_NONE`. + * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_* + * <NGTCP2_PKT_FLAG_NONE>`. */ uint8_t flags; } ngtcp2_pkt_hd; @@ -1246,6 +1337,29 @@ typedef struct ngtcp2_preferred_addr { uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; } ngtcp2_preferred_addr; +/** + * @struct + * + * :type:`ngtcp2_version_info` represents version_information + * structure. + */ +typedef struct ngtcp2_version_info { + /** + * :member:`chosen_version` is the version chosen by the sender. + */ + uint32_t chosen_version; + /** + * :member:`other_versions` points the wire image of other_versions + * field. The each version is therefore in network byte order. + */ + uint8_t *other_versions; + /** + * :member:`other_versionslen` is the number of bytes pointed by + * :member:`other_versions`, not the number of versions included. + */ + size_t other_versionslen; +} ngtcp2_version_info; + #define NGTCP2_TRANSPORT_PARAMS_VERSION_V1 1 #define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_VERSION_V1 @@ -1353,7 +1467,7 @@ typedef struct ngtcp2_transport_params { * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM * frame that this endpoint willingly receives. Specifying 0 * disables DATAGRAM support. See - * https://tools.ietf.org/html/draft-ietf-quic-datagram-01 + * https://datatracker.ietf.org/doc/html/rfc9221 */ uint64_t max_datagram_frame_size; /** @@ -1388,6 +1502,18 @@ typedef struct ngtcp2_transport_params { * regardless of this field value. */ uint8_t grease_quic_bit; + /** + * :member:`version_info` contains version_information field if + * :member:`version_info_present` is nonzero. Application should + * not specify this field. + */ + ngtcp2_version_info version_info; + /** + * :member:`version_info_present` is nonzero if + * :member:`version_info` is set. Application should not specify + * this field. + */ + uint8_t version_info_present; } ngtcp2_transport_params; /** @@ -1468,11 +1594,11 @@ typedef struct ngtcp2_conn_stat { ngtcp2_tstamp loss_detection_timer; /** * :member:`last_tx_pkt_ts` corresponds to - * time_of_last_ack_eliciting_packet in RFC 9002. + * time_of_last_ack_eliciting_packet in :rfc:`9002`. */ ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX]; /** - * :member:`loss_time` corresponds to loss_time in RFC 9002. + * :member:`loss_time` corresponds to loss_time in :rfc:`9002`. */ ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX]; /** @@ -1592,8 +1718,8 @@ typedef struct ngtcp2_rand_ctx { * * :type:`ngtcp2_qlog_write` is a callback function which is called to * write qlog |data| of length |datalen| bytes. |flags| is bitwise OR - * of zero or more of NGTCP2_QLOG_WRITE_FLAG_*. See - * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE`. If + * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_* + * <NGTCP2_QLOG_WRITE_FLAG_NONE>`. If * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0. */ typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags, @@ -1702,21 +1828,13 @@ typedef struct ngtcp2_settings { * immediate acknowledgement. */ size_t ack_thresh; - /** - * :member:`assume_symmetric_path`, if set to nonzero, assumes that - * a network path is symmetric and extends the UDP payload size up to - * the incoming UDP payload size. The size is still capped by - * :member:`max_udp_payload_size`. This field is ignored if - * :member:`no_udp_payload_size_shaping` is set to nonzero. - */ - int assume_symmetric_path; /** * :member:`no_udp_payload_size_shaping`, if set to nonzero, * instructs the library not to limit the UDP payload size to - * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended, see - * :member:`assume_symmetric_path`) and instead fully utilize the - * given buffer size or :member:`max_udp_payload_size` which is - * smaller. + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by + * Path MTU Discovery) and instead use the mininum size among the + * given buffer size, :member:`max_udp_payload_size`, and the + * received max_udp_payload QUIC transport parameter. */ int no_udp_payload_size_shaping; /** @@ -1728,8 +1846,155 @@ typedef struct ngtcp2_settings { * field is set to ``UINT64_MAX``, no handshake timeout is set. */ ngtcp2_duration handshake_timeout; + /** + * :member:`preferred_versions` is the array of versions that are + * preferred by the local endpoint. All versions set in this array + * must be supported by the library, and compatible to QUIC v1. The + * reserved versions are not allowed. They are sorted in the order + * of preference. + * + * On compatible version negotiation, server will negotiate one of + * those versions contained in this array if a client initially + * chooses a less preferred version. This version set corresponds + * to Offered Versions in QUIC Version Negotiation draft, and it should + * be sent in Version Negotiation packet. + * + * Client uses this field and :member:`original_version` to prevent + * version downgrade attack if it reacted upon Version Negotiation + * packet. If this field is specified, client must include + * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless + * |client_chosen_version| is a reserved version. + */ + uint32_t *preferred_versions; + /** + * :member:`preferred_versionslen` is the number of versions that + * are contained in the array pointed by + * :member:`preferred_versions`. + */ + size_t preferred_versionslen; + /** + * :member:`other_versions` is the array of versions that are set in + * :member:`other_versions <ngtcp2_version_info.other_versions>` + * field of outgoing version_information QUIC transport parameter. + * + * For server, this corresponds to Fully-Deployed Versions in QUIC + * Version Negotiation draft. If this field is set not, it is set + * to :member:`preferred_versions` internally if + * :member:`preferred_versionslen` is not zero. If this field is + * not set, and :member:`preferred_versionslen` is zero, this field + * is set to :macro:`NGTCP2_PROTO_VER_V1` internally. + * + * Client must include |client_chosen_version| passed to + * `ngtcp2_conn_client_new` in this array if this field is set and + * |client_chosen_version| is not a reserved version. If this field + * is not set, |client_chosen_version| passed to + * `ngtcp2_conn_client_new` will be set in this field internally + * unless |client_chosen_version| is a reserved version. + */ + uint32_t *other_versions; + /** + * :member:`other_versionslen` is the number of versions that are + * contained in the array pointed by :member:`other_versions`. + */ + size_t other_versionslen; + /** + * :member:`original_version` is the original version that client + * initially used to make a connection attempt. If it is set, and + * it differs from |client_chosen_version| passed to + * `ngtcp2_conn_client_new`, the library assumes that client reacted + * upon Version Negotiation packet. Server does not use this field. + */ + uint32_t original_version; + /** + * :member:`no_pmtud`, if set to nonzero, disables Path MTU + * Discovery. + */ + int no_pmtud; } ngtcp2_settings; +#ifdef NGTCP2_USE_GENERIC_SOCKADDR +typedef struct ngtcp2_sockaddr { + uint16_t sa_family; + uint8_t sa_data[14]; +} ngtcp2_sockaddr; + +typedef struct ngtcp2_in_addr { + uint32_t s_addr; +} ngtcp2_in_addr; + +typedef struct ngtcp2_sockaddr_in { + uint16_t sin_family; + uint16_t sin_port; + ngtcp2_in_addr sin_addr; + uint8_t sin_zero[8]; +} ngtcp2_sockaddr_in; + +# define NGTCP2_SS_MAXSIZE 128 +# define NGTCP2_SS_ALIGNSIZE (sizeof(uint64_t)) +# define NGTCP2_SS_PAD1SIZE (NGTCP2_SS_ALIGNSIZE - sizeof(uint16_t)) +# define NGTCP2_SS_PAD2SIZE \ + (NGTCP2_SS_MAXSIZE - \ + (sizeof(uint16_t) + NGTCP2_SS_PAD1SIZE + NGTCP2_SS_ALIGNSIZE)) + +typedef struct ngtcp2_sockaddr_storage { + uint16_t ss_family; + uint8_t _ss_pad1[NGTCP2_SS_PAD1SIZE]; + uint64_t _ss_align; + uint8_t _ss_pad2[NGTCP2_SS_PAD2SIZE]; +} ngtcp2_sockaddr_storage; + +# undef NGTCP2_SS_PAD2SIZE +# undef NGTCP2_SS_PAD1SIZE +# undef NGTCP2_SS_ALIGNSIZE +# undef NGTCP2_SS_MAXSIZE + +typedef uint32_t ngtcp2_socklen; +#else +/** + * @typedef + * + * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr. If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * the generic struct sockaddr defined in ngtcp2.h. + */ +typedef struct sockaddr ngtcp2_sockaddr; +/** + * @typedef + * + * :type:`ngtcp2_sockaddr_storage` is typedefed to struct + * sockaddr_storage. If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is + * defined, it is typedefed to the generic struct sockaddr_storage + * defined in ngtcp2.h. + */ +typedef struct sockaddr_storage ngtcp2_sockaddr_storage; +typedef struct sockaddr_in ngtcp2_sockaddr_in; +/** + * @typedef + * + * :type:`ngtcp2_socklen` is typedefed to socklen_t. If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * uint32_t. + */ +typedef socklen_t ngtcp2_socklen; +#endif + +#if defined(NGTCP2_USE_GENERIC_SOCKADDR) || \ + defined(NGTCP2_USE_GENERIC_IPV6_SOCKADDR) +typedef struct ngtcp2_in6_addr { + uint8_t in6_addr[16]; +} ngtcp2_in6_addr; + +typedef struct ngtcp2_sockaddr_in6 { + uint16_t sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + ngtcp2_in6_addr sin6_addr; + uint32_t sin6_scope_id; +} ngtcp2_sockaddr_in6; +#else +typedef struct sockaddr_in6 ngtcp2_sockaddr_in6; +#endif + /** * @struct * @@ -1740,11 +2005,11 @@ typedef struct ngtcp2_addr { * :member:`addr` points to the buffer which contains endpoint * address. It must not be ``NULL``. */ - struct sockaddr *addr; + ngtcp2_sockaddr *addr; /** * :member:`addrlen` is the length of addr. */ - size_t addrlen; + ngtcp2_socklen addrlen; } ngtcp2_addr; /** @@ -1793,11 +2058,11 @@ typedef struct ngtcp2_path_storage { /** * :member:`local_addrbuf` is a buffer to store local address. */ - struct sockaddr_storage local_addrbuf; + ngtcp2_sockaddr_storage local_addrbuf; /** * :member:`remote_addrbuf` is a buffer to store remote address. */ - struct sockaddr_storage remote_addrbuf; + ngtcp2_sockaddr_storage remote_addrbuf; } ngtcp2_path_storage; /** @@ -1884,7 +2149,7 @@ typedef struct ngtcp2_crypto_cipher_ctx { * :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all * crypto related objects in one place. Use * `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial - * packet encryption. For Handshake and Short packets, use + * packet encryption. For Handshake and 1RTT packets, use * `ngtcp2_crypto_ctx_tls`. */ typedef struct ngtcp2_crypto_ctx { @@ -2019,9 +2284,11 @@ ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, * Negotiation packet has random type in wire format. For * convenience, this function sets * :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to - * :member:`dest->type <ngtcp2_pkt_hd.type>`, and sets 0 to - * :member:`dest->len <ngtcp2_pkt_hd.len>`. Version Negotiation - * packet occupies a single packet. + * :member:`dest->type <ngtcp2_pkt_hd.type>`, clears + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags + * <ngtcp2_pkt_hd.flags>`, and sets 0 to :member:`dest->len + * <ngtcp2_pkt_hd.len>`. Version Negotiation packet occupies a single + * packet. * * It stores the result in the object pointed by |dest|, and returns * the number of bytes decoded to read the packet header if it @@ -2127,8 +2394,8 @@ typedef struct ngtcp2_conn ngtcp2_conn; * `ngtcp2_conn_submit_crypto_data` function. Make sure that before * calling `ngtcp2_conn_submit_crypto_data` function, client * application must create initial packet protection keys and IVs, and - * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key` - * and + * provide them to ngtcp2 library using + * `ngtcp2_conn_install_initial_key`. * * This callback function must return 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call @@ -2146,7 +2413,7 @@ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); * Initial packet from client. An server application must implement * this callback, and generate initial keys and IVs for both * transmission and reception. Install them using - * `ngtcp2_conn_set_initial_key`. |dcid| is the destination + * `ngtcp2_conn_install_initial_key`. |dcid| is the destination * connection ID which client generated randomly. It is used to * derive initial packet protection keys. * @@ -2206,12 +2473,24 @@ typedef enum ngtcp2_crypto_level { * * The application should provide the given data to TLS stack. * - * The callback function must return 0 if it succeeds. If TLS stack - * reported error, return :macro:`NGTCP2_ERR_CRYPTO`. If application - * encounters fatal error, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * which makes the library call return immediately. If the other - * value is returned, it is treated as + * The callback function must return 0 if it succeeds, or one of the + * following negative error codes: + * + * - :macro:`NGTCP2_ERR_CRYPTO` + * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_PROTO` + * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * - :macro:`NGTCP2_ERR_NOMEM` + * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * + * If the other value is returned, it is treated as * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + * + * If application encounters fatal error, return + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. */ typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, @@ -2358,8 +2637,8 @@ typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, * :macro:`NGTCP2_HP_SAMPLELEN` bytes available for convenience. * * The callback function must return 0 if it succeeds, or - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. */ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx, @@ -2400,8 +2679,8 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * * :type:`ngtcp2_recv_stream_data` is invoked when stream data is * received. The stream is specified by |stream_id|. |flags| is the - * bitwise-OR of zero or more of NGTCP2_STREAM_DATA_FLAG_*. See - * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE`. If |flags| & + * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_* + * <NGTCP2_STREAM_DATA_FLAG_NONE>`. If |flags| & * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of * the data is the last data in this stream. |offset| is the offset * where this data begins. The library ensures that data is passed to @@ -2464,9 +2743,9 @@ typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id, * :type:`ngtcp2_stream_close` is invoked when a stream is closed. * This callback is not called when QUIC connection is closed before * existing streams are closed. |flags| is the bitwise-OR of zero or - * more of NGTCP2_STREAM_CLOSE_FLAG_*. See - * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE`. |app_error_code| indicates - * the error code of this closure if + * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_* + * <NGTCP2_STREAM_CLOSE_FLAG_NONE>`. |app_error_code| indicates the + * error code of this closure if * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in * |flags|. If it is not set, the stream was closed without any error * code, which generally means success. @@ -2665,7 +2944,8 @@ typedef int (*ngtcp2_update_key)( * @macro * * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the - * validation involving server preferred address. + * validation involving server preferred address. This flag is only + * set for client. */ #define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01 @@ -2674,9 +2954,9 @@ typedef int (*ngtcp2_update_key)( * * :type:`ngtcp2_path_validation` is a callback function which tells * the application the outcome of path validation. |flags| is zero or - * more of NGTCP2_PATH_VALIDATION_FLAG_*. See - * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE`. |path| is the path that - * was validated. If |res| is + * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* + * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |path| is the path that was + * validated. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, * the path validation succeeded. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, @@ -2829,7 +3109,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( * * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is * received. |flags| is bitwise-OR of zero or more of - * NGTCP2_DATAGRAM_FLAG_*. See :macro:`NGTCP2_DATAGRAM_FLAG_NONE`. + * :macro:`NGTCP2_DATAGRAM_FLAG_* <NGTCP2_DATAGRAM_FLAG_NONE>`. * * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it * indicates that DATAGRAM frame was received in 0RTT packet and a @@ -2906,6 +3186,27 @@ typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data); +/** + * @functypedef + * + * :type:`ngtcp2_version_negotiation` is invoked when the compatible + * version negotiation takes place. For client, it is called when it + * sees a change in version field of a long header packet. This + * callback function might be called multiple times for client. For + * server, it is called once when the version is negotiated. + * + * The implementation of this callback must install new Initial keys + * for |version|. Use `ngtcp2_conn_install_vneg_initial_key` to + * install keys. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data); + #define NGTCP2_CALLBACKS_VERSION_V1 1 #define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1 @@ -3144,6 +3445,12 @@ typedef struct ngtcp2_callbacks { * optional. */ ngtcp2_stream_stop_sending stream_stop_sending; + /** + * :member:`version_negotiation` is a callback function which is + * invoked when the compatible version negotiation takes place. + * This callback function must be specified. + */ + ngtcp2_version_negotiation version_negotiation; } ngtcp2_callbacks; /** @@ -3209,18 +3516,15 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( * whether packet |pkt| of length |pktlen| from client is acceptable * for the very initial packet to a connection. * - * If |dest| is not ``NULL`` and the function returns 0, - * :macro:`NGTCP2_ERR_RETRY`, or - * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`, the decoded packet header - * is stored to the object pointed by |dest|. + * If |dest| is not ``NULL`` and the function returns 0, or + * :macro:`NGTCP2_ERR_RETRY`, the decoded packet header is stored to + * the object pointed by |dest|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`NGTCP2_ERR_RETRY` * Retry packet should be sent. - * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` - * Version Negotiation packet should be sent. * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * The packet is not acceptable for the very first packet to a new * connection; or it failed to parse the packet header. @@ -3233,15 +3537,16 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, * * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and * initializes it as client. |dcid| is randomized destination - * connection ID. |scid| is source connection ID. |version| is a - * QUIC version to use. |path| is the network path where this QUIC - * connection is being established and must not be ``NULL``. - * |callbacks|, |settings|, and |params| must not be ``NULL``, and the - * function make a copy of each of them. |params| is local QUIC - * transport parameters and sent to a remote endpoint during - * handshake. |user_data| is the arbitrary pointer which is passed to - * the user-defined callback functions. If |mem| is ``NULL``, the - * memory allocator returned by `ngtcp2_mem_default()` is used. + * connection ID. |scid| is source connection ID. + * |client_chosen_version| is a QUIC version that a cilent chooses. + * |path| is the network path where this QUIC connection is being + * established and must not be ``NULL``. |callbacks|, |settings|, and + * |params| must not be ``NULL``, and the function make a copy of each + * of them. |params| is local QUIC transport parameters and sent to a + * remote endpoint during handshake. |user_data| is the arbitrary + * pointer which is passed to the user-defined callback functions. If + * |mem| is ``NULL``, the memory allocator returned by + * `ngtcp2_mem_default()` is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3251,11 +3556,11 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, */ NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned( ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - const ngtcp2_path *path, uint32_t version, int callbacks_version, - const ngtcp2_callbacks *callbacks, int settings_version, - const ngtcp2_settings *settings, int transport_params_version, - const ngtcp2_transport_params *params, const ngtcp2_mem *mem, - void *user_data); + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); /** * @function @@ -3264,13 +3569,13 @@ NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned( * initializes it as server. |dcid| is a destination connection ID. * |scid| is a source connection ID. |path| is the network path where * this QUIC connection is being established and must not be ``NULL``. - * |version| is a QUIC version to use. |callbacks|, |settings|, and - * |params| must not be ``NULL``, and the function make a copy of each - * of them. |params| is local QUIC transport parameters and sent to a - * remote endpoint during handshake. |user_data| is the arbitrary - * pointer which is passed to the user-defined callback functions. If - * |mem| is ``NULL``, the memory allocator returned by - * `ngtcp2_mem_default()` is used. + * |client_chosen_version| is a QUIC version that a client chooses. + * |callbacks|, |settings|, and |params| must not be ``NULL``, and the + * function make a copy of each of them. |params| is local QUIC + * transport parameters and sent to a remote endpoint during + * handshake. |user_data| is the arbitrary pointer which is passed to + * the user-defined callback functions. If |mem| is ``NULL``, the + * memory allocator returned by `ngtcp2_mem_default()` is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3280,11 +3585,11 @@ NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned( */ NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned( ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - const ngtcp2_path *path, uint32_t version, int callbacks_version, - const ngtcp2_callbacks *callbacks, int settings_version, - const ngtcp2_settings *settings, int transport_params_version, - const ngtcp2_transport_params *params, const ngtcp2_mem *mem, - void *user_data); + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); /** * @function @@ -3300,8 +3605,8 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of * length |pktlen| and processes it. |path| is the network path the * packet is delivered and must not be ``NULL``. |pi| is packet - * metadata and must not be ``NULL``. This function performs QUIC - * handshake as well. + * metadata and may be ``NULL``. This function performs QUIC handshake + * as well. * * This function must not be called from inside the callback * functions. @@ -3339,15 +3644,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned( /** * @function * - * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC - * handshake has completed. + * `ngtcp2_conn_handshake_completed` tells |conn| that the TLS stack + * declares TLS handshake completion. This does not mean QUIC + * handshake has completed. The library needs extra conditions to be + * met. */ NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake + * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC handshake * has completed. */ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); @@ -3391,6 +3698,42 @@ NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); +/** + * @function + * + * `ngtcp2_conn_install_vneg_initial_key` installs packet protection + * keying materials for Initial packets on compatible version + * negotiation for |version|. |rx_aead_ctx| is AEAD cipher context + * object and must be initialized with a decryption key. |rx_iv| is + * IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is a packet + * header protection cipher context object for decryption. Similarly, + * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing + * packets and are the same length with the decryption counterpart . + * If they have already been set, they are overwritten. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, + * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. + * :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key( + ngtcp2_conn *conn, uint32_t version, + const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, + const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); + /** * @function * @@ -3605,15 +3948,6 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts); -/** - * @function - * - * `ngtcp2_conn_get_idle_expiry` returns the time when a connection - * should be closed if it continues to be idle. If idle timeout is - * disabled, this function returns ``UINT64_MAX``. - */ -NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); - /** * @function * @@ -3887,6 +4221,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned( * pointed by |dest| of length |destlen|. This function performs QUIC * handshake as well. * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_udp_payload_size`. + * * Specifying -1 to |stream_id| means no new stream data to send. * * If |path| is not ``NULL``, this function stores the network path @@ -3955,8 +4292,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned( * * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this + * `ngtcp2_conn_write_connection_close` to handle error from this * function). Just keep calling `ngtcp2_conn_writev_stream`, * `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram` until it * returns a positive number (which indicates a complete packet is @@ -4040,6 +4376,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( * |dest| of length |destlen|. This function performs QUIC handshake * as well. * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_udp_payload_size`. + * * For |path| and |pi| parameters, refer to * `ngtcp2_conn_writev_stream`. * @@ -4086,8 +4425,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( * * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this + * `ngtcp2_conn_write_connection_close` to handle error from this * function). Just keep calling `ngtcp2_conn_writev_datagram`, * `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it * returns a positive number (which indicates a complete packet is @@ -4125,88 +4463,6 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); -/** - * @function - * - * `ngtcp2_conn_write_connection_close` writes a packet which contains - * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by - * |dest| whose capacity is |datalen|. - * - * If |path| is not ``NULL``, this function stores the network path - * with which the packet should be sent. Each addr field must point - * to the buffer which should be at least ``sizeof(struct - * sockaddr_storage)`` bytes long. The assignment might not be done - * if nothing is written to |dest|. - * - * If |pi| is not ``NULL``, this function stores packet metadata in it - * if it succeeds. The metadata includes ECN markings. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory - * :macro:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :macro:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( - ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, - ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t error_code, - const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_application_close` writes a packet which - * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed - * by |dest| whose capacity is |datalen|. - * - * If |path| is not ``NULL``, this function stores the network path - * with which the packet should be sent. Each addr field must point - * to the buffer which should be at least ``sizeof(struct - * sockaddr_storage)`` bytes long. The assignment might not be done - * if nothing is written to |dest|. - * - * If |pi| is not ``NULL``, this function stores packet metadata in it - * if it succeeds. The metadata includes ECN markings. - * - * If handshake has not been confirmed yet, CONNECTION_CLOSE (type - * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written - * instead. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory - * :macro:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :macro:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close_versioned( - ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, - ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t app_error_code, - const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts); - /** * @function * @@ -4367,10 +4623,20 @@ typedef struct ngtcp2_cid_token { NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest); +/** + * @function + * + * `ngtcp2_conn_get_client_chosen_version` returns the client chosen + * version. + */ +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn); + /** * @function * * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + * + * Until the version is negotiated, this function returns 0. */ NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); @@ -4458,18 +4724,32 @@ NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, /** * @function * - * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum - * outgoing UDP payload size for the current path. + * `ngtcp2_conn_get_path` returns the current path. */ -NGTCP2_EXTERN size_t -ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn); +NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_get_path` returns the current path. + * `ngtcp2_conn_get_max_udp_payload_size` returns the maximum UDP + * payload size that this local endpoint would send. This is the + * value of :member:`ngtcp2_settings.max_udp_payload_size` that is + * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. */ -NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn); +NGTCP2_EXTERN size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum UDP + * payload size for the current path. If + * :member:`ngtcp2_settings.no_udp_payload_size_shaping` is set to + * nonzero, this function is equivalent to + * `ngtcp2_conn_get_max_udp_payload_size`. Otherwise, it returns the + * maximum UDP payload size that is probed for the current path. + */ +NGTCP2_EXTERN size_t +ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn); /** * @function @@ -4556,6 +4836,15 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn); */ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of + * bytes in flight on the current path. If the former is smaller than + * the latter, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn); + /** * @function * @@ -4580,7 +4869,7 @@ ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet + * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/1RTT packet * encryption. The passed data will be passed to * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and * :type:`ngtcp2_hp_mask` callbacks. @@ -4629,7 +4918,7 @@ ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead, * @function * * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` - * object for Handshake/Short packet encryption. + * object for Handshake/1RTT packet encryption. */ NGTCP2_EXTERN const ngtcp2_crypto_ctx * ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); @@ -4672,6 +4961,18 @@ typedef enum ngtcp2_connection_close_error_code_type { * indicates the error code is application error code. */ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION` + * is a special case of QUIC transport error, and it indicates that + * client receives Version Negotiation packet. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE` + * is a special case of QUIC transport error, and it indicates that + * connection is closed because of idle timeout. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE } ngtcp2_connection_close_error_code_type; /** @@ -4681,14 +4982,14 @@ typedef enum ngtcp2_connection_close_error_code_type { * error code, its type, and the optional reason phrase. */ typedef struct ngtcp2_connection_close_error { - /** - * :member:`error_code` is the error code for connection closure. - */ - uint64_t error_code; /** * :member:`type` is the type of :member:`error_code`. */ ngtcp2_connection_close_error_code_type type; + /** + * :member:`error_code` is the error code for connection closure. + */ + uint64_t error_code; /** * :member:`frame_type` is the type of QUIC frame which triggers * this connection error. This field is set to 0 if the frame type @@ -4697,8 +4998,9 @@ typedef struct ngtcp2_connection_close_error { uint64_t frame_type; /** * :member:`reason` points to the buffer which contains a reason - * phrase. It may be NULL if there is no reason phrase. It is - * truncated to 1024 bytes. + * phrase. It may be NULL if there is no reason phrase. If it is + * received from a remote endpoint, it is truncated to at most 1024 + * bytes. */ uint8_t *reason; /** @@ -4708,6 +5010,155 @@ typedef struct ngtcp2_connection_close_error { size_t reasonlen; } ngtcp2_connection_close_error; +/** + * @function + * + * `ngtcp2_connection_close_error_default` initializes |ccerr| with + * the default values. It sets the following fields: + * + * - :member:`type <ngtcp2_connection_close_error.type>` = + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT` + * - :member:`error_code <ngtcp2_connection_close_error.error_code>` = + * :macro:`NGTCP2_NO_ERROR`. + * - :member:`frame_type <ngtcp2_connection_close_error.frame_type>` = + * 0 + * - :member:`reason <ngtcp2_connection_close_error.reason>` = NULL + * - :member:`reasonlen <ngtcp2_connection_close_error.reasonlen>` = 0 + */ +NGTCP2_EXTERN void +ngtcp2_connection_close_error_default(ngtcp2_connection_close_error *ccerr); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error` sets + * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * <ngtcp2_connection_close_error.error_code>` to |error_code|. + * |reason| is the reason phrase of length |reasonlen|. This function + * does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error_liberr` sets + * type and error_code based on |liberr|. + * + * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`, + * :member:`ccerr->type <ngtcp2_connection_close_error.type>` is set + * to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`, + * and :member:`ccerr->error_code + * <ngtcp2_connection_close_error.error_code>` to + * :macro:`NGTCP2_NO_ERROR`. If |liberr| is + * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type + * <ngtcp2_connection_close_error.type>` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`, + * and :member:`ccerr->error_code + * <ngtcp2_connection_close_error.error_code>` to + * :macro:`NGTCP2_NO_ERROR`. Otherwise, :member:`ccerr->type + * <ngtcp2_connection_close_error.type>` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * <ngtcp2_connection_close_error.error_code>` is set to an error code + * inferred by |liberr| (see + * `ngtcp2_err_infer_quic_transport_error_code`). |reason| is the + * reason phrase of length |reasonlen|. This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_liberr( + ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason, + size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error_tls_alert` sets + * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * <ngtcp2_connection_close_error.error_code>` to bitwise-OR of + * :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|. |reason| is the + * reason phrase of length |reasonlen|. This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_tls_alert( + ngtcp2_connection_close_error *ccerr, uint8_t tls_alert, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_application_error` sets + * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * and :member:`ccerr->error_code + * <ngtcp2_connection_close_error.error_code>` to |error_code|. + * |reason| is the reason phrase of length |reasonlen|. This function + * does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_application_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close` writes a packet which contains + * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed + * by |dest| whose capacity is |destlen|. + * + * For client, |destlen| should be at least + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If :member:`ccerr->type <ngtcp2_connection_close_error.type>` == + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * this function sends CONNECTION_CLOSE (type 0x1c) frame. If + * :member:`ccerr->type <ngtcp2_connection_close_error.type>` == + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * it sends CONNECTION_CLOSE (type 0x1d) frame. Otherwise, it does + * not produce any data, and returns 0. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts); + /** * @function * @@ -4819,8 +5270,8 @@ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); * returns |dest|. */ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, - const struct sockaddr *addr, - size_t addrlen); + const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen); /** * @function @@ -4833,8 +5284,8 @@ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, * capacity to store the copy. */ NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, - const struct sockaddr *addr, - size_t addrlen); + const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen); /** * @function @@ -4843,10 +5294,10 @@ NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, * arguments. This function copies |local_addr| and |remote_addr|. */ NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, - const struct sockaddr *remote_addr, - size_t remote_addrlen, + const ngtcp2_sockaddr *local_addr, + ngtcp2_socklen local_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, void *user_data); /** @@ -4870,8 +5321,7 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2 * * :type:`max_udp_payload_size - * <ngtcp2_settings.max_udp_payload_size>` = - * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` + * <ngtcp2_settings.max_udp_payload_size>` = 1452 * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` = * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT`. */ @@ -4951,11 +5401,12 @@ typedef struct ngtcp2_info { /** * @function * - * Returns a pointer to a ngtcp2_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. + * `ngtcp2_version` returns a pointer to a ngtcp2_info struct with + * version information about the run-time library in use. The + * |least_version| argument can be set to a 24 bit numerical value for + * the least accepted version number and if the condition is not met, + * this function will return a ``NULL``. Pass in 0 to skip the + * version checking. */ NGTCP2_EXTERN const ngtcp2_info *ngtcp2_version(int least_version); @@ -4985,6 +5436,41 @@ NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); */ NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); +/** + * @function + * + * `ngtcp2_is_supported_version` returns nonzero if the library supports + * QUIC version |version|. + */ +NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version); + +/* + * @function + * + * `ngtcp2_is_reserved_version` returns nonzero if |version| is a + * reserved version. + */ +NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version); + +/** + * @function + * + * `ngtcp2_select_version` selects and returns a version from the + * version set |offered_versions| of |offered_versionslen| elements. + * |preferred_versions| of |preferred_versionslen| elements specifies + * the preference of versions, which is sorted in the order of + * preference. All versions included in |preferred_versions| must be + * supported by the library, that is, passing a version to + * `ngtcp2_is_supported_version` must return nonzero. This function + * is intended to be used by client when it receives Version + * Negotiation packet. If no version is selected, this function + * returns 0. + */ +NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + const uint32_t *offered_versions, + size_t offered_versionslen); + /* * Versioned function wrappers */ @@ -5045,21 +5531,10 @@ NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); * struct version. */ #define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN, \ - ERROR_CODE, REASON, REASONLEN, TS) \ + CCERR, TS) \ ngtcp2_conn_write_connection_close_versioned( \ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ - (ERROR_CODE), (REASON), (REASONLEN), (TS)) - -/* - * `ngtcp2_conn_write_application_close` is a wrapper around - * `ngtcp2_conn_write_application_close_versioned` to set the correct - * struct version. - */ -#define ngtcp2_conn_write_application_close( \ - CONN, PATH, PI, DEST, DESTLEN, APP_ERROR_CODE, REASON, REASONLEN, TS) \ - ngtcp2_conn_write_application_close_versioned( \ - (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ - (APP_ERROR_CODE), (REASON), (REASONLEN), (TS)) + (CCERR), (TS)) /* * `ngtcp2_encode_transport_params` is a wrapper around @@ -5173,6 +5648,10 @@ NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); #define ngtcp2_settings_default(SETTINGS) \ ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS)) +#ifdef WIN32 +# pragma warning(pop) +#endif + #ifdef __cplusplus } #endif diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h index f364bceabd..e21165048d 100644 --- a/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h +++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h @@ -59,15 +59,6 @@ extern "C" { */ #define NGTCP2_CRYPTO_INITIAL_IVLEN 12 -/** - * @function - * - * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet - * encryption and decryption. - */ -NGTCP2_EXTERN ngtcp2_crypto_ctx * -ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); - /** * @function * @@ -96,33 +87,6 @@ NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, NGTCP2_EXTERN ngtcp2_crypto_ctx * ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle); -/** - * @function - * - * `ngtcp2_crypto_aead_init` initializes |aead| with the provided - * |aead_native_handle| which is an underlying AEAD object. - * - * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be - * a pointer to EVP_CIPHER. - * - * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be - * gnutls_cipher_algorithm_t casted to ``void *``. - * - * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must - * be a pointer to EVP_AEAD. - */ -NGTCP2_EXTERN ngtcp2_crypto_aead * -ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_native_handle); - -/** - * @function - * - * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher - * AEAD_AES_128_GCM for Retry packet integrity protection. - */ -NGTCP2_EXTERN ngtcp2_crypto_aead * -ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); - /** * @function * @@ -253,31 +217,6 @@ typedef enum ngtcp2_crypto_side { NGTCP2_EXTERN size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead); -/** - * @function - * - * `ngtcp2_crypto_derive_packet_protection_key` derives packet - * protection key. This function writes packet protection key into - * the buffer pointed by |key|. The length of derived key is - * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>` - * bytes. |key| must have enough capacity to store the key. This - * function writes packet protection IV into |iv|. The length of - * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead) - * <ngtcp2_crypto_packet_protection_ivlen>` bytes. |iv| must have - * enough capacity to store the IV. - * - * If |hp| is not NULL, this function also derives packet header - * protection key and writes the key into the buffer pointed by |hp|. - * The length of derived key is `ngtcp2_crypto_aead_keylen(aead) - * <ngtcp2_crypto_aead_keylen>` bytes. |hp|, if not NULL, must have - * enough capacity to store the key. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen); - /** * @function * @@ -703,18 +642,18 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( * successfully generated token starts with * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length * |secretlen| is an initial keying material to generate keys to - * encrypt the token. |remote_addr| of length |remote_addrlen| is an - * address of client. |retry_scid| is a Source Connection ID chosen - * by server and set in Retry packet. |odcid| is a Destination - * Connection ID in Initial packet sent by client. |ts| is the - * timestamp when the token is generated. + * encrypt the token. |version| is QUIC version. |remote_addr| of + * length |remote_addrlen| is an address of client. |retry_scid| is a + * Source Connection ID chosen by server and set in Retry packet. + * |odcid| is a Destination Connection ID in Initial packet sent by + * client. |ts| is the timestamp when the token is generated. * * This function returns the length of generated token if it succeeds, * or -1. */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token( - uint8_t *token, const uint8_t *secret, size_t secretlen, - const struct sockaddr *remote_addr, size_t remote_addrlen, + uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts); /** @@ -723,22 +662,23 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token( * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in * the buffer pointed by |token| of length |tokenlen|. |secret| of * length |secretlen| is an initial keying material to generate keys - * to decrypt the token. |remote_addr| of length |remote_addrlen| is - * an address of client. |dcid| is a Destination Connection ID in - * Initial packet sent by client. |timeout| is the period during - * which the token is valid. |ts| is the current timestamp. When - * validation succeeds, the extracted Destination Connection ID (which - * is the Destination Connection ID in Initial packet sent by client - * that triggered Retry packet) is stored to the buffer pointed by - * |odcid|. + * to decrypt the token. |version| is QUIC version of the Initial + * packet that contains this token. |remote_addr| of length + * |remote_addrlen| is an address of client. |dcid| is a Destination + * Connection ID in Initial packet sent by client. |timeout| is the + * period during which the token is valid. |ts| is the current + * timestamp. When validation succeeds, the extracted Destination + * Connection ID (which is the Destination Connection ID in Initial + * packet sent by client that triggered Retry packet) is stored to the + * buffer pointed by |odcid|. * * This function returns 0 if it succeeds, or -1. */ NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token( ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, - const uint8_t *secret, size_t secretlen, const struct sockaddr *remote_addr, - socklen_t remote_addrlen, const ngtcp2_cid *dcid, ngtcp2_duration timeout, - ngtcp2_tstamp ts); + const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts); /** * @function @@ -759,7 +699,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token( */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token( uint8_t *token, const uint8_t *secret, size_t secretlen, - const struct sockaddr *remote_addr, size_t remote_addrlen, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, ngtcp2_tstamp ts); /** @@ -776,8 +716,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token( */ NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token( const uint8_t *token, size_t tokenlen, const uint8_t *secret, - size_t secretlen, const struct sockaddr *remote_addr, - socklen_t remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts); + size_t secretlen, const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts); /** * @function @@ -897,6 +837,21 @@ NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, void *user_data); +/** + * @function + * + * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for + * |version| which is negotiated or being negotiated. |client_dcid| + * is the destination connection ID in first Initial packet of client. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.version_negotiation` field. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data); + #ifdef __cplusplus } #endif diff --git a/src/contrib/libngtcp2/ngtcp2/version.h b/src/contrib/libngtcp2/ngtcp2/version.h index 7821bd0fb5..c6c1d26c70 100644 --- a/src/contrib/libngtcp2/ngtcp2/version.h +++ b/src/contrib/libngtcp2/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "0.1.0" +#define NGTCP2_VERSION "0.5.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 0x000100 +#define NGTCP2_VERSION_NUM 0x000500 #endif /* VERSION_H */ -- GitLab