samba/redhat-4.23.patch
2026-06-03 19:02:49 -04:00

24228 lines
834 KiB
Diff

From e8384b6daea3b8091ad1bcfce84efc9e2c6a746d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Thu, 22 Jan 2026 14:27:09 +0100
Subject: [PATCH 01/66] s3:libads: Allocate cli_credentials on a stackframe
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This fixes:
ERROR: talloc_free with references at ../../source3/libads/ldap_utils.c:158
What happens:
* `struct cli_credentials *creds` is allocated on `ads` talloc context
* gensec_set_credentials() creates a talloc_reference to `creds`
* TALLOC_FREE(creds) sees two parents and complains
All other code is using temporary talloc_stackframe() for `creds`.
Do it here as well.
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Autobuild-User(master): Stefan Metzmacher <metze@samba.org>
Autobuild-Date(master): Fri Jan 23 11:20:28 UTC 2026 on atb-devel-224
---
source3/libads/ldap_utils.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/source3/libads/ldap_utils.c b/source3/libads/ldap_utils.c
index 9d6d962a2bc..d01afa69697 100644
--- a/source3/libads/ldap_utils.c
+++ b/source3/libads/ldap_utils.c
@@ -99,6 +99,7 @@ static ADS_STATUS ads_do_search_retry_internal(ADS_STRUCT *ads, const char *bind
struct cli_credentials *creds = NULL;
char *cred_name = NULL;
NTSTATUS ntstatus;
+ TALLOC_CTX *frame = talloc_stackframe();
if (NT_STATUS_EQUAL(ads_ntstatus(status), NT_STATUS_IO_TIMEOUT) &&
ads->config.ldap_page_size >= (lp_ldap_page_size() / 4) &&
@@ -119,18 +120,20 @@ static ADS_STATUS ads_do_search_retry_internal(ADS_STRUCT *ads, const char *bind
DBG_NOTICE("Search for %s in <%s> failed: %s\n",
expr, bp, ads_errstr(status));
SAFE_FREE(bp);
+ TALLOC_FREE(frame);
return status;
}
ntstatus = ads->auth.reconnect_state->fn(ads,
ads->auth.reconnect_state->private_data,
- ads, &creds);
+ frame, &creds);
if (!NT_STATUS_IS_OK(ntstatus)) {
DBG_WARNING("Failed to get creds for realm(%s): %s\n",
ads->server.realm, nt_errstr(ntstatus));
DBG_WARNING("Search for %s in <%s> failed: %s\n",
expr, bp, ads_errstr(status));
SAFE_FREE(bp);
+ TALLOC_FREE(frame);
return status;
}
@@ -151,11 +154,11 @@ static ADS_STATUS ads_do_search_retry_internal(ADS_STRUCT *ads, const char *bind
* callers depend on it being around.
*/
ads_disconnect(ads);
- TALLOC_FREE(creds);
+ TALLOC_FREE(frame);
SAFE_FREE(bp);
return status;
}
- TALLOC_FREE(creds);
+ TALLOC_FREE(frame);
*res = NULL;
--
2.53.0
From 7af95c7cb142aeb5f422a69d3b7a0ea3c0d2c2c2 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Mon, 26 Jan 2026 13:36:02 +0100
Subject: [PATCH 02/66] s3:rpc_client: Fix memory leak opening local named pipe
If no local server name was passed to rpc_pipe_open_local_np() then
get_myname() was called with NULL talloc context instead of the
current stackframe.
This was causing an increase of memory usage on busy servers with long-living
rpcd_* workers.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15979
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Noel Power <noel.power@suse.com>
Reviewed-by: Volker Lendecke <vl@samba.org>
Autobuild-User(master): Volker Lendecke <vl@samba.org>
Autobuild-Date(master): Tue Jan 27 10:13:40 UTC 2026 on atb-devel-224
(cherry picked from commit 24dc455362fb49ef81c99d95880e106a234ce29a)
---
source3/rpc_client/cli_pipe.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c
index e3f48526492..c61b8eb16cf 100644
--- a/source3/rpc_client/cli_pipe.c
+++ b/source3/rpc_client/cli_pipe.c
@@ -3625,7 +3625,7 @@ NTSTATUS rpc_pipe_open_local_np(
}
if (local_server_name == NULL) {
- local_server_name = get_myname(result);
+ local_server_name = get_myname(frame);
}
if (local_server_addr != NULL) {
--
2.53.0
From ab1287f78bd9d2397c8eb26fbedafa028e2aaa16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
Date: Tue, 2 Dec 2025 17:17:33 +0100
Subject: [PATCH 03/66] s3-selftest: mention in-memory ccache usage when
nothing is provided
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15840
Guenther
Signed-off-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
---
source3/script/tests/test_net_ads_kerberos.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source3/script/tests/test_net_ads_kerberos.sh b/source3/script/tests/test_net_ads_kerberos.sh
index 8a3c9ef2bc7..92d3996d078 100755
--- a/source3/script/tests/test_net_ads_kerberos.sh
+++ b/source3/script/tests/test_net_ads_kerberos.sh
@@ -30,6 +30,7 @@ KRB5CCNAME="FILE:$KRB5CCNAME_PATH"
## Test "net ads kerberos kinit" variants
#################################################
+#simply uses in memory ccache
testit "net_ads_kerberos_kinit" \
"$VALGRIND" "$BINDIR"/net ads kerberos kinit \
-U"$USERNAME"%"$PASSWORD" "$ADDARGS" \
@@ -50,6 +51,7 @@ rm -f "$KRB5CCNAME_PATH"
# --use-krb5-ccache=${KRB5CCNAME} \
# || failed=$((failed + 1))
+#simply uses in memory ccache
testit "net_ads_kerberos_kinit (-P)" \
"$VALGRIND" "$BINDIR"/net ads kerberos kinit \
-P "$ADDARGS" \
--
2.53.0
From 0aa0d39e9a5deb77114f40930b599f11fd7cf3b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
Date: Tue, 2 Dec 2025 17:18:41 +0100
Subject: [PATCH 04/66] s3-selftest: verify KRB5CCNAME presence after kinit
using klist
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15840
Guenther
Signed-off-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
---
source3/script/tests/test_net_ads_kerberos.sh | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/source3/script/tests/test_net_ads_kerberos.sh b/source3/script/tests/test_net_ads_kerberos.sh
index 92d3996d078..c53520cf733 100755
--- a/source3/script/tests/test_net_ads_kerberos.sh
+++ b/source3/script/tests/test_net_ads_kerberos.sh
@@ -14,6 +14,12 @@ PREFIX="$4"
shift 4
ADDARGS="$*"
+if [ -x $(which klist) ]; then
+ KLIST=$(which klist);
+else
+ KLIST="test -e";
+fi
+
incdir=$(dirname "$0")/../../../testprogs/blackbox
. "$incdir"/subunit.sh
@@ -41,6 +47,9 @@ testit "net_ads_kerberos_kinit (KRB5CCNAME env set)" \
"$VALGRIND" "$BINDIR"/net ads kerberos kinit \
-U"$USERNAME"%"$PASSWORD" "$ADDARGS" \
|| failed=$((failed + 1))
+testit "klist env $KRB5CCNAME" \
+ "$KLIST" "$KRB5CCNAME" \
+ || failed=$((failed +1))
unset KRB5CCNAME
rm -f "$KRB5CCNAME_PATH"
@@ -62,6 +71,9 @@ testit "net_ads_kerberos_kinit (-P and KRB5CCNAME env set)" \
"$VALGRIND" "$BINDIR"/net ads kerberos kinit \
-P "$ADDARGS" \
|| failed=$((failed + 1))
+testit "klist env $KRB5CCNAME" \
+ "$KLIST" "$KRB5CCNAME" \
+ || failed=$((failed +1))
unset KRB5CCNAME
rm -f "$KRB5CCNAME_PATH"
--
2.53.0
From b9c07d59c6a20931b80fa104629477ab8f78b4ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
Date: Tue, 2 Dec 2025 17:01:31 +0100
Subject: [PATCH 05/66] s3-selftest: Activate "net ads kerberos kinit" tests
with --use-krb5-ccache
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15840
Guenther
Signed-off-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
---
selftest/knownfail | 2 ++
source3/script/tests/test_net_ads_kerberos.sh | 30 +++++++++++--------
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/selftest/knownfail b/selftest/knownfail
index ab2d79d7114..76f1dae605d 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -338,3 +338,5 @@
# We currently don't send referrals for LDAP modify of non-replicated attrs
^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.*
+
+^samba3.blackbox.net_ads_kerberos.*.klist.*--use-krb5-ccache.*
diff --git a/source3/script/tests/test_net_ads_kerberos.sh b/source3/script/tests/test_net_ads_kerberos.sh
index c53520cf733..b7933bab6a6 100755
--- a/source3/script/tests/test_net_ads_kerberos.sh
+++ b/source3/script/tests/test_net_ads_kerberos.sh
@@ -53,12 +53,15 @@ testit "klist env $KRB5CCNAME" \
unset KRB5CCNAME
rm -f "$KRB5CCNAME_PATH"
-# --use-krb5-ccache is not working
-#testit "net_ads_kerberos_kinit (with --use-krb5-ccache)" \
-# $VALGRIND $BINDIR/net ads kerberos kinit \
-# -U$USERNAME%$PASSWORD $ADDARGS \
-# --use-krb5-ccache=${KRB5CCNAME} \
-# || failed=$((failed + 1))
+testit "net_ads_kerberos_kinit (with --use-krb5-ccache)" \
+ "$VALGRIND" "$BINDIR"/net ads kerberos kinit \
+ -U"$USERNAME"%"$PASSWORD" "$ADDARGS" \
+ --use-krb5-ccache="${KRB5CCNAME_PATH}" \
+ || failed=$((failed + 1))
+testit "klist --use-krb5-ccache $KRB5CCNAME_PATH" \
+ "$KLIST" "$KRB5CCNAME_PATH" \
+ || failed=$((failed +1))
+rm -f "$KRB5CCNAME_PATH"
#simply uses in memory ccache
testit "net_ads_kerberos_kinit (-P)" \
@@ -77,12 +80,15 @@ testit "klist env $KRB5CCNAME" \
unset KRB5CCNAME
rm -f "$KRB5CCNAME_PATH"
-# --use-krb5-ccache is not working
-#testit "net_ads_kerberos_kinit (-P with --use-krb5-ccache)" \
-# $VALGRIND $BINDIR/net ads kerberos kinit \
-# -P $ADDARGS \
-# --use-krb5-ccache=${KRB5CCNAME} \
-# || failed=$((failed + 1))
+testit "net_ads_kerberos_kinit (-P with --use-krb5-ccache)" \
+ "$VALGRIND" "$BINDIR"/net ads kerberos kinit \
+ -P "$ADDARGS" \
+ --use-krb5-ccache="${KRB5CCNAME_PATH}" \
+ || failed=$((failed + 1))
+testit "klist --use-krb5-ccache $KRB5CCNAME_PATH" \
+ "$KLIST" "$KRB5CCNAME_PATH" \
+ || failed=$((failed +1))
+rm -f "$KRB5CCNAME_PATH"
#################################################
--
2.53.0
From c82b7636b633575621e8e5964a93332956c238ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
Date: Tue, 2 Dec 2025 16:56:44 +0100
Subject: [PATCH 06/66] s3-net: properly setup krb5 ccache name via
--use-krb5-ccache
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15840
Guenther
Signed-off-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
---
selftest/knownfail | 2 --
source3/utils/net.c | 19 ++++++++++++-------
source3/utils/net_ads.c | 4 ++++
3 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/selftest/knownfail b/selftest/knownfail
index 76f1dae605d..ab2d79d7114 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -338,5 +338,3 @@
# We currently don't send referrals for LDAP modify of non-replicated attrs
^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.*
-
-^samba3.blackbox.net_ads_kerberos.*.klist.*--use-krb5-ccache.*
diff --git a/source3/utils/net.c b/source3/utils/net.c
index ecabd980d0c..271c96cf804 100644
--- a/source3/utils/net.c
+++ b/source3/utils/net.c
@@ -1396,7 +1396,7 @@ static struct functable net_func[] = {
cli_credentials_get_principal_obtained(c->creds);
enum credentials_obtained password_obtained =
cli_credentials_get_password_obtained(c->creds);
- char *krb5ccname = NULL;
+ const char *krb5ccname = NULL;
if (principal_obtained == CRED_SPECIFIED) {
c->explicit_credentials = true;
@@ -1415,15 +1415,20 @@ static struct functable net_func[] = {
}
/* cli_credentials_get_ccache_name_obtained() would not work
- * here, we also cannot get the content of --use-krb5-ccache= so
- * for now at least honour the KRB5CCNAME environment variable
- * to get 'net ads kerberos' functions to work at all - gd */
-
- krb5ccname = getenv("KRB5CCNAME");
- if (krb5ccname == NULL) {
+ * here but we can now access the content of the
+ * --use-krb5-ccache option via cli credentials. Fallback to
+ * KRB5CCNAME environment variable to get 'net ads kerberos'
+ * functions to work at all - gd */
+
+ krb5ccname = cli_credentials_get_out_ccache_name(c->creds);
+ if (krb5ccname == NULL || krb5ccname[0] == '\0') {
+ krb5ccname = getenv("KRB5CCNAME");
+ }
+ if (krb5ccname == NULL || krb5ccname[0] == '\0') {
krb5ccname = talloc_strdup(c, "MEMORY:net");
}
if (krb5ccname == NULL) {
+ DBG_ERR("Not able to setup krb5 ccache");
exit(1);
}
c->opt_krb5_ccache = krb5ccname;
diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c
index d49b7537e71..5c57a0b290e 100644
--- a/source3/utils/net_ads.c
+++ b/source3/utils/net_ads.c
@@ -3245,7 +3245,11 @@ static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **
if (ret) {
d_printf(_("failed to kinit password: %s\n"),
nt_errstr(status));
+ return ret;
}
+
+ d_printf("Stored Kerberos TGT in: %s\n", c->opt_krb5_ccache);
+
return ret;
}
--
2.53.0
From 4f5ffea631d805564f7e92cc5f0f2f7ad55ba493 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
Date: Sat, 13 Dec 2025 13:49:37 +0100
Subject: [PATCH 07/66] doc-xml: Document "net ads kerberos" commands
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15840
Guenther
Signed-off-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
Autobuild-User(master): Günther Deschner <gd@samba.org>
Autobuild-Date(master): Mon Jan 5 15:49:04 UTC 2026 on atb-devel-224
---
docs-xml/manpages/net.8.xml | 139 ++++++++++++++++++++++++++++++++++++
1 file changed, 139 insertions(+)
diff --git a/docs-xml/manpages/net.8.xml b/docs-xml/manpages/net.8.xml
index d9293d0bb34..737415b3722 100644
--- a/docs-xml/manpages/net.8.xml
+++ b/docs-xml/manpages/net.8.xml
@@ -1810,7 +1810,146 @@ the following entry types;
</refsect2>
+<refsect2>
+ <title>ADS KERBEROS</title>
+
+<para>
+ Issue Kerberos operations against an Active Directory KDC.
+</para>
+
+</refsect2>
+
+<refsect2>
+ <title>ADS KERBEROS KINIT</title>
+
+<para>
+ Issue a kinit request for a given user. When no other options are
+ defined the ticket granting ticket (TGT) will be stored in a memory cache.
+</para>
+
+<para>
+ To store the TGT in a different location either use the
+ <option>--krb5-ccache</option> option or set the
+ <replaceable>KRB5CCNAME</replaceable> environment variable.
+</para>
+
+<para>Example: <userinput>net ads kerberos kinit -P --krb5-ccache=/tmp/krb5cache</userinput></para>
+
+</refsect2>
+
+<refsect2>
+ <title>ADS KERBEROS RENEW</title>
+
+<para>
+ Renew an already acquired ticket granting ticket (TGT).
+</para>
+
+<para>Example: <userinput>net ads kerberos renew</userinput></para>
+
+</refsect2>
+
+<refsect2>
+ <title>ADS KERBEROS PAC</title>
+
+<para>
+ Request a Kerberos PAC while authenticating to an Active Directory KDC.
+</para>
+
+<para>
+ The following commands are provided:
+</para>
+
+<simplelist>
+<member>net ads kerberos pac dump - Dump a PAC to stdout.</member>
+<member>net ads kerneros pac save - Save a PAC to a file.</member>
+</simplelist>
+
+<para>
+ All commands allow to define an impersonation principal to do a Kerberos
+ Service for User (S4U2SELF) operation via
+ the <replaceable>impersonate=STRING</replaceable> option.
+ The impersonation principal can have multiple different formats:
+</para>
+
+<itemizedlist>
+ <listitem>
+ <para><replaceable>user@MY.REALM</replaceable></para>
+ <para>This is the default format.</para>
+ </listitem>
+ <listitem>
+ <para><replaceable>user@MY.REALM@MY.REALM</replaceable></para>
+ <para>The Kerberos Service for User (S4U2SELF) also supports
+ Enterprise Principals.</para>
+ </listitem>
+ <listitem>
+ <para><replaceable>user@UPN.SUFFIX@MY.REALM</replaceable></para>
+ <para>Enterprise Principal using a defined upn suffix.</para>
+ </listitem>
+ <listitem>
+ <para><replaceable>user@WORKGROUP@MY.REALM</replaceable></para>
+ <para>Enterprise Principal with netbios domain name.
+ This format is currently not supported by Samba AD.</para>
+ </listitem>
+</itemizedlist>
+<para>
+ By default net will request a service ticket for the local service
+ of the joined machine. A different service can be defined via
+ <replaceable>local_service=STRING</replaceable>.
+</para>
+
+</refsect2>
+<refsect2>
+ <title>ADS KERBEROS PAC DUMP [impersonate=string] [local_service=string] [pac_buffer_type=int]</title>
+
+<para>
+ Request a Kerberos PAC while authenticating to an Active Directory KDC.
+ The PAC will be printed on stdout.
+</para>
+
+<para>
+ When no specific pac_buffer is selected, all buffers will be printed.
+ It is possible to select a specific one via
+ <replaceable>pac_buffer_type=INT</replaceable> from this list:
+</para>
+
+<simplelist>
+<member>1 PAC_TYPE_LOGON_INFO</member>
+<member>2 PAC_TYPE_CREDENTIAL_INFO</member>
+<member>6 PAC_TYPE_SRV_CHECKSUM</member>
+<member>7 PAC_TYPE_KDC_CHECKSUM</member>
+<member>10 PAC_TYPE_LOGON_NAME</member>
+<member>11 PAC_TYPE_CONSTRAINED_DELEGATION</member>
+<member>12 PAC_TYPE_UPN_DNS_INFO</member>
+<member>13 PAC_TYPE_CLIENT_CLAIMS_INFO</member>
+<member>14 PAC_TYPE_DEVICE_INFO</member>
+<member>15 PAC_TYPE_DEVICE_CLAIMS_INFO</member>
+<member>16 PAC_TYPE_TICKET_CHECKSUM</member>
+<member>17 PAC_TYPE_ATTRIBUTES_INFO</member>
+<member>18 PAC_TYPE_REQUESTER_SID</member>
+<member>19 PAC_TYPE_FULL_CHECKSUM</member>
+</simplelist>
+
+<para>Example: <userinput>net ads kerberos pac dump -P impersonate=anyuser@MY.REALM.COM</userinput></para>
+
+</refsect2>
+
+<refsect2>
+ <title>ADS KERBEROS PAC SAVE [impersonate=string] [local_service=string] [filename=string]</title>
+
+<para>
+ Request a Kerberos PAC while authenticating to an Active Directory KDC.
+ The PAC will be saved in a file.
+</para>
+
+<para>
+ The filename to store the PAC can be set via the
+ <replaceable>filename=STRING</replaceable> option.
+</para>
+
+<para>Example: <userinput>net ads kerberos pac save -U user%password filename=/tmp/pacstore</userinput></para>
+
+</refsect2>
<refsect2>
<title>SAM CREATEBUILTINGROUP &lt;NAME&gt;</title>
--
2.53.0
From f634526bd95b8396ea7f5f1c8ed059eb01a5286b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Tue, 3 Feb 2026 12:53:10 +0100
Subject: [PATCH 08/66] s3:utils: 'net ads kerberos kinit' should use also
default ccache name from krb5.conf
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is re-introducing the behavior from samba-4.20 where both these
commands operated on the same ccache (default_ccache_name in
[libdefaults] section of krb5.conf)
'net ads kerberos kinit -P'
'klist'
With samba-4.21 it no longer works, 'net ads kerberos kinit -P'
fallbacks to 'MEMORY:net' (which is of a very limited use, ticket
cannot be used by other process) and klist finds no ticket.
The order is changed from:
--use-krb5-ccache
env "KRB5CCNAME"
"MEMORY:net"
to ("MEMORY:net" is removed):
--use-krb5-ccache
env "KRB5CCNAME"
default_ccache_name
'--use-krb5-ccache=MEMORY:net' can be used to validate the credentials.
Use smb_force_krb5_cc_default_name() instead of krb5_cc_default_name()
because of commit:
1ca6fb5 make sure krb5_cc_default[_name]() is no longer used directly
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15993
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit 4cc6a13590434f6a3aa1add663728188970d727e)
---
source3/utils/net.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/source3/utils/net.c b/source3/utils/net.c
index 271c96cf804..0ce03f8213d 100644
--- a/source3/utils/net.c
+++ b/source3/utils/net.c
@@ -54,6 +54,7 @@
#include "source3/utils/passwd_proto.h"
#include "auth/gensec/gensec.h"
#include "lib/param/param.h"
+#include "lib/krb5_wrap/krb5_samba.h"
#ifdef WITH_FAKE_KASERVER
#include "utils/net_afs.h"
@@ -1414,18 +1415,33 @@ static struct functable net_func[] = {
CRED_SPECIFIED);
}
- /* cli_credentials_get_ccache_name_obtained() would not work
- * here but we can now access the content of the
- * --use-krb5-ccache option via cli credentials. Fallback to
- * KRB5CCNAME environment variable to get 'net ads kerberos'
- * functions to work at all - gd */
-
+ /*
+ * Priority order for krb5 credential cache name
+ *
+ * via cli_credentials_get_out_ccache_name() :
+ *
+ * 1. '--use-krb5-ccache' option
+ *
+ * via krb5_cc_default_name() :
+ *
+ * 2. KRB5CCNAME environment variable
+ * 3. default_ccache_name in [libdefaults] section of krb5.conf
+ * 4. ...more - krb5_cc_default_name() always returns something
+ * - see documentation
+ */
krb5ccname = cli_credentials_get_out_ccache_name(c->creds);
if (krb5ccname == NULL || krb5ccname[0] == '\0') {
- krb5ccname = getenv("KRB5CCNAME");
- }
- if (krb5ccname == NULL || krb5ccname[0] == '\0') {
- krb5ccname = talloc_strdup(c, "MEMORY:net");
+ krb5_context ct = NULL;
+ krb5_error_code ret = smb_krb5_init_context_common(&ct);
+
+ if (ret == 0) {
+ krb5ccname = smb_force_krb5_cc_default_name(ct);
+ if (krb5ccname != NULL) {
+ krb5ccname = talloc_strdup(c,
+ krb5ccname);
+ }
+ krb5_free_context(ct);
+ }
}
if (krb5ccname == NULL) {
DBG_ERR("Not able to setup krb5 ccache");
--
2.53.0
From 0ca830d6ddded29b2b5d1969ebcbc4df1156656e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Thu, 5 Feb 2026 16:04:25 +0100
Subject: [PATCH 09/66] manpages: Update NET ADS KERBEROS KINIT manpage
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15993
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
Autobuild-User(master): Pavel Filipensky <pfilipensky@samba.org>
Autobuild-Date(master): Thu Feb 5 21:11:13 UTC 2026 on atb-devel-224
(cherry picked from commit 9d083a28fe45afd8f82441c6e24255e4c64c113b)
---
docs-xml/manpages/net.8.xml | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/docs-xml/manpages/net.8.xml b/docs-xml/manpages/net.8.xml
index 737415b3722..b793361a27f 100644
--- a/docs-xml/manpages/net.8.xml
+++ b/docs-xml/manpages/net.8.xml
@@ -1823,17 +1823,37 @@ the following entry types;
<title>ADS KERBEROS KINIT</title>
<para>
- Issue a kinit request for a given user. When no other options are
- defined the ticket granting ticket (TGT) will be stored in a memory cache.
+ Issue a kinit request for a given user. The following methods can be used
+ to specify where to store the ticket granting ticket (TGT) (in order of
+ precedence):
</para>
-<para>
- To store the TGT in a different location either use the
- <option>--krb5-ccache</option> option or set the
- <replaceable>KRB5CCNAME</replaceable> environment variable.
-</para>
+<itemizedlist>
+ <listitem>
+ <para>option <option>--use-krb5-ccache</option></para>
+ </listitem>
+ <listitem>
+ <para><replaceable>KRB5CCNAME</replaceable> environment variable</para>
+ </listitem>
+ <listitem>
+ <para><parameter>default_ccache_name</parameter> setting in <filename>krb5.conf</filename></para>
+ </listitem>
+</itemizedlist>
-<para>Example: <userinput>net ads kerberos kinit -P --krb5-ccache=/tmp/krb5cache</userinput></para>
+<variablelist><title>Examples:</title>
+<varlistentry>
+<term>Use file based cache (FILE:/tmp/krb5cache)</term>
+<listitem><literallayout>
+net ads kerberos kinit -P --use-krb5-ccache=/tmp/krb5cache
+</literallayout></listitem>
+</varlistentry>
+<varlistentry>
+<term>Use memory cache (MEMORY:net) to verify the authentication</term>
+<listitem><literallayout>
+net ads kerberos kinit -P --use-krb5-ccache=MEMORY:net
+</literallayout></listitem>
+</varlistentry>
+</variablelist>
</refsect2>
--
2.53.0
From 44b613d80c6a3818cc6ca593d57d51cd1bc00aa5 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power@suse.com>
Date: Fri, 13 Feb 2026 11:54:46 +0000
Subject: [PATCH 10/66] selftest: Update tests to use
--use-kereros=desired|required no creds
Add tests to call smbclient without passing credentials to
demonstrate failure with --use-kereros=desired
Also add knownfail
Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit a22af9420965083b99b956477d1833000b7f2414)
---
selftest/knownfail | 2 ++
source3/script/tests/test_smbclient_kerberos.sh | 12 ++++++++++++
2 files changed, 14 insertions(+)
diff --git a/selftest/knownfail b/selftest/knownfail
index ab2d79d7114..f0a5f7bb935 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -315,6 +315,8 @@
# ad_member don't support ntlmv1 (not even over SMB1)
^samba3.blackbox.smbclient_auth.plain.*option=clientntlmv2auth=no.member.creds.*as.user.*ad_member
^samba3.blackbox.smbclient_auth.plain.*option=clientntlmv2auth=no.*mNT1.member.creds.*as.user.*ad_member
+# regression smbclient using --use-kerberos=desired https://bugzilla.samba.org/show_bug.cgi?id=15789
+samba3.blackbox.smbclient.kerberos.smbclient.smb3.kerberos.desired \(no user/pass\).*
#nt-vfs server blocks read with execute access
^samba4.smb2.read.access
#ntvfs server blocks copychunk with execute access on read handle
diff --git a/source3/script/tests/test_smbclient_kerberos.sh b/source3/script/tests/test_smbclient_kerberos.sh
index 31678d17e28..1139efd70d7 100755
--- a/source3/script/tests/test_smbclient_kerberos.sh
+++ b/source3/script/tests/test_smbclient_kerberos.sh
@@ -73,6 +73,18 @@ test_smbclient "smbclient.smb3.kerberos.desired[//${SERVER}/tmp]" \
--use-kerberos=desired -U${USERNAME}%${PASSWORD} -mSMB3 ||
failed=$(expr $failed + 1)
+test_smbclient "smbclient.smb3.kerberos.desired (no user/pass) [//${SERVER}/tmp]" \
+ "ls; quit" //${SERVER}/tmp \
+ --use-kerberos=desired -mSMB3 ||
+ failed=$(expr $failed + 1)
+
+test_smbclient "smbclient.smb3.kerberos.required (no user/pass) [//${SERVER}/tmp]" \
+ "ls; quit" //${SERVER}/tmp \
+ --use-kerberos=required -mSMB3 ||
+ failed=$(expr $failed + 1)
+
+
+
$samba_kdestroy
rm -rf $KRB5CCNAME_PATH
--
2.53.0
From 65f70c0505759489a8b219e1297f8cdee2cc260a Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power@suse.com>
Date: Mon, 19 Jan 2026 15:46:59 +0000
Subject: [PATCH 11/66] auth/credentials: Fix regression with
--use-kerberos=desired for smbclient
As part of the gse_krb5 processing the following call chain
gensec_gse_client_start()
---> gensec_kerberos_possible()
---> cli_credentials_authentication_requested()
gensec_kerberos_possible() will always fail when
cli_credentials_get_kerberos_state() returns CRED_USE_KERBEROS_DESIRED
It seems since use kerberos == desired is the default that it isn't
necessary to see if credentials were modified to indicated authentication
was requested. gensec_kerberos_possible() should afaics return true
if kerberos is desired OR required (regardless of whether credentials
were requested)
This commit removes the knownfail associated with this bug.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=15789
Signed-off-by: <noel.power@suse.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit 88f42eb222f299189d5f5f8204ae353e63a50970)
---
auth/gensec/gensec_util.c | 5 -----
selftest/knownfail | 2 --
2 files changed, 7 deletions(-)
diff --git a/auth/gensec/gensec_util.c b/auth/gensec/gensec_util.c
index 0c7688d33d2..af6d198d48f 100644
--- a/auth/gensec/gensec_util.c
+++ b/auth/gensec/gensec_util.c
@@ -362,7 +362,6 @@ char *gensec_get_unparsed_target_principal(struct gensec_security *gensec_securi
NTSTATUS gensec_kerberos_possible(struct gensec_security *gensec_security)
{
struct cli_credentials *creds = gensec_get_credentials(gensec_security);
- bool auth_requested = cli_credentials_authentication_requested(creds);
enum credentials_use_kerberos krb5_state =
cli_credentials_get_kerberos_state(creds);
char *user_principal = NULL;
@@ -370,10 +369,6 @@ NTSTATUS gensec_kerberos_possible(struct gensec_security *gensec_security)
const char *target_principal = gensec_get_target_principal(gensec_security);
const char *hostname = gensec_get_target_hostname(gensec_security);
- if (!auth_requested) {
- return NT_STATUS_INVALID_PARAMETER;
- }
-
if (krb5_state == CRED_USE_KERBEROS_DISABLED) {
return NT_STATUS_INVALID_PARAMETER;
}
diff --git a/selftest/knownfail b/selftest/knownfail
index f0a5f7bb935..ab2d79d7114 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -315,8 +315,6 @@
# ad_member don't support ntlmv1 (not even over SMB1)
^samba3.blackbox.smbclient_auth.plain.*option=clientntlmv2auth=no.member.creds.*as.user.*ad_member
^samba3.blackbox.smbclient_auth.plain.*option=clientntlmv2auth=no.*mNT1.member.creds.*as.user.*ad_member
-# regression smbclient using --use-kerberos=desired https://bugzilla.samba.org/show_bug.cgi?id=15789
-samba3.blackbox.smbclient.kerberos.smbclient.smb3.kerberos.desired \(no user/pass\).*
#nt-vfs server blocks read with execute access
^samba4.smb2.read.access
#ntvfs server blocks copychunk with execute access on read handle
--
2.53.0
From 8c955cad98b197936fceaf98306047e1f929ddfe Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power@suse.com>
Date: Mon, 19 Jan 2026 16:10:10 +0000
Subject: [PATCH 12/66] s3/libsmb: cli_session_creds_init fails when kerberos
is desired
There is a regression with code using cli_session_creds_init when
cli_credentials_get_kerberos_state() returns CRED_USE_KERBEROS_DESIRED
Authentication succeeds when boolean fallback_after_kerberos is false
and fails when true.
There doesn't seem to be a good reason why the value of
fallback_after_kerberos should initialise the krb5 ccache or not.
It would seems that krb5 cache should be setup for creds
for *any* kerberos auth (whether fallback is enabled or not)
Partial patch from <will69@gmx.de> (see bug referenced below)
Bug: https://bugzilla.samba.org/show_bug.cgi?id=15789
Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit 1c48599105736499d18aa1f647bce9e1f8dbdcca)
---
source3/libsmb/cliconnect.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 116f746d37e..3fd423d8e5f 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -218,7 +218,7 @@ struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
goto fail;
}
}
- } else if (use_kerberos && !fallback_after_kerberos) {
+ } else if (use_kerberos) {
const char *error_string = NULL;
int rc;
--
2.53.0
From 015167aea7ece2bb683f86aa4b8c688d7a83267d Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power@suse.com>
Date: Mon, 19 Jan 2026 16:18:02 +0000
Subject: [PATCH 13/66] s3/libsmb: block anon authentication fallback is
use-kerberos = desired
When cli_credentials_get_kerberos_state returns CRED_USE_KERBEROS_REQUIRED
libsmbclient method SMBC_server_internal will still try to fallback to
anon NTLM. This patch prevents that.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=15789
Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
Autobuild-User(master): Noel Power <npower@samba.org>
Autobuild-Date(master): Tue Feb 17 16:06:18 UTC 2026 on atb-devel-224
(cherry picked from commit bc868800276fe09cbcb206ebe4cb4da32af7599f)
---
source3/libsmb/libsmb_server.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c
index f9b52e1f05a..8c7208aaee0 100644
--- a/source3/libsmb/libsmb_server.c
+++ b/source3/libsmb/libsmb_server.c
@@ -632,6 +632,8 @@ SMBC_server_internal(TALLOC_CTX *ctx,
password_used = "";
if (smbc_getOptionNoAutoAnonymousLogin(context) ||
+ cli_credentials_get_kerberos_state(creds) ==
+ CRED_USE_KERBEROS_REQUIRED ||
!NT_STATUS_IS_OK(cli_session_setup_anon(c))) {
cli_shutdown(c);
--
2.53.0
From 3f66a4fbb46f614bf81533677944b1093439aaf8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Wed, 18 Mar 2026 20:24:37 +0100
Subject: [PATCH 14/66] s3:libnet: Fix DC numeric ip handling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is fixing regression introduced via 82f53c8
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15999
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
---
source3/libnet/libnet_join.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c
index 609b2b96222..66d682a5b95 100644
--- a/source3/libnet/libnet_join.c
+++ b/source3/libnet/libnet_join.c
@@ -2643,7 +2643,7 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
struct sockaddr_storage ss = {0};
const char *numeric_dcip = info->dc_address + 2;
- if (numeric_dcip[0] == '\0') {
+ if (numeric_dcip[0] != '\0') {
if (!interpret_string_addr(&ss, numeric_dcip,
AI_NUMERICHOST)) {
DBG_ERR(
--
2.53.0
From 4725da7df3028d37d8bf34b3671c553b7337703b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Mon, 23 Mar 2026 19:03:34 +0100
Subject: [PATCH 15/66] s3:libads: Allow to specify 'dns_lookup_kdc' in
krb5.conf
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15999
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
---
source3/libads/kerberos.c | 23 ++++++++++++++++-------
source3/libads/kerberos_proto.h | 33 +++++++++++++++++++++++++++++----
2 files changed, 45 insertions(+), 11 deletions(-)
diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c
index 5593364c397..865c32d73ca 100644
--- a/source3/libads/kerberos.c
+++ b/source3/libads/kerberos.c
@@ -1362,10 +1362,12 @@ static char *get_enctypes(TALLOC_CTX *mem_ctx)
}
#endif
-bool create_local_private_krb5_conf_for_domain(const char *realm,
- const char *domain,
- const char *sitename,
- const struct sockaddr_storage *pss)
+bool create_local_private_krb5_conf_for_domain_internal(
+ const char *realm,
+ const char *domain,
+ const char *sitename,
+ const struct sockaddr_storage *pss,
+ bool dns_lookup_kdc)
{
char *dname;
char *tmpname = NULL;
@@ -1450,10 +1452,16 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
#endif
/*
- * We are setting 'dns_lookup_kdc' to true, because we want to lookup
- * KDCs which are not configured via DNS SRV records, eg. if we do:
+ * Normally 'dns_lookup_kdc' should be set to true, because we want to
+ * also lookup KDCs via DNS SRV records, e.g. cross domain scenario:
*
* net ads join -Uadmin@otherdomain
+ *
+ * However, during domain join we need to set it to false when we
+ * reconnect using the freshly created machine account credentials.
+ * With dns_lookup_kdc = true, Kerberos may pick a different DC
+ * for the TCP retry (after UDP response is too large), and that DC
+ * might not have replicated the new machine account yet.
*/
file_contents =
talloc_asprintf(fname,
@@ -1467,7 +1475,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
"\tdefault_realm = %s\n"
"%s"
"\tdns_lookup_realm = false\n"
- "\tdns_lookup_kdc = true\n\n"
+ "\tdns_lookup_kdc = %s\n\n"
"[realms]\n\t%s = {\n"
"%s\t}\n"
"\t%s = {\n"
@@ -1476,6 +1484,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
timeout_sec,
realm_upper,
enctypes,
+ dns_lookup_kdc ? "true" : "false",
realm_upper,
kdc_ip_string,
domain,
diff --git a/source3/libads/kerberos_proto.h b/source3/libads/kerberos_proto.h
index a96211c7289..fbeaeff92a9 100644
--- a/source3/libads/kerberos_proto.h
+++ b/source3/libads/kerberos_proto.h
@@ -70,10 +70,35 @@ int ads_kdestroy(const char *cc_name);
int kerberos_kinit_password(const char *principal,
const char *password,
const char *cache_name);
-bool create_local_private_krb5_conf_for_domain(const char *realm,
- const char *domain,
- const char *sitename,
- const struct sockaddr_storage *pss);
+
+bool create_local_private_krb5_conf_for_domain_internal(
+ const char *realm,
+ const char *domain,
+ const char *sitename,
+ const struct sockaddr_storage *pss,
+ bool dns_lookup_kdc);
+
+/* Create krb5.conf that allows DC lookup using DNS. */
+static inline bool create_local_private_krb5_conf_for_domain(
+ const char *realm,
+ const char *domain,
+ const char *sitename,
+ const struct sockaddr_storage *pss)
+{
+ return create_local_private_krb5_conf_for_domain_internal(
+ realm, domain, sitename, pss, true);
+}
+
+/* Create krb5.conf that disables DC lookup using DNS - needed during join. */
+static inline bool create_local_private_krb5_conf_for_domain_join(
+ const char *realm,
+ const char *domain,
+ const char *sitename,
+ const struct sockaddr_storage *pss)
+{
+ return create_local_private_krb5_conf_for_domain_internal(
+ realm, domain, sitename, pss, false);
+}
/* The following definitions come from libads/authdata.c */
--
2.53.0
From 5e1fde5e03c3899b329bd38c36033c385f6fb5f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Mon, 23 Mar 2026 19:05:31 +0100
Subject: [PATCH 16/66] s3:libads: Set dns_lookup_kdc=false during net ads join
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15999
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
Autobuild-User(master): Pavel Filipensky <pfilipensky@samba.org>
Autobuild-Date(master): Tue Apr 7 14:09:40 UTC 2026 on atb-devel-224
---
source3/libnet/libnet_join.c | 60 +++++++++++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c
index 66d682a5b95..81bcb9793a6 100644
--- a/source3/libnet/libnet_join.c
+++ b/source3/libnet/libnet_join.c
@@ -2881,6 +2881,10 @@ WERROR libnet_Join(TALLOC_CTX *mem_ctx,
struct libnet_JoinCtx *r)
{
WERROR werr;
+#ifdef HAVE_ADS
+ struct sockaddr_storage dc_ss = {0};
+ bool dns_lookup_kdc_disabled = false;
+#endif /* HAVE_ADS */
if (r->in.debug) {
LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
@@ -2904,6 +2908,49 @@ WERROR libnet_Join(TALLOC_CTX *mem_ctx,
}
}
+#ifdef HAVE_ADS
+ /*
+ * The machine account was just created on r->in.dc_name,
+ * but might not have replicated to other DCs yet.
+ * Regenerate the krb5.conf with dns_lookup_kdc = false
+ * so that the Kerberos library only talks to the DC
+ * where the account was created. This covers all
+ * subsequent machine-credential operations:
+ * - libnet_join_post_processing_ads_modify() (etype update)
+ * - libnet_join_post_verify() (domain membership verification)
+ */
+ if (r->out.domain_is_ad &&
+ !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE) &&
+ !r->in.request_offline_join)
+ {
+ bool ok;
+ const char *ip = NULL;
+
+ /* dcinfo might not be set for offline joins, however this
+ * check is redundant since we have a guard:
+ * !r->in.request_offline_join
+ */
+ if (r->out.dcinfo) {
+ ip = r->out.dcinfo->dc_address + 2; /* Strip "\\" */
+ }
+
+ if (ip && ip[0] != '\0') {
+ ok = interpret_string_addr(&dc_ss, ip, AI_NUMERICHOST);
+ } else {
+ ok = interpret_string_addr(&dc_ss, r->in.dc_name, 0);
+ }
+
+ if (ok) {
+ create_local_private_krb5_conf_for_domain_join(
+ r->out.dns_domain_name,
+ r->out.netbios_domain_name,
+ NULL, /* sitename */
+ &dc_ss);
+ dns_lookup_kdc_disabled = true;
+ }
+ }
+#endif /* HAVE_ADS */
+
werr = libnet_join_post_processing(mem_ctx, r);
if (!W_ERROR_IS_OK(werr)) {
goto done;
@@ -2931,7 +2978,18 @@ WERROR libnet_Join(TALLOC_CTX *mem_ctx,
}
}
- done:
+done:
+#ifdef HAVE_ADS
+ if (dns_lookup_kdc_disabled) {
+ /* Restore dns_lookup_kdc = true for subsequent operations */
+ create_local_private_krb5_conf_for_domain(
+ r->out.dns_domain_name,
+ r->out.netbios_domain_name,
+ NULL, /* sitename */
+ &dc_ss);
+ }
+#endif /* HAVE_ADS */
+
r->out.result = werr;
if (r->in.debug) {
--
2.53.0
From 42e48d3dd29027573f6e1c04f78f8d9cb91eb4ab Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Fri, 14 Feb 2025 17:07:14 +0100
Subject: [PATCH 17/66] vfs: Add VFS_OPEN_HOW_RESOLVE_NO_XDEV flag
It disallows traversal of mount points during path resolution, including bind
mounts.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/include/vfs.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index e87a0d923e5..15e2186eab7 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -926,6 +926,7 @@ struct vfs_aio_state {
#define VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS 1
#define VFS_OPEN_HOW_WITH_BACKUP_INTENT 2
+#define VFS_OPEN_HOW_RESOLVE_NO_XDEV 4
struct vfs_open_how {
int flags;
--
2.53.0
From 5656562fd43507501aea62b7a43b1d3d5431b313 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Fri, 14 Feb 2025 17:13:39 +0100
Subject: [PATCH 18/66] vfs: Use RESOLVE_NO_XDEV by default on all shares
Enable the flag by default on all shares, it will be automatically
disabled if the system does not support openat2().
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
script/autobuild.py | 2 +-
source3/modules/vfs_default.c | 11 +++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/script/autobuild.py b/script/autobuild.py
index 8acf6cdc854..97d44a07ee4 100755
--- a/script/autobuild.py
+++ b/script/autobuild.py
@@ -338,7 +338,7 @@ tasks = {
"samba-no-opath-build": {
"git-clone-required": True,
"sequence": [
- ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=1 -DDISABLE_PROC_FDS=1' ./configure.developer --without-ad-dc " + samba_configure_params),
+ ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_XDEV=1 -DDISABLE_PROC_FDS=1' ./configure.developer --without-ad-dc " + samba_configure_params),
("make", "make -j"),
("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
("chmod-R-a-w", "chmod -R a-w ."),
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 83c9cc06de8..d531f277199 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -76,6 +76,17 @@ static int vfswrap_connect(vfs_handle_struct *handle, const char *service, const
#ifdef DISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS
handle->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS;
#endif
+ bval = lp_parm_bool(SNUM(handle->conn),
+ "vfs_default",
+ "VFS_OPEN_HOW_RESOLVE_NO_XDEV",
+ true);
+ if (bval) {
+ handle->conn->open_how_resolve |=
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV;
+ }
+#ifdef DISABLE_VFS_OPEN_HOW_RESOLVE_NO_XDEV
+ handle->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV;
+#endif
return 0; /* Return >= 0 for success */
}
--
2.53.0
From 21ce7f160fb6f2bec613be36f2120002205bf84e Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Wed, 8 Oct 2025 10:54:55 +0200
Subject: [PATCH 19/66] selftest/Samba3: nt4_dc* use
vfs_default:VFS_OPEN_HOW_RESOLVE_NO_XDEV=no
From 076c22fbd7ecbf22dbfeb1711609f07fd42f88b0, we should always test the
code path without openat2 being available, even if the kernel supports it.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
selftest/target/Samba3.pm | 1 +
1 file changed, 1 insertion(+)
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 6f17d659d96..3b990bcb349 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -304,6 +304,7 @@ sub setup_nt4_dc
server schannel require seal:torturetest\$ = no
vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS = no
+ vfs_default:VFS_OPEN_HOW_RESOLVE_NO_XDEV = no
fss: sequence timeout = 1
check parent directory delete on close = yes
--
2.53.0
From 0f873b9e694c2db4c54bb480183583d04dde3bdd Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Fri, 14 Feb 2025 17:14:59 +0100
Subject: [PATCH 20/66] vfs: Pass the RESOLVE_NO_XDEV from upper layers to
openat2() syscall
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_default.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index d531f277199..f0270e96002 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -630,7 +630,9 @@ static int vfswrap_openat(vfs_handle_struct *handle,
SMB_ASSERT((dirfd != -1) || (smb_fname->base_name[0] == '/'));
if (how->resolve & ~(VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS |
- VFS_OPEN_HOW_WITH_BACKUP_INTENT)) {
+ VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV))
+ {
errno = ENOSYS;
result = -1;
goto out;
@@ -663,12 +665,20 @@ static int vfswrap_openat(vfs_handle_struct *handle,
}
#endif
- if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS) {
+ if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS ||
+ how->resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV)
+ {
struct open_how linux_how = {
.flags = flags,
.mode = mode,
- .resolve = RESOLVE_NO_SYMLINKS,
+ .resolve = 0,
};
+ if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS) {
+ linux_how.resolve |= RESOLVE_NO_SYMLINKS;
+ }
+ if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) {
+ linux_how.resolve |= RESOLVE_NO_XDEV;
+ }
result = openat2(dirfd,
smb_fname->base_name,
@@ -681,10 +691,13 @@ static int vfswrap_openat(vfs_handle_struct *handle,
* openat2(), so indicate to
* the callers that
* VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS
+ * or VFS_OPEN_HOW_RESOLVE_NO_XDEV
* would just be a waste of time.
*/
fsp->conn->open_how_resolve &=
~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS;
+ fsp->conn->open_how_resolve &=
+ ~VFS_OPEN_HOW_RESOLVE_NO_XDEV;
}
goto out;
}
--
2.53.0
From ff04fbf4e754c6eca9ea87a13d91bb84613ff9f9 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Wed, 8 Oct 2025 13:18:44 +0200
Subject: [PATCH 21/66] smbd: Refactor reopen_from_fsp(), factor out name based
reopen
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/smbd/open.c | 55 +++++++++++++++++++++++++++++++--------------
1 file changed, 38 insertions(+), 17 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index f2cbf7d6bf6..8443ddd32d3 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -847,6 +847,33 @@ static NTSTATUS fd_open_atomic(struct files_struct *dirfsp,
return status;
}
+/*
+ * Close the existing pathref fd and set the fsp flag
+ * is_pathref to false so we get a "normal" fd this time.
+ */
+static NTSTATUS reopen_from_fsp_namebased(struct files_struct *dirfsp,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ const struct vfs_open_how *how,
+ bool *p_file_created)
+{
+ NTSTATUS status;
+
+ status = fd_close(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ fsp->fsp_flags.is_pathref = false;
+
+ status = fd_open_atomic(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
+ return status;
+}
+
NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
struct smb_filename *smb_fname,
struct files_struct *fsp,
@@ -896,7 +923,12 @@ NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
* point we get ENOENT. We
* have to retry pathbased.
*/
- goto namebased_open;
+ return reopen_from_fsp_namebased(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
+
}
/* restore ENOENT if changed in the meantime */
errno = ENOENT;
@@ -916,22 +948,11 @@ NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
return NT_STATUS_OK;
}
-#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H)
-namebased_open:
-#endif
- /*
- * Close the existing pathref fd and set the fsp flag
- * is_pathref to false so we get a "normal" fd this time.
- */
- status = fd_close(fsp);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- fsp->fsp_flags.is_pathref = false;
-
- status = fd_open_atomic(dirfsp, smb_fname, fsp, how, p_file_created);
- return status;
+ return reopen_from_fsp_namebased(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
}
/****************************************************************************
--
2.53.0
From 1f869b2d814930c4c97a9fff461a3ec2327f4503 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Wed, 8 Oct 2025 13:53:14 +0200
Subject: [PATCH 22/66] smbd: Refactor reopen_from_fsp(), factor out
automounter mountpoint check
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/smbd/open.c | 66 +++++++++++++++++++++++++++------------------
1 file changed, 40 insertions(+), 26 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 8443ddd32d3..67bb9774429 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -874,6 +874,30 @@ static NTSTATUS reopen_from_fsp_namebased(struct files_struct *dirfsp,
return status;
}
+static bool fsp_is_automount_mountpoint(struct files_struct *fsp, int old_fd)
+{
+#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H)
+ struct statfs sbuf = {};
+ int ret;
+
+ if (!S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
+ return false;
+ }
+
+ ret = fstatfs(old_fd, &sbuf);
+ if (ret == -1) {
+ DBG_ERR("fstatfs failed: %s\n", strerror(errno));
+ return false;
+ }
+ if (sbuf.f_type == AUTOFS_SUPER_MAGIC) {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif
+}
+
NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
struct smb_filename *smb_fname,
struct files_struct *fsp,
@@ -908,33 +932,23 @@ NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
fsp,
how);
if (new_fd == -1) {
-#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H)
- if (S_ISDIR(fsp->fsp_name->st.st_ex_mode) &&
- (errno == ENOENT)) {
- struct statfs sbuf = {};
- int ret = fstatfs(old_fd, &sbuf);
- if (ret == -1) {
- DBG_ERR("fstatfs failed: %s\n",
- strerror(errno));
- } else if (sbuf.f_type == AUTOFS_SUPER_MAGIC) {
- /*
- * When reopening an as-yet
- * unmounted autofs mount
- * point we get ENOENT. We
- * have to retry pathbased.
- */
- return reopen_from_fsp_namebased(dirfsp,
- smb_fname,
- fsp,
- how,
- p_file_created);
-
- }
- /* restore ENOENT if changed in the meantime */
- errno = ENOENT;
+ int saved_errno = errno;
+ if (saved_errno == ENOENT &&
+ fsp_is_automount_mountpoint(fsp, old_fd))
+ {
+ /*
+ * When reopening an as-yet unmounted autofs
+ * mount point we get ENOENT. We have to retry
+ * pathbased.
+ */
+ return reopen_from_fsp_namebased(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
}
-#endif
- status = map_nt_error_from_unix(errno);
+
+ status = map_nt_error_from_unix(saved_errno);
fd_close(fsp);
return status;
}
--
2.53.0
From d1f0323322b0c70998e901ad79aafcce795646a4 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Wed, 8 Oct 2025 14:17:27 +0200
Subject: [PATCH 23/66] smbd: Refactor reopen_from_fsp(), factor out pathref
based
Best viewed ignoring white space changes
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/smbd/open.c | 117 +++++++++++++++++++++++++-------------------
1 file changed, 66 insertions(+), 51 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 67bb9774429..69c78ff563c 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -898,68 +898,83 @@ static bool fsp_is_automount_mountpoint(struct files_struct *fsp, int old_fd)
#endif
}
-NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
- struct smb_filename *smb_fname,
- struct files_struct *fsp,
- const struct vfs_open_how *how,
- bool *p_file_created)
+static NTSTATUS reopen_from_fsp_pathref_based(
+ struct files_struct *dirfsp,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ const struct vfs_open_how *how,
+ bool *p_file_created)
{
NTSTATUS status;
- int old_fd;
+ struct sys_proc_fd_path_buf buf;
+ int pathref_fd = fsp_get_pathref_fd(fsp);
+ struct smb_filename proc_fname = {
+ .base_name = sys_proc_fd_path(pathref_fd, &buf),
+ };
+ mode_t mode = fsp->fsp_name->st.st_ex_mode;
+ int new_fd;
- if (fsp->fsp_flags.have_proc_fds &&
- ((old_fd = fsp_get_pathref_fd(fsp)) != -1)) {
+ if (S_ISLNK(mode)) {
+ return NT_STATUS_STOPPED_ON_SYMLINK;
+ }
+ if (!(S_ISREG(mode) || S_ISDIR(mode))) {
+ return NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+ }
- struct sys_proc_fd_path_buf buf;
- struct smb_filename proc_fname = {
- .base_name = sys_proc_fd_path(old_fd, &buf),
- };
- mode_t mode = fsp->fsp_name->st.st_ex_mode;
- int new_fd;
+ fsp->fsp_flags.is_pathref = false;
- if (S_ISLNK(mode)) {
- return NT_STATUS_STOPPED_ON_SYMLINK;
- }
- if (!(S_ISREG(mode) || S_ISDIR(mode))) {
- return NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+ new_fd = SMB_VFS_OPENAT(fsp->conn,
+ fsp->conn->cwd_fsp,
+ &proc_fname,
+ fsp,
+ how);
+ if (new_fd == -1) {
+ int saved_errno = errno;
+ if (saved_errno == ENOENT &&
+ fsp_is_automount_mountpoint(fsp, pathref_fd))
+ {
+ /*
+ * When reopening an as-yet unmounted autofs
+ * mount point we get ENOENT. We have to retry
+ * pathbased.
+ */
+ return reopen_from_fsp_namebased(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
}
- fsp->fsp_flags.is_pathref = false;
+ status = map_nt_error_from_unix(saved_errno);
+ fd_close(fsp);
+ return status;
+ }
- new_fd = SMB_VFS_OPENAT(fsp->conn,
- fsp->conn->cwd_fsp,
- &proc_fname,
- fsp,
- how);
- if (new_fd == -1) {
- int saved_errno = errno;
- if (saved_errno == ENOENT &&
- fsp_is_automount_mountpoint(fsp, old_fd))
- {
- /*
- * When reopening an as-yet unmounted autofs
- * mount point we get ENOENT. We have to retry
- * pathbased.
- */
- return reopen_from_fsp_namebased(dirfsp,
- smb_fname,
- fsp,
- how,
- p_file_created);
- }
+ status = fd_close(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
- status = map_nt_error_from_unix(saved_errno);
- fd_close(fsp);
- return status;
- }
+ fsp_set_fd(fsp, new_fd);
+ return NT_STATUS_OK;
+}
- status = fd_close(fsp);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+NTSTATUS reopen_from_fsp(struct files_struct *dirfsp,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ const struct vfs_open_how *how,
+ bool *p_file_created)
+{
+ int old_fd;
- fsp_set_fd(fsp, new_fd);
- return NT_STATUS_OK;
+ if (fsp->fsp_flags.have_proc_fds &&
+ ((old_fd = fsp_get_pathref_fd(fsp)) != -1))
+ {
+ return reopen_from_fsp_pathref_based(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
}
return reopen_from_fsp_namebased(dirfsp,
--
2.53.0
From 55bddf76fe649ca7ba30f0645a199255f2698a70 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Wed, 8 Oct 2025 17:09:22 +0200
Subject: [PATCH 24/66] smbd: Fix crossing direct automounter mount points
The workaround implemented in commit ac7a16f9cc4bd97ef546d1b7b02605991000d0f9
to trigger automounts does not work for direct automounts (either with
systemd-automount or autofs daemon).
In direct automounts the mount point is a real directory instead of a "ghost"
directory so when turning the O_PATH handle into a real one through
/proc/self/fd/<fdnum> openat() does not return ENOENT, it returs a fd referring
to the mount point without triggering the mount.
To trigger the mount first we have to know when we are crossing mount points
by using the RESOLVE_NO_XDEV flag in open_how.resolve, then we can check with
fstatfs() the .f_type and fallback to a path-based open for automounts or
retry without RESOLVE_NO_XDEV otherwise.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/smbd/open.c | 71 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 67 insertions(+), 4 deletions(-)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 69c78ff563c..ea400c41aa8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -913,6 +913,7 @@ static NTSTATUS reopen_from_fsp_pathref_based(
};
mode_t mode = fsp->fsp_name->st.st_ex_mode;
int new_fd;
+ struct vfs_open_how pathref_how = *how;
if (S_ISLNK(mode)) {
return NT_STATUS_STOPPED_ON_SYMLINK;
@@ -923,26 +924,88 @@ static NTSTATUS reopen_from_fsp_pathref_based(
fsp->fsp_flags.is_pathref = false;
+#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H)
+ /*
+ * There is no point in setting RESOLVE_NO_XDEV if we can't
+ * check with fstatfs later in fsp_is_automount_mountpoint
+ */
+ if (S_ISDIR(fsp->fsp_name->st.st_ex_mode) &&
+ fsp->conn->open_how_resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) {
+ /*
+ * If the *at cwd_fsp is a pathref (opened with O_PATH)
+ * and old_fd refers to an automounter mount point not
+ * yet mounted, we will get a fd referring to the
+ * mount point without actually triggering the mount
+ * (man 2 openat). To detect this situation set the
+ * RESOLVE_NO_XDEV flag so openat2 will return an
+ * error when crossing mount points. Then check
+ * with fstatfs if it is an autofs mount point or not,
+ * falling back to name-based openat or retry without
+ * RESOLVE_NO_XDEV otherwise (could be a bind mount,
+ * other type of mount of an automounter mount point
+ * already mounted).
+ */
+ pathref_how.resolve |= VFS_OPEN_HOW_RESOLVE_NO_XDEV;
+ }
+#endif
+
+retry:
new_fd = SMB_VFS_OPENAT(fsp->conn,
fsp->conn->cwd_fsp,
&proc_fname,
fsp,
- how);
+ &pathref_how);
if (new_fd == -1) {
int saved_errno = errno;
if (saved_errno == ENOENT &&
fsp_is_automount_mountpoint(fsp, pathref_fd))
{
/*
- * When reopening an as-yet unmounted autofs
- * mount point we get ENOENT. We have to retry
- * pathbased.
+ * This is a not yet triggered indirect automount
+ * detected by openat(pathref_fd). Retry name-based.
*/
return reopen_from_fsp_namebased(dirfsp,
smb_fname,
fsp,
how,
p_file_created);
+ } else if (saved_errno == EXDEV &&
+ pathref_how.resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV &&
+ fsp_is_automount_mountpoint(fsp, pathref_fd))
+ {
+ /*
+ * This is a not yet triggered direct or indirect
+ * automount, detected by
+ * openat2(pathref_fd, .., RESOLVE_NO_XDEV).
+ * Retry name-based.
+ */
+ return reopen_from_fsp_namebased(dirfsp,
+ smb_fname,
+ fsp,
+ how,
+ p_file_created);
+ } else if (saved_errno == ENOSYS &&
+ pathref_how.resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV)
+ {
+ /*
+ * The kernel doesn't support openat2() yet, or any
+ * VFS module rejected the flag. Notify to the user
+ * and retry without RESOLVE_NO_XDEV.
+ */
+ DBG_WARNING("Failed to open directory disallowing the "
+ "traversal of mount points during path "
+ "resolution. Retrying allowing traversal, "
+ "but automounts won't be triggered.\n");
+ pathref_how.resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV;
+ goto retry;
+ } else if (saved_errno == EXDEV &&
+ pathref_how.resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV)
+ {
+ /*
+ * Just crossing a mount. Retry allowing traversals.
+ */
+ pathref_how.resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV;
+ goto retry;
}
status = map_nt_error_from_unix(saved_errno);
--
2.53.0
From 4872b32c28d540618462ee186e91716c9ee3807c Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Fri, 2 May 2025 11:57:30 +0200
Subject: [PATCH 25/66] vfs:aio_pthread: Handle VFS_OPEN_HOW_RESOLVE_NO_XDEV
flag
This module uses openat() instead of openat2() so the flag won't be used and
automounts might not be triggered.
Disable flag usage for subsequent opens and return an error to callers to warn
the user and retry without the flag.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_aio_pthread.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_aio_pthread.c b/source3/modules/vfs_aio_pthread.c
index bd0c94b8cce..afbaaedf7b5 100644
--- a/source3/modules/vfs_aio_pthread.c
+++ b/source3/modules/vfs_aio_pthread.c
@@ -457,7 +457,9 @@ static int aio_pthread_openat_fn(vfs_handle_struct *handle,
bool aio_allow_open = lp_parm_bool(
SNUM(handle->conn), "aio_pthread", "aio open", false);
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
errno = ENOSYS;
return -1;
}
@@ -498,6 +500,16 @@ static int aio_pthread_openat_fn(vfs_handle_struct *handle,
aio_allow_open = false;
}
+ if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) {
+ /*
+ * RESOLVE_NO_XDEV needs openat2(). Disallow further usage of
+ * this flag and return ENOSYS to force a retry.
+ */
+ fsp->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV;
+ errno = ENOSYS;
+ return -1;
+ }
+
if (!aio_allow_open) {
/* aio opens turned off. */
return SMB_VFS_NEXT_OPENAT(handle,
--
2.53.0
From e11fa05e10b1a5fadd7c73c87dcb68b048c73878 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Fri, 2 May 2025 12:11:01 +0200
Subject: [PATCH 26/66] vfs:ceph: Allow VFS_OPEN_HOW_RESOLVE_NO_XDEV flag
Don't return ENOSYS if the flag is set. It will be ignored,
does not make sense in a ceph virtual filesystem.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_ceph.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index 8ea7eb09099..4add6cf993e 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -472,7 +472,9 @@ static int cephwrap_openat(struct vfs_handle_struct *handle,
int result = -ENOENT;
int dirfd = -1;
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
errno = ENOSYS;
return -1;
}
--
2.53.0
From 4cd5f6c4c39ac172e3e3e9e2c11fa9be06f2ed38 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Tue, 30 Sep 2025 10:32:36 +0200
Subject: [PATCH 27/66] vfs:ceph_new: Allow VFS_OPEN_HOW_RESOLVE_NO_XDEV flag
Don't return ENOSYS if the flag is set. It will be ignored,
does not make sense in a ceph filesystem.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_ceph_new.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_ceph_new.c b/source3/modules/vfs_ceph_new.c
index 6ea8e56c155..37cff6fe051 100644
--- a/source3/modules/vfs_ceph_new.c
+++ b/source3/modules/vfs_ceph_new.c
@@ -2325,7 +2325,9 @@ static int vfs_ceph_openat(struct vfs_handle_struct *handle,
int result = -ENOENT;
START_PROFILE_X(SNUM(handle->conn), syscall_openat);
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
result = -ENOSYS;
goto err_out;
}
--
2.53.0
From 8f4ad32afca03b564d00a5a6795d4b4a50ae8c66 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Thu, 9 Oct 2025 12:30:17 +0200
Subject: [PATCH 28/66] vfs:glusterfs: Allow VFS_OPEN_HOW_RESOLVE_NO_XDEV
Don't return ENOSYS if the flag is set. It will be ignored as does not make
sense in this module.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_glusterfs.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c
index 63dc7a30b04..4d7d96f2888 100644
--- a/source3/modules/vfs_glusterfs.c
+++ b/source3/modules/vfs_glusterfs.c
@@ -731,7 +731,9 @@ static int vfs_gluster_openat(struct vfs_handle_struct *handle,
START_PROFILE(syscall_openat);
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
END_PROFILE(syscall_openat);
errno = ENOSYS;
return -1;
--
2.53.0
From b41558ebe158f413d2cfa71478c3eeb5e1a201ec Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@suse.de>
Date: Fri, 2 May 2025 13:21:52 +0200
Subject: [PATCH 29/66] vfs:shadow_copy2: Allow RESOLVE_NO_XDEV flag
This module updates the path and calls the next VFS module.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_shadow_copy2.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
index 449d08e8830..465a0f9d28e 100644
--- a/source3/modules/vfs_shadow_copy2.c
+++ b/source3/modules/vfs_shadow_copy2.c
@@ -1563,7 +1563,9 @@ static int shadow_copy2_openat(vfs_handle_struct *handle,
int ret;
bool ok;
- if ((how.resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how.resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
errno = ENOSYS;
return -1;
}
--
2.53.0
From 49ac956d858b455291edd1330a8d428209728ea0 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Thu, 9 Oct 2025 12:52:11 +0200
Subject: [PATCH 30/66] vfs:streams_depot: Allow VFS_OPEN_HOW_RESOLVE_NO_XDEV
flag
The flag is passed down the modules stack.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_streams_depot.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c
index 19b9356fd57..bcf5acf79b4 100644
--- a/source3/modules/vfs_streams_depot.c
+++ b/source3/modules/vfs_streams_depot.c
@@ -729,7 +729,9 @@ static int streams_depot_openat(struct vfs_handle_struct *handle,
handle, dirfsp, smb_fname, fsp, how);
}
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
errno = ENOSYS;
return -1;
}
--
2.53.0
From 7b875e245ec205ee083c5c4158933e0e42e8a064 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Thu, 9 Oct 2025 12:59:59 +0200
Subject: [PATCH 31/66] vfs:fruit: Allow RESOLVE_NO_XDEV flag
For stream opens, it returns a fake fd. The streams will be stored by
vfs_streams_depot or vfs_streams_xattr.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
---
source3/modules/vfs_fruit.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 4da7c1efa07..812e3a351d2 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1787,7 +1787,9 @@ static int fruit_openat(vfs_handle_struct *handle,
return fd;
}
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
errno = ENOSYS;
return -1;
}
--
2.53.0
From 97fb984b6c5c5fe1c562cd23980ea8e110438577 Mon Sep 17 00:00:00 2001
From: Samuel Cabrero <scabrero@samba.org>
Date: Thu, 9 Oct 2025 13:05:16 +0200
Subject: [PATCH 32/66] vfs:streams_xattr: Allow VFS_OPEN_HOW_RESOLVE_NO_XDEV
The open function returns a fake fd. Extended attributes will be stored by
vfs_xattr_tdb or vfs_default.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
Autobuild-User(master): Samuel Cabrero <scabrero@samba.org>
Autobuild-Date(master): Tue Nov 18 09:08:38 UTC 2025 on atb-devel-224
---
source3/modules/vfs_streams_xattr.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 58e5b1eb189..410cd793cd9 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -416,7 +416,9 @@ static int streams_xattr_openat(struct vfs_handle_struct *handle,
how);
}
- if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) {
+ if ((how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT |
+ VFS_OPEN_HOW_RESOLVE_NO_XDEV)) != 0)
+ {
errno = ENOSYS;
return -1;
}
--
2.53.0
From 23aa86c38e049eb0f75bdd21d18c670abf2c6134 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
Date: Tue, 7 Apr 2026 16:28:05 +0200
Subject: [PATCH 33/66] smbdotconf: Add "automount fs types" to smb.conf
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This adds a new global parameter "automount fs types" that allows
administrators to configure additional filesystem types that should
trigger automounting, beyond the always-supported autofs filesystem.
To enable 'samba unaware FS' automounting, add:
automount fs types = 0x12345678
This allows e.g. ZFS snapshots in <dataset root>/.zfs/snapshot to be
mounted. To find out the magic number that is not listed
in /usr/include/linux/magic.h, run:
stat -f -c '0x%t' /path/to/mountpoint
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15991
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
Reviewed-by: Samuel Cabrero <scabrero@samba.org>
Autobuild-User(master): Pavel Filipensky <pfilipensky@samba.org>
Autobuild-Date(master): Mon Apr 20 19:57:42 UTC 2026 on atb-devel-224
---
docs-xml/smbdotconf/misc/automountfstypes.xml | 24 ++++++++++++++
source3/smbd/open.c | 31 +++++++++++++++++++
2 files changed, 55 insertions(+)
create mode 100644 docs-xml/smbdotconf/misc/automountfstypes.xml
diff --git a/docs-xml/smbdotconf/misc/automountfstypes.xml b/docs-xml/smbdotconf/misc/automountfstypes.xml
new file mode 100644
index 00000000000..4c5bc510520
--- /dev/null
+++ b/docs-xml/smbdotconf/misc/automountfstypes.xml
@@ -0,0 +1,24 @@
+<samba:parameter name="automount fs types"
+ context="G"
+ type="cmdlist"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>This parameter specifies a list of additional filesystem magic numbers
+ that should trigger automounting when accessed.</para>
+
+ <para>The values should be specified as hexadecimal numbers (with or without
+ 0x prefix), separated by spaces or commas.</para>
+
+ <para>Note: This parameter is only available on Linux systems.</para>
+
+ <para>To find the filesystem magic number for a mounted filesystem,
+ consult /usr/include/linux/magic.h or call:
+ <command>stat -f -c '0x%t' /path/to/mountpoint</command></para>
+
+ <para>Note: autofs (0x187) is always checked and does not need to be included
+ in this list.</para>
+</description>
+
+<value type="default"></value>
+<value type="example">0xA0B0C0D0 0x12345678</value>
+</samba:parameter>
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index ea400c41aa8..ae1ce208cba 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -42,6 +42,7 @@
#include "locking/leases_db.h"
#include "librpc/gen_ndr/ndr_leases_db.h"
#include "lib/util/time_basic.h"
+#include "lib/util/smb_strtox.h"
#include "source3/smbd/dir.h"
#if defined(HAVE_LINUX_MAGIC_H)
@@ -879,6 +880,8 @@ static bool fsp_is_automount_mountpoint(struct files_struct *fsp, int old_fd)
#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H)
struct statfs sbuf = {};
int ret;
+ const char **fs_types_list = NULL;
+ int i;
if (!S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
return false;
@@ -892,6 +895,34 @@ static bool fsp_is_automount_mountpoint(struct files_struct *fsp, int old_fd)
if (sbuf.f_type == AUTOFS_SUPER_MAGIC) {
return true;
}
+
+ /* Check for additional filesystem types from configuration */
+ fs_types_list = lp_automount_fs_types();
+ if (fs_types_list == NULL) {
+ return false;
+ }
+
+ for (i = 0; fs_types_list[i] != NULL; i++) {
+ unsigned long long fs_type_val;
+ int error = 0;
+
+ fs_type_val = smb_strtoull(fs_types_list[i],
+ NULL,
+ 0,
+ &error,
+ SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ DBG_WARNING(
+ "Invalid value in 'automount fs types': %s\n",
+ fs_types_list[i]);
+ continue;
+ }
+
+ if (sbuf.f_type == fs_type_val) {
+ return true;
+ }
+ }
+
return false;
#else
return false;
--
2.53.0
From 1405b4a8f4772d603083535d6db153c9189ae18c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Thu, 5 Feb 2026 20:24:12 +0100
Subject: [PATCH 34/66] CVE-2026-1933 tests: Fix permissions used for creating
reparse points
SEC_STD_ALL does not lead to fsp->access_mask to include the required
bits.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=15992
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
---
python/samba/tests/smb3unix.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/samba/tests/smb3unix.py b/python/samba/tests/smb3unix.py
index 075b2a07b17..3039a68a1cd 100644
--- a/python/samba/tests/smb3unix.py
+++ b/python/samba/tests/smb3unix.py
@@ -446,7 +446,7 @@ class Smb3UnixTests(samba.tests.libsmb.LibsmbTests):
wire_mode = libsmb.unix_mode_to_wire(0o600)
f,_,cc_out = c.create_ex('\\reparse',
- DesiredAccess=security.SEC_STD_ALL,
+ DesiredAccess=security.SEC_FILE_WRITE_ATTRIBUTE,
CreateDisposition=libsmb.FILE_CREATE,
CreateContexts=[posix_context(wire_mode)])
@@ -460,7 +460,7 @@ class Smb3UnixTests(samba.tests.libsmb.LibsmbTests):
wire_mode = libsmb.unix_mode_to_wire(0o600)
f,_,cc_out = c.create_ex('\\reparse',
- DesiredAccess=security.SEC_STD_ALL,
+ DesiredAccess=security.SEC_FILE_WRITE_ATTRIBUTE,
CreateDisposition=libsmb.FILE_OPEN,
CreateContexts=[posix_context(wire_mode)])
c.close(f)
--
2.53.0
From 6af47933d060e03ab1cceff2652658c2dd230872 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Mon, 2 Feb 2026 11:43:37 +0100
Subject: [PATCH 35/66] CVE-2026-1933 smbd: Add access checks to reparse point
operations
On a share marked "read only = yes" and on file handles opened R/O
users can set or delete the reparse point xattrs on files that the
user has write-access in the file system for. Add the required access
checks.
Thanks to Asim Viladi Oglu Manizada for reporting the issue.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=15992
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
---
source3/modules/util_reparse.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/source3/modules/util_reparse.c b/source3/modules/util_reparse.c
index 60373d7fd4e..75aa745e070 100644
--- a/source3/modules/util_reparse.c
+++ b/source3/modules/util_reparse.c
@@ -320,6 +320,14 @@ NTSTATUS fsctl_set_reparse_point(struct files_struct *fsp,
return NT_STATUS_ACCESS_DENIED;
}
+ if ((fsp->fsp_name->twrp != 0) ||
+ ((fsp->access_mask &
+ (SEC_FILE_WRITE_DATA | SEC_FILE_WRITE_ATTRIBUTE)) == 0))
+ {
+ DBG_DEBUG("Access denied on a readonly handle\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
status = reparse_buffer_check(in_data,
in_len,
&reparse_tag,
@@ -390,6 +398,14 @@ NTSTATUS fsctl_del_reparse_point(struct files_struct *fsp,
uint32_t dos_mode;
int ret;
+ if ((fsp->fsp_name->twrp != 0) ||
+ ((fsp->access_mask &
+ (SEC_FILE_WRITE_DATA | SEC_FILE_WRITE_ATTRIBUTE)) == 0))
+ {
+ DBG_DEBUG("Access denied on a readonly handle\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
status = fsctl_get_reparse_tag(fsp, &existing_tag);
if (!NT_STATUS_IS_OK(status)) {
return status;
--
2.53.0
From aed5ff8b967502a40eefe01f85e292c622931551 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Thu, 19 Feb 2026 12:50:38 +1300
Subject: [PATCH 36/66] CVE-2026-2340: test whether vfs_worm allows overwrite
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15997
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Volker Lendecke <vl@samba.org>
---
selftest/knownfail.d/vfs-worm | 2 ++
source3/script/tests/test_worm.sh | 30 ++++++++++++++++++++++++++++++
2 files changed, 32 insertions(+)
create mode 100644 selftest/knownfail.d/vfs-worm
diff --git a/selftest/knownfail.d/vfs-worm b/selftest/knownfail.d/vfs-worm
new file mode 100644
index 00000000000..f4a330c744b
--- /dev/null
+++ b/selftest/knownfail.d/vfs-worm
@@ -0,0 +1,2 @@
+^samba3.blackbox.worm.SMB3
+^samba3.blackbox.worm.NT1
diff --git a/source3/script/tests/test_worm.sh b/source3/script/tests/test_worm.sh
index f96c8ec7e47..d38488cb790 100755
--- a/source3/script/tests/test_worm.sh
+++ b/source3/script/tests/test_worm.sh
@@ -40,6 +40,7 @@ do_cleanup()
#subshell.
cd "$share_test_dir" || return
rm -f must-be-deleted must-not-be-deleted must-be-deleted-after-ctime-refresh
+ rm -f must-not-be-overwritten sentinel-value
)
rm -f $tmpfile
}
@@ -51,6 +52,10 @@ do_cleanup
tmpfile=$PREFIX/smbclient_interactive_prompt_commands
+tmp_sentinel=$PREFIX/sentinel_value
+SENTINEL_VALUE='1'
+echo $SENTINEL_VALUE > $tmp_sentinel
+
test_worm()
{
# use echo because helo scripts don't support variables
@@ -58,6 +63,7 @@ test_worm()
put $tmpfile must-be-deleted
put $tmpfile must-be-deleted-after-ctime-refresh
put $tmpfile must-not-be-deleted
+put $tmpfile must-not-be-overwritten
del must-be-deleted
quit" > $tmpfile
# make sure the directory is not too old for worm:
@@ -97,6 +103,30 @@ quit" > $tmpfile
printf "$0: ERROR: must-not-be-deleted WAS deleted\n"
return 1
}
+
+ # Check we can't change a protected file by renaming over it.
+ # The source file needs to recently created or access will be
+ # denied before RENAME_AT is reached, which is the thing we
+ # want to test.
+ original_contents=`cat $share_test_dir/must-not-be-overwritten`
+ echo "
+put $tmp_sentinel sentinel-value
+rename sentinel-value must-not-be-overwritten -f
+quit" > $tmpfile
+ cmd='CLI_FORCE_INTERACTIVE=yes $SMBCLIENT -U$USERNAME%$PASSWORD //$SERVER/worm -I$SERVER_IP $ADDARGS < $tmpfile 2>&1'
+ eval echo "$cmd"
+ out=$(eval "$cmd")
+ new_contents=`cat $share_test_dir/must-not-be-overwritten`
+
+ if [ "$new_contents" = "$SENTINEL_VALUE" ]; then
+ echo "must-not-be-overwritten was overwritten"
+ return 1
+ fi
+ if [ "$new_contents" != "$original_contents" ]; then
+ echo "must-not-be-overwritten was changed (but not precisely overwritten)"
+ return 1
+ fi
+
# if we're not root, return here:
test "$UID" = "0" || {
return 0
--
2.53.0
From c8e94fe47601143a86d7b9362313e77d4d19fd2d Mon Sep 17 00:00:00 2001
From: Pavel Kohout <pavel@aisle.com>
Date: Fri, 13 Feb 2026 15:51:41 +1300
Subject: [PATCH 37/66] CVE-2026-2340: vfs_worm: Check destination WORM status
in rename
vfs_worm_renameat() only checked if the source file was WORM-protected,
but not the destination. This allowed overwriting immutable files via
SMB2 rename with ReplaceIfExists=1, bypassing WORM protection.
Add destination check using FSTATAT on the destination dirfsp, as
suggested by the maintainer.
CWE-284 (Improper Access Control)
Reported-by: Pavel Kohout, Aisle Research, www.aisle.com
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15997
To backport to 4.23 we change the name of dst_dirfsp and src_dirfsp to
dstfsp and srcfsp, respectively (accounting for
76796180cf3af3252db2c29d0e95282a498a8527 in 4.24/master).
Signed-off-by: Pavel Kohout <pavel.kohout@aisle.com>
Reviewed-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
selftest/knownfail.d/vfs-worm | 2 --
source3/modules/vfs_worm.c | 26 ++++++++++++++++++++++++--
2 files changed, 24 insertions(+), 4 deletions(-)
delete mode 100644 selftest/knownfail.d/vfs-worm
diff --git a/selftest/knownfail.d/vfs-worm b/selftest/knownfail.d/vfs-worm
deleted file mode 100644
index f4a330c744b..00000000000
--- a/selftest/knownfail.d/vfs-worm
+++ /dev/null
@@ -1,2 +0,0 @@
-^samba3.blackbox.worm.SMB3
-^samba3.blackbox.worm.NT1
diff --git a/source3/modules/vfs_worm.c b/source3/modules/vfs_worm.c
index 0fcda162cd7..a1dca280279 100644
--- a/source3/modules/vfs_worm.c
+++ b/source3/modules/vfs_worm.c
@@ -218,13 +218,35 @@ static int vfs_worm_renameat(vfs_handle_struct *handle,
const struct smb_filename *smb_fname_dst,
const struct vfs_rename_how *how)
{
+ struct stat_ex dst_st;
+ int ret;
+
if (is_readonly(handle, smb_fname_src)) {
errno = EACCES;
return -1;
}
- return SMB_VFS_NEXT_RENAMEAT(
- handle, srcfsp, smb_fname_src, dstfsp, smb_fname_dst, how);
+ /* Check if destination is WORM-protected (fixes CVE-2026-2340) */
+ ret = SMB_VFS_FSTATAT(handle->conn,
+ dstfsp,
+ smb_fname_dst,
+ &dst_st,
+ AT_SYMLINK_NOFOLLOW);
+ if (ret == 0) {
+ struct smb_filename dst_with_stat = *smb_fname_dst;
+ dst_with_stat.st = dst_st;
+ if (is_readonly(handle, &dst_with_stat)) {
+ errno = EACCES;
+ return -1;
+ }
+ }
+
+ return SMB_VFS_NEXT_RENAMEAT(handle,
+ srcfsp,
+ smb_fname_src,
+ dstfsp,
+ smb_fname_dst,
+ how);
}
static int vfs_worm_fsetxattr(struct vfs_handle_struct *handle,
--
2.53.0
From 1af2b41cd7ae0d1b65f6ba44a29a2500a0177d24 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Fri, 27 Feb 2026 11:30:40 +1300
Subject: [PATCH 38/66] CVE-2026-3012: gpo tests: fix test cleanup
These tests are going to fail soon but as currently written they do
not clean up after themselves, erroring instead of failing and causing
cascading errors in subsequent tests. For now we don't care to make
the other tests less fragile.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
---
python/samba/tests/gpo.py | 42 +++++++++++++++++++++++----------------
1 file changed, 25 insertions(+), 17 deletions(-)
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
index 2e4696cd926..0972cd2f63c 100644
--- a/python/samba/tests/gpo.py
+++ b/python/samba/tests/gpo.py
@@ -6951,6 +6951,7 @@ class GPOTests(tests.TestCase):
confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
ca_cn = '%s-CA' % hostname.replace('.', '-')
certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
+ self.addCleanup(ldb.delete, certa_dn)
ldb.add({'dn': certa_dn,
'objectClass': 'certificationAuthority',
'authorityRevocationList': ['XXX'],
@@ -6959,6 +6960,7 @@ class GPOTests(tests.TestCase):
})
# Write the dummy pKIEnrollmentService
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
+ self.addCleanup(ldb.delete, enroll_dn)
ldb.add({'dn': enroll_dn,
'objectClass': 'pKIEnrollmentService',
'cACertificate': dummy_certificate(),
@@ -6967,6 +6969,7 @@ class GPOTests(tests.TestCase):
})
# Write the dummy pKICertificateTemplate
template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
+ self.addCleanup(ldb.delete, template_dn)
ldb.add({'dn': template_dn,
'objectClass': 'pKICertificateTemplate',
})
@@ -7012,11 +7015,6 @@ class GPOTests(tests.TestCase):
self.assertNotIn(b'Workstation', out,
'Workstation certificate not removed')
- # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
- ldb.delete(certa_dn)
- ldb.delete(enroll_dn)
- ldb.delete(template_dn)
-
# Unstage the Registry.pol file
unstage_file(reg_pol)
@@ -7027,6 +7025,7 @@ class GPOTests(tests.TestCase):
'MACHINE/REGISTRY.POL')
cache_dir = self.lp.get('cache directory')
store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
+ self.addCleanup(store.log.close)
machine_creds = Credentials()
machine_creds.guess(self.lp)
@@ -7059,6 +7058,7 @@ class GPOTests(tests.TestCase):
confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
ca_cn = '%s-CA' % hostname.replace('.', '-')
certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
+ self.addCleanup(ldb.delete, certa_dn)
ldb.add({'dn': certa_dn,
'objectClass': 'certificationAuthority',
'authorityRevocationList': ['XXX'],
@@ -7067,6 +7067,7 @@ class GPOTests(tests.TestCase):
})
# Write the dummy pKIEnrollmentService
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
+ self.addCleanup(ldb.delete, enroll_dn)
ldb.add({'dn': enroll_dn,
'objectClass': 'pKIEnrollmentService',
'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
@@ -7075,12 +7076,16 @@ class GPOTests(tests.TestCase):
})
# Write the dummy pKICertificateTemplate
template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
+ self.addCleanup(ldb.delete, template_dn)
ldb.add({'dn': template_dn,
'objectClass': 'pKICertificateTemplate',
})
with TemporaryDirectory() as dname:
- ext.process_group_policy([], gpos, dname, dname)
+ try:
+ ext.process_group_policy([], gpos, dname, dname)
+ except Exception as e:
+ self.fail(f"process_group_policy() raised {e}")
ca_crt = os.path.join(dname, '%s.crt' % ca_cn)
self.assertTrue(os.path.exists(ca_crt),
'Root CA certificate was not requested')
@@ -7169,11 +7174,6 @@ class GPOTests(tests.TestCase):
self.assertNotIn(b'Workstation', out,
'Workstation certificate not removed')
- # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
- ldb.delete(certa_dn)
- ldb.delete(enroll_dn)
- ldb.delete(template_dn)
-
# Unstage the Registry.pol file
unstage_file(reg_pol)
@@ -7626,6 +7626,7 @@ class GPOTests(tests.TestCase):
'MACHINE/REGISTRY.POL')
cache_dir = self.lp.get('cache directory')
store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
+ self.addCleanup(store.log.close)
machine_creds = Credentials()
machine_creds.guess(self.lp)
@@ -7667,6 +7668,8 @@ class GPOTests(tests.TestCase):
confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
ca_cn = '%s-CA' % hostname.replace('.', '-')
certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
+ self.addCleanup(ldb.delete, certa_dn)
+
ldb.add({'dn': certa_dn,
'objectClass': 'certificationAuthority',
'authorityRevocationList': ['XXX'],
@@ -7675,6 +7678,7 @@ class GPOTests(tests.TestCase):
})
# Write the dummy pKIEnrollmentService
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
+ self.addCleanup(ldb.delete, enroll_dn)
ldb.add({'dn': enroll_dn,
'objectClass': 'pKIEnrollmentService',
'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
@@ -7683,12 +7687,21 @@ class GPOTests(tests.TestCase):
})
# Write the dummy pKICertificateTemplate
template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
+ try:
+ ldb.delete(template_dn)
+ except _ldb.LdbError:
+ pass
+
+ self.addCleanup(ldb.delete, template_dn)
ldb.add({'dn': template_dn,
'objectClass': 'pKICertificateTemplate',
})
with TemporaryDirectory() as dname:
- ext.process_group_policy([], gpos, dname, dname)
+ try:
+ ext.process_group_policy([], gpos, dname, dname)
+ except Exception as e:
+ self.fail(f"process_group_policy() raised {e}")
ca_list = [ca_cn, 'example0-com-CA', 'example1-com-CA',
'example2-com-CA']
for ca in ca_list:
@@ -7751,11 +7764,6 @@ class GPOTests(tests.TestCase):
self.assertNotIn(b'Workstation', out,
'Workstation certificate not removed')
- # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
- ldb.delete(certa_dn)
- ldb.delete(enroll_dn)
- ldb.delete(template_dn)
-
# Unstage the Registry.pol file
unstage_file(reg_pol)
--
2.53.0
From eb449819e1acaa88249f398e15a19ede9adf49b9 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Mon, 23 Feb 2026 11:01:57 +1300
Subject: [PATCH 39/66] CVE-2026-3012: do not fetch certificate over http
In the case where a certificate was found via HTTP, it was trusted
without verification and put in the global CA store.
There is no means to check the certificate other than by comparing it
to certificates we may have gathered via LDAP, but in that case there
is no advantage over just using the LDAP-derived certificates.
Using the LDAP certificates was already the fallback case if HTTP
failed, so we just make it the default.
The HTTP fetch depends on the NDES service, which is a variant of
Simple Certificate Enrolment Protocol (SCEP, RFC8894), but in fact
Samba implements none of that protocol other than the HTTP fetch. SCEP
is for clients that are not true domain members. Domain members can
access to certificates over LDAP. This patch is not reducing SCEP
client support because Samba never had it.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
Reported-by: Arad Inbar, DREAM Security Research Team
Reported-by: Nir Somech, DREAM Security Research Team
Reported-by: Ben Grinberg, DREAM Security Research Team
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
---
python/samba/gp/gp_cert_auto_enroll_ext.py | 54 ++++------------------
selftest/knownfail.d/gpo-auto-enrol | 2 +
2 files changed, 11 insertions(+), 45 deletions(-)
create mode 100644 selftest/knownfail.d/gpo-auto-enrol
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
index 877659b043e..815436e11e9 100644
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
@@ -16,7 +16,6 @@
import os
import operator
-import requests
from samba.gp.gpclass import gp_pol_ext, gp_applier, GPOSTATE
from samba import Ldb
from samba.dcerpc import misc
@@ -195,58 +194,24 @@ def get_supported_templates(server):
return out.strip().split()
-def getca(ca, url, trust_dir):
- """Fetch Certificate Chain from the CA."""
+def getca(ca, trust_dir):
+ """Fetch a certificate from LDAP."""
root_cert = os.path.join(trust_dir, '%s.crt' % ca['name'])
root_certs = []
-
- try:
- r = requests.get(url=url, params={'operation': 'GetCACert',
- 'message': 'CAIdentifier'})
- except requests.exceptions.ConnectionError:
- log.warn('Could not connect to Network Device Enrollment Service.')
- r = None
- if r is None or r.content == b'' or r.headers['Content-Type'] == 'text/html':
- log.warn('Unable to fetch root certificates (requires NDES).')
- if 'cACertificate' in ca:
- log.warn('Installing the server certificate only.')
- der_certificate = base64.b64decode(ca['cACertificate'])
- try:
- cert = load_der_x509_certificate(der_certificate)
- except TypeError:
- cert = load_der_x509_certificate(der_certificate,
- default_backend())
- cert_data = cert.public_bytes(Encoding.PEM)
- with open(root_cert, 'wb') as w:
- w.write(cert_data)
- root_certs.append(root_cert)
- return root_certs
-
- if r.headers['Content-Type'] == 'application/x-x509-ca-cert':
- # Older versions of load_der_x509_certificate require a backend param
+ if 'cACertificate' in ca:
+ log.warn('Installing the server certificate only.')
+ der_certificate = base64.b64decode(ca['cACertificate'])
try:
- cert = load_der_x509_certificate(r.content)
+ cert = load_der_x509_certificate(der_certificate)
except TypeError:
- cert = load_der_x509_certificate(r.content, default_backend())
+ cert = load_der_x509_certificate(der_certificate,
+ default_backend())
cert_data = cert.public_bytes(Encoding.PEM)
with open(root_cert, 'wb') as w:
w.write(cert_data)
root_certs.append(root_cert)
- elif r.headers['Content-Type'] == 'application/x-x509-ca-ra-cert':
- certs = load_der_pkcs7_certificates(r.content)
- for i in range(0, len(certs)):
- cert = certs[i].public_bytes(Encoding.PEM)
- filename, extension = root_cert.rsplit('.', 1)
- dest = '%s.%d.%s' % (filename, i, extension)
- with open(dest, 'wb') as w:
- w.write(cert)
- root_certs.append(dest)
- else:
- log.warn('getca: Wrong (or missing) MIME content type')
-
return root_certs
-
def find_global_trust_dir():
"""Return the global trust dir using known paths from various Linux distros."""
for trust_dir in global_trust_dirs:
@@ -266,11 +231,10 @@ def changed(new_data, old_data):
def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
"""Install the root certificate chain."""
data = dict({'files': [], 'templates': []}, **ca)
- url = 'http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?' % ca['hostname']
log.info("Try to get root or server certificates")
- root_certs = getca(ca, url, trust_dir)
+ root_certs = getca(ca, trust_dir)
data['files'].extend(root_certs)
global_trust_dir = find_global_trust_dir()
for src in root_certs:
diff --git a/selftest/knownfail.d/gpo-auto-enrol b/selftest/knownfail.d/gpo-auto-enrol
new file mode 100644
index 00000000000..4bf4b8e3c72
--- /dev/null
+++ b/selftest/knownfail.d/gpo-auto-enrol
@@ -0,0 +1,2 @@
+^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
+^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)
--
2.53.0
From a8fc1666366716af45336f32af48826cefd5435e Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Thu, 26 Feb 2026 14:21:01 +1300
Subject: [PATCH 40/66] CVE-2026-3012: gp_auto_enrol: skip CAs not found in
LDAP
If a certificate is mentioned in a GPO but is not present as a
cACertificate attribute on a pKIEnrollmentService object, we have no way
of obtaining it, so we might as well forget it.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
---
python/samba/gp/gp_cert_auto_enroll_ext.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
index 815436e11e9..de8b310afd9 100644
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
@@ -452,11 +452,21 @@ class gp_cert_auto_enroll_ext(gp_pol_ext, gp_applier):
# This is a basic configuration.
cas = fetch_certification_authorities(ldb)
for _ca in cas:
+ if 'cACertificate' not in _ca:
+ log.warning(f"ignoring CA '{_ca['name']}' with no "
+ "cACertificate in LDAP.")
+ continue
+
self.apply(guid, _ca, cert_enroll, _ca, ldb, trust_dir,
private_dir)
ca_names.append(_ca['name'])
# If EndPoint.URI starts with "HTTPS//":
elif ca['URL'].lower().startswith('https://'):
+ if 'cACertificate' not in ca:
+ log.warning(f"ignoring CA '{ca['name']}' "
+ f"({ca['URL']}) with no "
+ "cACertificate in LDAP.")
+ continue
self.apply(guid, ca, cert_enroll, ca, ldb, trust_dir,
private_dir, auth=ca['auth'])
ca_names.append(ca['name'])
--
2.53.0
From cd0a831279bbe890b64898f5a1892bf8c73cfca7 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Fri, 27 Feb 2026 14:46:04 +1300
Subject: [PATCH 41/66] CVE-2026-3012: gpo tests should use real certificates
Or at least, more real than a short arbitrary byte string, so that
the certificates can be parsed.
This shows that certificate enrolment works via LDAP in the situations
where we would have fetched them via HTTP.
This does not fix the advanced_gp_cert_auto_enroll_ext test which
wants to install certificates it has no access too. This will not be
fixed in the security release.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
---
python/samba/tests/gpo.py | 8 ++++----
selftest/knownfail.d/gpo-auto-enrol | 1 -
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
index 0972cd2f63c..5bdee29b50a 100644
--- a/python/samba/tests/gpo.py
+++ b/python/samba/tests/gpo.py
@@ -7062,7 +7062,7 @@ class GPOTests(tests.TestCase):
ldb.add({'dn': certa_dn,
'objectClass': 'certificationAuthority',
'authorityRevocationList': ['XXX'],
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
+ 'cACertificate': dummy_certificate(),
'certificateRevocationList': ['XXX'],
})
# Write the dummy pKIEnrollmentService
@@ -7070,7 +7070,7 @@ class GPOTests(tests.TestCase):
self.addCleanup(ldb.delete, enroll_dn)
ldb.add({'dn': enroll_dn,
'objectClass': 'pKIEnrollmentService',
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
+ 'cACertificate': dummy_certificate(),
'certificateTemplates': ['Machine'],
'dNSHostName': hostname,
})
@@ -7673,7 +7673,7 @@ class GPOTests(tests.TestCase):
ldb.add({'dn': certa_dn,
'objectClass': 'certificationAuthority',
'authorityRevocationList': ['XXX'],
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
+ 'cACertificate': dummy_certificate(),
'certificateRevocationList': ['XXX'],
})
# Write the dummy pKIEnrollmentService
@@ -7681,7 +7681,7 @@ class GPOTests(tests.TestCase):
self.addCleanup(ldb.delete, enroll_dn)
ldb.add({'dn': enroll_dn,
'objectClass': 'pKIEnrollmentService',
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
+ 'cACertificate': dummy_certificate(),
'certificateTemplates': ['Machine'],
'dNSHostName': hostname,
})
diff --git a/selftest/knownfail.d/gpo-auto-enrol b/selftest/knownfail.d/gpo-auto-enrol
index 4bf4b8e3c72..4b787a5ac86 100644
--- a/selftest/knownfail.d/gpo-auto-enrol
+++ b/selftest/knownfail.d/gpo-auto-enrol
@@ -1,2 +1 @@
^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
-^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)
--
2.53.0
From 39970373fe70615e28b0fe6e56169b0e3674b95a Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Tue, 24 Feb 2026 16:11:15 +0100
Subject: [PATCH 42/66] CVE-2026-3238 winsserver4: Dissolve direct variable
initialization
Checks are required before the packet is dereferenced
Bug: https://bugzilla.samba.org/show_bug.cgi?id=16012
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source4/nbt_server/wins/winsserver.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/source4/nbt_server/wins/winsserver.c b/source4/nbt_server/wins/winsserver.c
index 6679961dc03..1b7fe5641a6 100644
--- a/source4/nbt_server/wins/winsserver.c
+++ b/source4/nbt_server/wins/winsserver.c
@@ -460,16 +460,27 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
- struct nbt_name *name = &packet->questions[0].name;
+ struct nbt_name *name = NULL;
struct winsdb_record *rec;
uint8_t rcode = NBT_RCODE_OK;
- uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
- const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+ struct nbt_res_rec *additional = NULL;
+ uint16_t nb_flags;
+ const char *address = NULL;
+ struct nbt_rdata_address *addresses = NULL;
bool mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
- enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
+ enum wrepl_name_type new_type;
struct winsdb_addr *winsdb_addr = NULL;
bool duplicate_packet;
+ name = &packet->questions[0].name;
+ additional = packet->additional;
+
+ addresses = additional[0].rdata.netbios.addresses;
+
+ nb_flags = addresses[0].nb_flags;
+ address = addresses[0].ipaddr;
+ new_type = wrepl_type(nb_flags, name, mhomed);
+
/*
* as a special case, the local master browser name is always accepted
* for registration, but never stored, but w2k3 stores it if it's registered
@@ -729,13 +740,15 @@ static void nbtd_winsserver_query(struct loadparm_context *lp_ctx,
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
- struct nbt_name *name = &packet->questions[0].name;
+ struct nbt_name *name = NULL;
struct winsdb_record *rec;
struct winsdb_record *rec_1b = NULL;
const char **addresses;
const char **addresses_1b = NULL;
uint16_t nb_flags = 0;
+ name = &packet->questions[0].name;
+
if (name->type == NBT_NAME_MASTER) {
goto notfound;
}
@@ -871,11 +884,13 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
struct nbtd_interface);
struct wins_server *winssrv = iface->nbtsrv->winssrv;
- struct nbt_name *name = &packet->questions[0].name;
+ struct nbt_name *name = NULL;
struct winsdb_record *rec;
uint32_t modify_flags = 0;
uint8_t ret;
+ name = &packet->questions[0].name;
+
if (name->type == NBT_NAME_MASTER) {
goto done;
}
--
2.53.0
From a9c5255d1e46738e8148152721aa49add737e64b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Tue, 24 Feb 2026 16:30:46 +0100
Subject: [PATCH 43/66] CVE-2026-3238 winsserver4: Validate incoming packets
Avoid NULL pointer dereferences, leading to a crash in the nbt process
serving wins.
Thanks to Arad Inbar, Erez Cohen, Nir Somech and Ben Grinberg from
DREAM Security Research Team for pointing out this crash bug out to
the Samba team.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=16012
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source4/nbt_server/wins/winsserver.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/source4/nbt_server/wins/winsserver.c b/source4/nbt_server/wins/winsserver.c
index 1b7fe5641a6..c637657f07c 100644
--- a/source4/nbt_server/wins/winsserver.c
+++ b/source4/nbt_server/wins/winsserver.c
@@ -472,9 +472,16 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
struct winsdb_addr *winsdb_addr = NULL;
bool duplicate_packet;
+ NBTD_ASSERT_PACKET(packet, src, packet->qdcount > 0);
+ NBTD_ASSERT_PACKET(packet, src, packet->arcount > 0);
+
name = &packet->questions[0].name;
additional = packet->additional;
+ NBTD_ASSERT_PACKET(packet,
+ src,
+ additional[0].rdata.netbios.length > 0);
+
addresses = additional[0].rdata.netbios.addresses;
nb_flags = addresses[0].nb_flags;
@@ -747,6 +754,8 @@ static void nbtd_winsserver_query(struct loadparm_context *lp_ctx,
const char **addresses_1b = NULL;
uint16_t nb_flags = 0;
+ NBTD_ASSERT_PACKET(packet, src, packet->qdcount > 0);
+
name = &packet->questions[0].name;
if (name->type == NBT_NAME_MASTER) {
@@ -889,6 +898,8 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
uint32_t modify_flags = 0;
uint8_t ret;
+ NBTD_ASSERT_PACKET(packet, src, packet->qdcount > 0);
+
name = &packet->questions[0].name;
if (name->type == NBT_NAME_MASTER) {
--
2.53.0
From ae5323f5bacb2d461a663cc34ba33c9c0fa05f8e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:20:15 +0200
Subject: [PATCH 44/66] CVE-2026-4480/CVE-2026-4408: lib/util: inline
string_sub2() into string_sub() the only caller
This will simplify further changes.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.c | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index b7b5588da86..26362ca77b2 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -47,10 +47,9 @@
use of len==0 which was for no length checks to be done.
**/
-static void string_sub2(char *s,const char *pattern, const char *insert, size_t len,
- bool remove_unsafe_characters, bool replace_once,
- bool allow_trailing_dollar)
+void string_sub(char *s, const char *pattern, const char *insert, size_t len)
{
+ bool remove_unsafe_characters = true;
char *p;
size_t ls, lp, li, i;
@@ -79,13 +78,6 @@ static void string_sub2(char *s,const char *pattern, const char *insert, size_t
for (i=0;i<li;i++) {
switch (insert[i]) {
case '$':
- /* allow a trailing $
- * (as in machine accounts) */
- if (allow_trailing_dollar && (i == li - 1 )) {
- p[i] = insert[i];
- break;
- }
- FALL_THROUGH;
case '`':
case '"':
case '\'':
@@ -107,17 +99,9 @@ static void string_sub2(char *s,const char *pattern, const char *insert, size_t
}
s = p + li;
ls = ls + li - lp;
-
- if (replace_once)
- break;
}
}
-void string_sub(char *s,const char *pattern, const char *insert, size_t len)
-{
- string_sub2( s, pattern, insert, len, true, false, false );
-}
-
/**
Similar to string_sub() but allows for any character to be substituted.
Use with caution!
--
2.53.0
From cc8fb24520b1c11e5b43714333d642fdb02449cb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:20:15 +0200
Subject: [PATCH 45/66] CVE-2026-4480/CVE-2026-4408: lib/util: remove unused
talloc_strdup(insert) from talloc_string_sub2()
The insert string is not modified, so we do not need to copy it.
This will simplify further changes.
Review with: git show --patience
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.c | 57 +++++++++++++++++++------------------------
1 file changed, 25 insertions(+), 32 deletions(-)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index 26362ca77b2..4a0c58ab3a7 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -157,7 +157,7 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
bool replace_once,
bool allow_trailing_dollar)
{
- char *p, *in;
+ char *p;
char *s;
char *string;
ssize_t ls,lp,li,ld, i;
@@ -175,22 +175,32 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
s = string;
- in = talloc_strdup(mem_ctx, insert);
- if (!in) {
- DEBUG(0, ("talloc_string_sub2: ENOMEM\n"));
- talloc_free(string);
- return NULL;
- }
ls = (ssize_t)strlen(s);
lp = (ssize_t)strlen(pattern);
li = (ssize_t)strlen(insert);
ld = li - lp;
- for (i=0;i<li;i++) {
- switch (in[i]) {
+ while ((p = strstr_m(s,pattern))) {
+ if (ld > 0) {
+ int offset = PTR_DIFF(s,string);
+ string = (char *)talloc_realloc_size(mem_ctx, string,
+ ls + ld + 1);
+ if (!string) {
+ DEBUG(0, ("talloc_string_sub: out of "
+ "memory!\n"));
+ return NULL;
+ }
+ p = string + offset + (p - s);
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ for (i=0; i<li; i++) {
+ switch (insert[i]) {
case '$':
- /* allow a trailing $
- * (as in machine accounts) */
+ /*
+ * allow a trailing $ (as in machine accounts)
+ */
if (allow_trailing_dollar && (i == li - 1 )) {
break;
}
@@ -204,34 +214,18 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
case '\r':
case '\n':
if (remove_unsafe_characters) {
- in[i] = '_';
- break;
+ p[i] = '_';
+ continue;
}
FALL_THROUGH;
default:
/* ok */
break;
- }
- }
-
- while ((p = strstr_m(s,pattern))) {
- if (ld > 0) {
- int offset = PTR_DIFF(s,string);
- string = (char *)talloc_realloc_size(mem_ctx, string,
- ls + ld + 1);
- if (!string) {
- DEBUG(0, ("talloc_string_sub: out of "
- "memory!\n"));
- TALLOC_FREE(in);
- return NULL;
}
- p = string + offset + (p - s);
- }
- if (li != lp) {
- memmove(p+li,p+lp,strlen(p+lp)+1);
+
+ p[i] = insert[i];
}
- memcpy(p, in, li);
s = p + li;
ls += ld;
@@ -239,7 +233,6 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
break;
}
}
- TALLOC_FREE(in);
return string;
}
--
2.53.0
From 1a672e8ba0300ab2067e50fd7bb53a7e3fa7a194 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:20:15 +0200
Subject: [PATCH 46/66] CVE-2026-4480/CVE-2026-4408: lib/util: factor out a
mask_unsafe_character() helper function
This moves the logic into a single place and
makes if more flexible to be used with more
values than STRING_SUB_UNSAFE_CHARACTERS.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.c | 109 +++++++++++++++++++++---------------------
lib/util/substitute.h | 6 ++-
2 files changed, 60 insertions(+), 55 deletions(-)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index 4a0c58ab3a7..b9fe32e993e 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -35,6 +35,33 @@
* @brief Substitute utilities.
**/
+static inline
+char mask_unsafe_character(char in,
+ bool is_last,
+ bool allow_trailing_dollar,
+ const char *unsafe_characters,
+ char safe_out)
+{
+ const char *unsafe = NULL;
+
+ if (unsafe_characters == NULL) {
+ return in;
+ }
+
+ /* allow a trailing $ (as in machine accounts) */
+ if (allow_trailing_dollar && is_last && in == '$') {
+ return in;
+ }
+
+ unsafe = strchr(unsafe_characters, in);
+ if (unsafe != NULL) {
+ return safe_out;
+ }
+
+ /* ok */
+ return in;
+}
+
/**
Substitute a string for a pattern in another string. Make sure there is
enough room!
@@ -42,14 +69,16 @@
This routine looks for pattern in s and replaces it with
insert. It may do multiple replacements or just one.
- Any of " ; ' $ or ` in the insert string are replaced with _
+ Any of STRING_SUB_UNSAFE_CHARACTERS in the insert string are replaced with _
+
if len==0 then the string cannot be extended. This is different from the old
use of len==0 which was for no length checks to be done.
**/
void string_sub(char *s, const char *pattern, const char *insert, size_t len)
{
- bool remove_unsafe_characters = true;
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+ char safe_character = '_';
char *p;
size_t ls, lp, li, i;
@@ -76,26 +105,18 @@ void string_sub(char *s, const char *pattern, const char *insert, size_t len)
memmove(p+li,p+lp,strlen(p+lp)+1);
}
for (i=0;i<li;i++) {
- switch (insert[i]) {
- case '$':
- case '`':
- case '"':
- case '\'':
- case ';':
- case '%':
- case '\r':
- case '\n':
- if ( remove_unsafe_characters ) {
- p[i] = '_';
- /* yes this break should be here
- * since we want to fall throw if
- * not replacing unsafe chars */
- break;
- }
- FALL_THROUGH;
- default:
- p[i] = insert[i];
- }
+ /*
+ * Without allow_trailing_dollar we don't
+ * need to calculate is_last...
+ */
+ const bool is_last = false;
+ const bool allow_trailing_dollar = false;
+
+ p[i] = mask_unsafe_character(insert[i],
+ is_last,
+ allow_trailing_dollar,
+ unsafe_characters,
+ safe_character);
}
s = p + li;
ls = ls + li - lp;
@@ -157,9 +178,11 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
bool replace_once,
bool allow_trailing_dollar)
{
- char *p;
- char *s;
- char *string;
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+ const char safe_character = '_';
+ char *p = NULL,
+ char *s = NULL;
+ char *string = NULL;
ssize_t ls,lp,li,ld, i;
if (!insert || !pattern || !*pattern || !src) {
@@ -195,36 +218,14 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
if (li != lp) {
memmove(p+li,p+lp,strlen(p+lp)+1);
}
- for (i=0; i<li; i++) {
- switch (insert[i]) {
- case '$':
- /*
- * allow a trailing $ (as in machine accounts)
- */
- if (allow_trailing_dollar && (i == li - 1 )) {
- break;
- }
-
- FALL_THROUGH;
- case '`':
- case '"':
- case '\'':
- case ';':
- case '%':
- case '\r':
- case '\n':
- if (remove_unsafe_characters) {
- p[i] = '_';
- continue;
- }
-
- FALL_THROUGH;
- default:
- /* ok */
- break;
- }
+ for (i=0; i < li; i++) {
+ bool is_last = (i == li - 1);
- p[i] = insert[i];
+ p[i] = mask_unsafe_character(insert[i],
+ is_last,
+ allow_trailing_dollar,
+ unsafe_characters,
+ safe_character);
}
s = p + li;
ls += ld;
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
index 3134cfcdea5..e1a82859dac 100644
--- a/lib/util/substitute.h
+++ b/lib/util/substitute.h
@@ -26,6 +26,8 @@
#include <talloc.h>
+#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%\r\n"
+
/**
Substitute a string for a pattern in another string. Make sure there is
enough room!
@@ -33,7 +35,9 @@
This routine looks for pattern in s and replaces it with
insert. It may do multiple replacements.
- Any of " ; ' $ or ` in the insert string are replaced with _
+ Any of STRING_SUB_UNSAFE_CHARACTERS (see above) in the
+ insert string are replaced with _
+
if len==0 then the string cannot be extended. This is different from the old
use of len==0 which was for no length checks to be done.
**/
--
2.53.0
From f311078eae4731ca2af8a14c1d4091e7263ca507 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 30 Apr 2026 14:48:26 +0200
Subject: [PATCH 47/66] CVE-2026-4480/CVE-2026-4408: lib/util: split out
realloc_string_sub_raw()
This will allow realloc_string_sub2() to use it in order
to have the logic in one place only.
And it will also allow adjacted callers to be
more flexible.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.c | 85 ++++++++++++++++++++++++++++++-------------
lib/util/substitute.h | 18 +++++++++
2 files changed, 78 insertions(+), 25 deletions(-)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index b9fe32e993e..465aea86605 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -171,32 +171,24 @@ _PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, siz
* talloc version of string_sub2.
*/
-char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
- const char *pattern,
- const char *insert,
- bool remove_unsafe_characters,
- bool replace_once,
- bool allow_trailing_dollar)
+bool realloc_string_sub_raw(char **_string,
+ const char *pattern,
+ const char *insert,
+ bool replace_once,
+ bool allow_trailing_dollar,
+ const char *unsafe_characters,
+ char safe_character)
{
- const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
- const char safe_character = '_';
- char *p = NULL,
+ char *p = NULL;
char *s = NULL;
char *string = NULL;
ssize_t ls,lp,li,ld, i;
- if (!insert || !pattern || !*pattern || !src) {
- return NULL;
- }
-
- string = talloc_strdup(mem_ctx, src);
- if (string == NULL) {
- DEBUG(0, ("talloc_string_sub2: "
- "talloc_strdup failed\n"));
- return NULL;
+ if (!insert || !pattern || !*pattern || !_string|| !*_string) {
+ return false;
}
- s = string;
+ s = string = *_string;
ls = (ssize_t)strlen(s);
lp = (ssize_t)strlen(pattern);
@@ -205,14 +197,13 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
while ((p = strstr_m(s,pattern))) {
if (ld > 0) {
- int offset = PTR_DIFF(s,string);
- string = (char *)talloc_realloc_size(mem_ctx, string,
- ls + ld + 1);
+ ptrdiff_t offset = PTR_DIFF(s,string);
+ string = talloc_realloc(NULL, string, char, ls + ld + 1);
if (!string) {
- DEBUG(0, ("talloc_string_sub: out of "
- "memory!\n"));
- return NULL;
+ DBG_ERR("out of memory(realloc)!\n");
+ return false;
}
+ *_string = string;
p = string + offset + (p - s);
}
if (li != lp) {
@@ -234,6 +225,50 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
break;
}
}
+ return true;
+}
+
+char *talloc_string_sub2(TALLOC_CTX *mem_ctx,
+ const char *src,
+ const char *pattern,
+ const char *insert,
+ bool remove_unsafe_characters,
+ bool replace_once,
+ bool allow_trailing_dollar)
+{
+ const char *unsafe_characters = NULL;
+ char safe_character = '\0';
+ char *string = NULL;
+ bool ok;
+
+ if (!insert || !pattern || !*pattern || !src) {
+ return NULL;
+ }
+
+ if (remove_unsafe_characters) {
+ unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+ safe_character = '_';
+ }
+
+ string = talloc_strdup(mem_ctx, src);
+ if (string == NULL) {
+ DBG_ERR("out of memory, talloc_strdup(src)!\n");
+ return NULL;
+ }
+
+ ok = realloc_string_sub_raw(&string,
+ pattern,
+ insert,
+ replace_once,
+ allow_trailing_dollar,
+ unsafe_characters,
+ safe_character);
+ if (!ok) {
+ TALLOC_FREE(string);
+ DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
+ return NULL;
+ }
+
return string;
}
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
index e1a82859dac..041a649fd18 100644
--- a/lib/util/substitute.h
+++ b/lib/util/substitute.h
@@ -51,6 +51,24 @@ void string_sub(char *s,const char *pattern, const char *insert, size_t len);
**/
void all_string_sub(char *s,const char *pattern,const char *insert, size_t len);
+/*
+ * If unsafe_characters is NULL all characters are allowed,
+ * if unsafe_characters is not NULL all characters caught
+ * by iscntrl() are also replaced by safe_character.
+ *
+ * *_string might be reallocated!
+ *
+ * On error *_string may still be reallocated and
+ * may contain partial replacements.
+ */
+bool realloc_string_sub_raw(char **_string,
+ const char *pattern,
+ const char *insert,
+ bool replace_once,
+ bool allow_trailing_dollar,
+ const char *unsafe_characters,
+ char safe_character);
+
char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
const char *pattern,
const char *insert,
--
2.53.0
From 3d28a71755b69b6e0afe5f5d9bbc9aff620b0f4f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Wed, 6 May 2026 17:23:39 +0200
Subject: [PATCH 48/66] CVE-2026-4480/CVE-2026-4408: s3:lib: fix potential
memory leak in talloc_sub_basic()
This makes the code easier to understand...
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/lib/substitute.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c
index 40eb15aee04..5121fcaac1c 100644
--- a/source3/lib/substitute.c
+++ b/source3/lib/substitute.c
@@ -317,6 +317,7 @@ char *talloc_sub_basic(TALLOC_CTX *mem_ctx,
}
tmp_ctx = talloc_stackframe();
+ a_string = talloc_steal(tmp_ctx, a_string);
for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
@@ -478,6 +479,7 @@ error:
TALLOC_FREE(a_string);
done:
+ a_string = talloc_steal(mem_ctx, a_string);
TALLOC_FREE(tmp_ctx);
return a_string;
}
--
2.53.0
From c73fc960e67474ded96fdeebf3e2779e2b28799b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 21:11:27 +0200
Subject: [PATCH 49/66] CVE-2026-4480/CVE-2026-4408: s3:lib: let
realloc_string_sub2() use realloc_string_sub_raw()
We don't need this logic more than once!
But we leave the strange calling convention of
realloc_string_sub2(), where the caller it
not allowed to use the passed pointer when
NULL is returned...
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/lib/substitute_generic.c | 81 ++++++++++----------------------
1 file changed, 24 insertions(+), 57 deletions(-)
diff --git a/source3/lib/substitute_generic.c b/source3/lib/substitute_generic.c
index 26c5ee761f8..e0639f04eb8 100644
--- a/source3/lib/substitute_generic.c
+++ b/source3/lib/substitute_generic.c
@@ -37,71 +37,38 @@ char *realloc_string_sub2(char *string,
bool remove_unsafe_characters,
bool allow_trailing_dollar)
{
- char *p, *in;
- char *s;
- ssize_t ls,lp,li,ld, i;
+ const char *unsafe_characters = NULL;
+ char safe_character = '\0';
+ bool ok;
if (!insert || !pattern || !*pattern || !string || !*string)
return NULL;
- s = string;
+ if (remove_unsafe_characters) {
+ unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+ safe_character = '_';
+ }
- in = talloc_strdup(talloc_tos(), insert);
- if (!in) {
- DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+ ok = realloc_string_sub_raw(&string,
+ pattern,
+ insert,
+ false, /* replace_once */
+ allow_trailing_dollar,
+ unsafe_characters,
+ safe_character);
+ if (!ok) {
+ DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
+ /*
+ * The calling convention of realloc_string_sub2()
+ * is very strange regarding stale string pointers.
+ *
+ * It is assumed the given string was allocated
+ * on talloc_tos(), so we just don't touch
+ * it at all here...
+ */
return NULL;
}
- ls = (ssize_t)strlen(s);
- lp = (ssize_t)strlen(pattern);
- li = (ssize_t)strlen(insert);
- ld = li - lp;
- for (i=0;i<li;i++) {
- switch (in[i]) {
- case '$':
- /* allow a trailing $
- * (as in machine accounts) */
- if (allow_trailing_dollar && (i == li - 1 )) {
- break;
- }
- FALL_THROUGH;
- case '`':
- case '"':
- case '\'':
- case ';':
- case '%':
- case '\r':
- case '\n':
- if ( remove_unsafe_characters ) {
- in[i] = '_';
- break;
- }
- FALL_THROUGH;
- default:
- /* ok */
- break;
- }
- }
- while ((p = strstr_m(s,pattern))) {
- if (ld > 0) {
- int offset = PTR_DIFF(s,string);
- string = talloc_realloc(NULL, string, char, ls + ld + 1);
- if (!string) {
- DEBUG(0, ("realloc_string_sub: "
- "out of memory!\n"));
- talloc_free(in);
- return NULL;
- }
- p = string + offset + (p - s);
- }
- if (li != lp) {
- memmove(p+li,p+lp,strlen(p+lp)+1);
- }
- memcpy(p, in, li);
- s = p + li;
- ls += ld;
- }
- talloc_free(in);
return string;
}
--
2.53.0
From fc4aa4b816bfe1c3c128acf2cbfac652899d4cb6 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:21:08 +0200
Subject: [PATCH 50/66] CVE-2026-4480/CVE-2026-4408: lib/util: let
mask_unsafe_character() check all control characters
There's no reason to mask only \r and \n.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.c | 8 +++++++-
lib/util/substitute.h | 6 +++---
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index 465aea86605..30989927da7 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -22,6 +22,7 @@
*/
#include "replace.h"
+#include "system/locale.h"
#include "debug.h"
#ifndef SAMBA_UTIL_CORE_ONLY
#include "charset/charset.h"
@@ -53,6 +54,10 @@ char mask_unsafe_character(char in,
return in;
}
+ if (iscntrl(in)) {
+ return safe_out;
+ }
+
unsafe = strchr(unsafe_characters, in);
if (unsafe != NULL) {
return safe_out;
@@ -69,7 +74,8 @@ char mask_unsafe_character(char in,
This routine looks for pattern in s and replaces it with
insert. It may do multiple replacements or just one.
- Any of STRING_SUB_UNSAFE_CHARACTERS in the insert string are replaced with _
+ Any of STRING_SUB_UNSAFE_CHARACTERS and any character
+ caught by calling iscntrl() in the insert string are replaced with _
if len==0 then the string cannot be extended. This is different from the old
use of len==0 which was for no length checks to be done.
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
index 041a649fd18..b183d864671 100644
--- a/lib/util/substitute.h
+++ b/lib/util/substitute.h
@@ -26,7 +26,7 @@
#include <talloc.h>
-#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%\r\n"
+#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%"
/**
Substitute a string for a pattern in another string. Make sure there is
@@ -35,8 +35,8 @@
This routine looks for pattern in s and replaces it with
insert. It may do multiple replacements.
- Any of STRING_SUB_UNSAFE_CHARACTERS (see above) in the
- insert string are replaced with _
+ Any of STRING_SUB_UNSAFE_CHARACTERS (see above) and any character
+ caught by calling iscntrl() in the insert string are replaced with _
if len==0 then the string cannot be extended. This is different from the old
use of len==0 which was for no length checks to be done.
--
2.53.0
From fbe0cdf05b0f8f23a39766158cea9c9886000c67 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:21:08 +0200
Subject: [PATCH 51/66] CVE-2026-4480/CVE-2026-4408: lib/util: add more unsafe
characters to STRING_SUB_UNSAFE_CHARACTERS
|&<> are unsafe characters for shell processing.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
index b183d864671..41f56c73ba2 100644
--- a/lib/util/substitute.h
+++ b/lib/util/substitute.h
@@ -26,7 +26,7 @@
#include <talloc.h>
-#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%"
+#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%|&<>"
/**
Substitute a string for a pattern in another string. Make sure there is
--
2.53.0
From 37158038bc10998a59ff7c2b699c35e0dfecc83d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Fri, 8 May 2026 22:33:32 +0200
Subject: [PATCH 52/66] CVE-2026-4480/CVE-2026-4408: lib/util: let log_escape()
make use of iscntrl()
using iscntrl() also handles 0x7F (DEL).
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/util_str_escape.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/util/util_str_escape.c b/lib/util/util_str_escape.c
index 8f1f34912ee..c6d7a0c9e77 100644
--- a/lib/util/util_str_escape.c
+++ b/lib/util/util_str_escape.c
@@ -18,6 +18,7 @@
*/
#include "replace.h"
+#include "system/locale.h"
#include "lib/util/debug.h"
#include "lib/util/util_str_escape.h"
@@ -28,7 +29,7 @@
*/
static size_t encoded_length(unsigned char c)
{
- if (c != '\\' && c > 0x1F) {
+ if (c != '\\' && !iscntrl(c)) {
return 1;
} else {
switch (c) {
@@ -79,7 +80,7 @@ char *log_escape(TALLOC_CTX *frame, const char *in)
c = in;
e = encoded;
while (*c) {
- if (*c != '\\' && (unsigned char)(*c) > 0x1F) {
+ if (*c != '\\' && !iscntrl((unsigned char)(*c))) {
*e++ = *c++;
} else {
switch (*c) {
--
2.53.0
From e0f1a55382891d113a43184a692f58a7c9832efe Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 7 May 2026 18:10:50 +0200
Subject: [PATCH 53/66] CVE-2026-4480/CVE-2026-4408: lib/util: add
talloc_string_sub_{mixed_quoting,unsafe}() helpers
This is the basic helper function for the security problems.
talloc_string_sub_mixed_quoting() checks for strange quoting
in smb.conf options.
And talloc_string_sub_unsafe() tries to autodetect how the unsafe
(client controlled value) and masked and single quote it,
as a fallback for strange quoting a fixed fallback string
is used and the caller should warn the admin and give
hints how to fix the configuration.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Pair-Programmed-With: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/substitute.c | 260 ++++++++++++++++++++++++++++++++++++++++++
lib/util/substitute.h | 17 +++
2 files changed, 277 insertions(+)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
index 30989927da7..406d8424be1 100644
--- a/lib/util/substitute.c
+++ b/lib/util/substitute.c
@@ -25,6 +25,8 @@
#include "system/locale.h"
#include "debug.h"
#ifndef SAMBA_UTIL_CORE_ONLY
+#include "lib/util/fault.h"
+#include "lib/util/talloc_stack.h"
#include "charset/charset.h"
#else
#include "charset_compat.h"
@@ -297,3 +299,261 @@ char *talloc_all_string_sub(TALLOC_CTX *ctx,
return talloc_string_sub2(ctx, src, pattern, insert,
false, false, false);
}
+
+#ifndef SAMBA_UTIL_CORE_ONLY
+
+bool talloc_string_sub_mixed_quoting(const char *full_cmd, char variable_char)
+{
+ /*
+ * Try to make sure talloc_string_sub_unsafe()
+ * won't return NULL, instead talloc_stackframe_pool()
+ * would panic
+ */
+ size_t cmd_len = full_cmd != NULL ? strlen(full_cmd) : 0;
+ size_t pool_size = 512 + cmd_len;
+ TALLOC_CTX *frame = talloc_stackframe_pool(pool_size);
+ char *cmd = NULL;
+ bool modified = false;
+ bool masked = false;
+ bool mixed_fallback = false;
+
+ cmd = talloc_string_sub_unsafe(frame,
+ full_cmd,
+ variable_char,
+ "U", /* unsafe_value */
+ "'\"%", /* unsafe_characters */
+ '_', /* safe_character */
+ "F", /* fallback_value */
+ &modified,
+ &masked,
+ &mixed_fallback);
+ if (cmd == NULL) {
+ mixed_fallback = false;
+ }
+ TALLOC_FREE(frame);
+ return mixed_fallback;
+}
+
+char *talloc_string_sub_unsafe(TALLOC_CTX *mem_ctx,
+ const char *orig_cmd,
+ char variable_char,
+ const char *unsafe_value,
+ const char *unsafe_characters,
+ char safe_character,
+ const char *fallback_value,
+ bool *_modified,
+ bool *_masked,
+ bool *_mixed_fallback)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char variable[3] =
+ { '%', variable_char, '\0' };
+ const char variable_s_quoted[5] =
+ { '\'', '%', variable_char, '\'', '\0' };
+ const char variable_d_quoted[5] =
+ { '"', '%', variable_char, '"', '\0' };
+ char *cmd = NULL;
+ char *masked_value = NULL;
+ char *quoted_value = NULL;
+ bool has_s_quotes;
+ bool has_d_quotes;
+ bool has_variable;
+ bool has_variable_s_quoted;
+ bool has_variable_d_quoted;
+ bool modified = false;
+ bool masked = false;
+ bool mixed_fallback = false;
+ bool ok;
+
+ /*
+ * The unsafe_characters argument should contain
+ * single and double quotes.
+ * Otherwise We can't safely handle this.
+ */
+ SMB_ASSERT(unsafe_characters != NULL);
+ SMB_ASSERT(strchr(unsafe_characters, '\'') != NULL);
+ SMB_ASSERT(strchr(unsafe_characters, '"') != NULL);
+ SMB_ASSERT(strchr(unsafe_characters, '%') != NULL);
+
+ cmd = talloc_strdup(mem_ctx, orig_cmd);
+ if (cmd == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ cmd = talloc_steal(frame, cmd);
+
+ has_variable = strstr(orig_cmd, variable) != NULL;
+ if (!has_variable) {
+ /*
+ * Nothing to do...
+ */
+ goto done;
+ }
+ modified = true;
+
+ /*
+ * Replace all unsafe characters as well as control
+ * characters.
+ *
+ * Note that we start with masked_value = "%u"
+ * and then replace "%u" with unsafe_value,
+ * as a result we have a masked version of
+ * unsafe_value.
+ *
+ * And don't allow option injected like
+ *
+ * '-h value'
+ * '--help value'
+ *
+ */
+ masked_value = talloc_strdup(frame, variable);
+ if (masked_value == NULL) {
+ goto nomem;
+ }
+ ok = realloc_string_sub_raw(&masked_value,
+ variable,
+ unsafe_value,
+ false, /* replace_once */
+ false, /* allow_trailing_dollar */
+ unsafe_characters,
+ safe_character);
+ if (!ok) {
+ goto nomem;
+ }
+ if (masked_value[0] == '-') {
+ masked_value[0] = safe_character;
+ }
+ masked = strcmp(masked_value, unsafe_value) != 0;
+
+retry:
+
+ has_s_quotes = strchr(cmd, '\'') != NULL;
+ has_d_quotes = strchr(cmd, '"') != NULL;
+ has_variable = strstr(cmd, variable) != NULL;
+ has_variable_s_quoted = strstr(cmd, variable_s_quoted) != NULL;
+ has_variable_d_quoted = strstr(cmd, variable_d_quoted) != NULL;
+
+ if (has_variable_s_quoted) {
+ /*
+ * In smb.conf we have something like
+ *
+ * some script = /usr/bin/script '%u'
+ *
+ * It is safe to replace '%u' (or '%J' etc, depending
+ * on variable_char) with '<masked_value>' if
+ * masked_value does not contain single quotes. We
+ * have checked that.
+ */
+
+ if (quoted_value == NULL) {
+ quoted_value = talloc_asprintf(frame, "'%s'",
+ masked_value);
+ if (quoted_value == NULL) {
+ goto nomem;
+ }
+ }
+
+ ok = realloc_string_sub_raw(&cmd,
+ variable_s_quoted,
+ quoted_value,
+ false, /* replace_once */
+ false, /* allow_trailing_dollar */
+ NULL, /* unsafe_characters */
+ '\0'); /* safe_character */
+ if (!ok) {
+ goto nomem;
+ }
+
+ goto retry;
+ }
+
+ if (has_variable_d_quoted && !has_s_quotes) {
+ /*
+ * replace the "%u"
+ *
+ * some script = /usr/bin/script "%u"
+ *
+ * with '%u' and try the '%u' -> 'variable' substitution
+ * again.
+ */
+
+ ok = realloc_string_sub_raw(&cmd,
+ variable_d_quoted,
+ variable_s_quoted,
+ false, /* replace_once */
+ false, /* allow_trailing_dollar */
+ NULL, /* unsafe_characters */
+ '\0'); /* safe_character */
+ if (!ok) {
+ goto nomem;
+ }
+
+ goto retry;
+ }
+
+ if (has_variable && !has_s_quotes && !has_d_quotes) {
+ /*
+ * In this case:
+ *
+ * some script = /usr/bin/script %u
+ *
+ * we can safely substitute %u -> '%u' and try the
+ * single quote test again.
+ */
+
+ ok = realloc_string_sub_raw(&cmd,
+ variable,
+ variable_s_quoted,
+ false, /* replace_once */
+ false, /* allow_trailing_dollar */
+ NULL, /* unsafe_characters */
+ '\0'); /* safe_character */
+ if (!ok) {
+ goto nomem;
+ }
+
+ goto retry;
+ }
+
+ if (has_variable) {
+ /*
+ * There are single or double quotes, but not tightly
+ * bound around a %u.
+ *
+ * Or there's a mix of single and double quotes.
+ *
+ * We just use a generic fallback value.
+ * and let the caller warn about this
+ * and give the admin a hind to fix the smb.conf
+ * option.
+ */
+ mixed_fallback = true;
+
+ ok = realloc_string_sub_raw(&cmd,
+ variable,
+ fallback_value,
+ false, /* replace_once */
+ false, /* allow_trailing_dollar */
+ NULL, /* unsafe_characters */
+ '\0'); /* safe_character */
+ if (!ok) {
+ goto nomem;
+ }
+ }
+
+done:
+ *_modified = modified;
+ *_masked = masked;
+ *_mixed_fallback = mixed_fallback;
+ cmd = talloc_steal(mem_ctx, cmd);
+ TALLOC_FREE(frame);
+ return cmd;
+
+nomem:
+ *_modified = false;
+ *_masked = false;
+ *_mixed_fallback = false;
+ TALLOC_FREE(frame);
+ return NULL;
+}
+#endif /* ! SAMBA_UTIL_CORE_ONLY */
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
index 41f56c73ba2..b8205055da1 100644
--- a/lib/util/substitute.h
+++ b/lib/util/substitute.h
@@ -83,4 +83,21 @@ char *talloc_all_string_sub(TALLOC_CTX *ctx,
const char *src,
const char *pattern,
const char *insert);
+
+#ifndef SAMBA_UTIL_CORE_ONLY
+bool talloc_string_sub_mixed_quoting(const char *full_cmd, char variable_char);
+
+char *talloc_string_sub_unsafe(TALLOC_CTX *mem_ctx,
+ const char *orig_cmd,
+ char variable_char,
+ const char *unsafe_value,
+ const char *unsafe_characters,
+ char safe_character,
+ const char *fallback_value,
+ bool *_modified,
+ bool *_masked,
+ bool *_mixed_fallback);
+
+#endif /* ! SAMBA_UTIL_CORE_ONLY */
+
#endif /* _SAMBA_SUBSTITUTE_H_ */
--
2.53.0
From b0425235ed880fb510aba1fe0fc07ee5d7304337 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Sat, 9 May 2026 22:02:47 +1200
Subject: [PATCH 54/66] CVE-2026-4480/CVE-2026-4408: lib/util: add
test_string_sub unittests
This demonstrates the logic of talloc_string_sub_{mixed_quoting,unsafe}()
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
lib/util/tests/test_string_sub.c | 1044 ++++++++++++++++++++++++++++++
lib/util/wscript_build | 6 +
selftest/tests.py | 2 +
3 files changed, 1052 insertions(+)
create mode 100644 lib/util/tests/test_string_sub.c
diff --git a/lib/util/tests/test_string_sub.c b/lib/util/tests/test_string_sub.c
new file mode 100644
index 00000000000..da97c1c936c
--- /dev/null
+++ b/lib/util/tests/test_string_sub.c
@@ -0,0 +1,1044 @@
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include "replace.h"
+#include <cmocka.h>
+#include "talloc.h"
+
+#include "../substitute.h"
+
+/* set _DEBUG_VERBOSE to print more. */
+#define _DEBUG_VERBOSE
+
+#ifdef _DEBUG_VERBOSE
+#define debug_message(...) print_message(__VA_ARGS__)
+#else
+#define debug_message(...) /* debug_message */
+#endif
+
+
+static int setup_talloc_context(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ *state = mem_ctx;
+ return 0;
+}
+
+static int teardown_talloc_context(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ TALLOC_FREE(mem_ctx);
+ return 0;
+}
+
+struct cmd_expansion {
+ const char *lp_cmd;
+ const char *username;
+ const char *result_cmd;
+ bool modified;
+ bool masked;
+ bool mixed_fallback;
+};
+
+static void _test_talloc_string_sub_unsafe(void **state,
+ struct cmd_expansion expansions[],
+ size_t n_expansions,
+ const char *unsafe_characters)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ size_t i;
+
+ for (i = 0; i < n_expansions; i++) {
+ struct cmd_expansion t = expansions[i];
+ char *result_cmd = NULL;
+ bool masked;
+ bool mixed_fallback;
+ bool modified;
+ bool flags_correct;
+ bool mixed;
+ int cmp;
+
+ mixed = talloc_string_sub_mixed_quoting(t.lp_cmd, 'u');
+
+ result_cmd = talloc_string_sub_unsafe(mem_ctx,
+ t.lp_cmd,
+ 'u',
+ t.username,
+ unsafe_characters,
+ '_',
+ "FallbackUsername",
+ &modified,
+ &masked,
+ &mixed_fallback);
+ assert_ptr_not_equal(result_cmd, NULL);
+ assert_ptr_not_equal(t.result_cmd, NULL);
+
+ cmp = strcmp(t.result_cmd, result_cmd);
+ flags_correct = (modified == t.modified &&
+ masked == t.masked &&
+ mixed_fallback == t.mixed_fallback);
+
+ if (cmp == 0) {
+ debug_message("[%zu] «%s» «%s» -> «%s»; AS EXPECTED\n",
+ i, t.lp_cmd,
+ t.username,
+ result_cmd);
+ } else {
+ debug_message("[%zu] «%s» «%s»; "
+ "expected [%zu] «%s» got [%zu] «%s»\033[1;31m BAD! \033[0m\n",
+ i, t.lp_cmd,
+ t.username,
+ strlen(t.result_cmd), t.result_cmd,
+ strlen(result_cmd), result_cmd);
+ }
+ assert_int_equal(cmp, 0);
+ if (!flags_correct) {
+ debug_message("[%zu] ", i);
+#define _FLAG(x) debug_message((t. x == x) ? "%s: %s √; ": \
+ "%s \033[1;31m expected %s \033[0m; ", \
+ #x, t.x ? "true": "false");
+ _FLAG(modified);
+ _FLAG(masked);
+ _FLAG(mixed_fallback);
+ debug_message("\n");
+ }
+ assert_int_equal(flags_correct, true);
+ if (mixed_fallback != mixed) {
+ debug_message("[%zu] %s mixed \033[1;31m expected %s \033[0m; ",
+ i, t.lp_cmd,
+ mixed_fallback ? "true": "false");
+ }
+ assert_int_equal(mixed_fallback, mixed);
+#undef _FLAG
+ }
+ debug_message("ALL correct\n");
+}
+
+static void test_talloc_string_sub_unsafe(void **state)
+{
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+
+ static struct cmd_expansion expansions[] = {
+ {
+ "/bin/echo \"bob'",
+ "bob",
+ "/bin/echo \"bob'",
+ false,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "bob",
+ "/bin/echo 'bob'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob",
+ "/bin/echo 'bob'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob'",
+ "/bin/echo 'bob_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob'''",
+ "/bin/echo 'bob___'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob\'",
+ "/bin/echo 'bob_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '%u",
+ "bob bob bob",
+ "/bin/echo 'FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo \"%u\"",
+ " ",
+ "/bin/echo ' '",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob",
+ "/bin/echo \"--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob !0",
+ "/bin/echo \"--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo %u",
+ "!0",
+ "/bin/echo '!0'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob \\",
+ "/bin/echo \"--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "bob >> x",
+ "/bin/echo --uu='bob __ x'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '--uu=%u\"",
+ "bob",
+ "/bin/echo '--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "bob",
+ "/bin/echo --uu='bob'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "bob",
+ "/bin/echo --uu'=FallbackUsername'",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "`ls`",
+ "/bin/echo --uu'=FallbackUsername'",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "u%u%u%u%u",
+ "/bin/echo --uu='u_u_u_u_u'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "$(ls)",
+ "/bin/echo --uu='_(ls)'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "`ls`",
+ "/bin/echo --uu='_ls_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo --uu='1' %u",
+ "`ls`",
+ "/bin/echo --uu='1' FallbackUsername",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo --uu=\"'%u'\"",
+ "bob",
+ "/bin/echo --uu=\"'bob'\"",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo --uu='%u' --yy='%u' '%u' %u",
+ "bob",
+ "/bin/echo --uu='bob' --yy='bob' 'bob' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu=%u%u%u'' %user 50%u",
+ "bob",
+ "/bin/echo --uu=FallbackUsernameFallbackUsernameFallbackUsername'' FallbackUsernameser 50FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo %u",
+ "!!",
+ "/bin/echo '!!'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ ">xxx",
+ "/bin/echo '_xxx'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "3",
+ "/bin/echo '3'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "3$",
+ "/bin/echo '3_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "comp$",
+ "/bin/echo 'comp_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "3$3",
+ "/bin/echo '3_3'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "q $3",
+ "/bin/echo 'q _3'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '%u",
+ "q $3",
+ "/bin/echo 'FallbackUsername",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo -s '%u' %u",
+ "āāā",
+ "/bin/echo -s 'āāā' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -s '%u' %u",
+ "-āāā",
+ "/bin/echo -s '_āāā' FallbackUsername",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo -s %u",
+ "āāā",
+ "/bin/echo -s 'āāā'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo -s %u",
+ "a -a",
+ "/bin/echo -s 'a -a'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo -s=%u %u",
+ "ā -a",
+ "/bin/echo -s='ā -a' 'ā -a'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo -s=\"%u %u\"",
+ "ā -a",
+ "/bin/echo -s=\"FallbackUsername FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -m='fridge' %u",
+ "ā -ß",
+ "/bin/echo -m='fridge' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -m='fridge' %u",
+ "-ā -a",
+ "/bin/echo -m='fridge' FallbackUsername",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo %u",
+ "-n",
+ "/bin/echo '_n'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "o'clock",
+ "/bin/echo 'o_clock'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo \"bob'",
+ "bob",
+ "/bin/echo \"bob'",
+ false,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"%u\"",
+ "%u",
+ "/bin/echo '_u'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo \"$(ls)\"",
+ "%u",
+ "/bin/echo \"$(ls)\"",
+ false,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "\\",
+ "/bin/echo '\\'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "\\",
+ "/bin/echo '\\'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"%u\"",
+ "\\",
+ "/bin/echo '\\'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"%u\" %u",
+ "\\",
+ "/bin/echo '\\' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo '%u' \"%u\" %u",
+ "\\",
+ "/bin/echo '\\' \"FallbackUsername\" FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo '%u' \"%u\"",
+ "bob",
+ "/bin/echo 'bob' \"FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ };
+
+ _test_talloc_string_sub_unsafe(state,
+ expansions,
+ ARRAY_SIZE(expansions),
+ unsafe_characters);
+}
+
+static void test_talloc_string_sub_unsafe_minimal_unsafe_chars(void **state)
+{
+ const char *unsafe_characters = "\"'%";
+
+ static struct cmd_expansion expansions[] = {
+ {
+ "/bin/echo \"bob'",
+ "bob",
+ "/bin/echo \"bob'",
+ false,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "bob",
+ "/bin/echo 'bob'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob",
+ "/bin/echo 'bob'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob'",
+ "/bin/echo 'bob_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob'''",
+ "/bin/echo 'bob___'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "bob\'",
+ "/bin/echo 'bob_'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo '%u",
+ "bob bob bob",
+ "/bin/echo 'FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo \"%u\"",
+ " ",
+ "/bin/echo ' '",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob",
+ "/bin/echo \"--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob !0",
+ "/bin/echo \"--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo %u",
+ "!0",
+ "/bin/echo '!0'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob \\",
+ "/bin/echo \"--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "bob >> x",
+ "/bin/echo --uu='bob >> x'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '--uu=%u\"",
+ "bob",
+ "/bin/echo '--uu=FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "bob",
+ "/bin/echo --uu='bob'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "bob",
+ "/bin/echo --uu'=FallbackUsername'",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "`ls`",
+ "/bin/echo --uu'=FallbackUsername'",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "u%u%u%u%u",
+ "/bin/echo --uu='u_u_u_u_u'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "$(ls)",
+ "/bin/echo --uu='$(ls)'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "`ls`",
+ "/bin/echo --uu='`ls`'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo --uu='1' %u",
+ "`ls`",
+ "/bin/echo --uu='1' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu=\"'%u'\"",
+ "bob",
+ "/bin/echo --uu=\"'bob'\"",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo --uu='%u' --yy='%u' '%u' %u",
+ "bob",
+ "/bin/echo --uu='bob' --yy='bob' 'bob' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo --uu=%u%u%u'' %user 50%u",
+ "bob",
+ "/bin/echo --uu=FallbackUsernameFallbackUsernameFallbackUsername'' FallbackUsernameser 50FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo %u",
+ "!!",
+ "/bin/echo '!!'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ ">xxx",
+ "/bin/echo '>xxx'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "3",
+ "/bin/echo '3'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "3$",
+ "/bin/echo '3$'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "comp$",
+ "/bin/echo 'comp$'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "3$3",
+ "/bin/echo '3$3'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "q $3",
+ "/bin/echo 'q $3'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u",
+ "q $3",
+ "/bin/echo 'FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -s '%u' %u",
+ "āāā",
+ "/bin/echo -s 'āāā' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -s '%u' %u",
+ "-āāā",
+ "/bin/echo -s '_āāā' FallbackUsername",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo -s %u",
+ "āāā",
+ "/bin/echo -s 'āāā'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo -s %u",
+ "a -a",
+ "/bin/echo -s 'a -a'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo -s=%u %u",
+ "ā -a",
+ "/bin/echo -s='ā -a' 'ā -a'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo -s=\"%u %u\"",
+ "ā -a",
+ "/bin/echo -s=\"FallbackUsername FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -m='fridge' %u",
+ "ā -ß",
+ "/bin/echo -m='fridge' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo -m='fridge' %u",
+ "-ā -a",
+ "/bin/echo -m='fridge' FallbackUsername",
+ true,
+ true,
+ true,
+ },
+ {
+ "/bin/echo %u",
+ "-n",
+ "/bin/echo '_n'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "o'clock",
+ "/bin/echo 'o_clock'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo \"bob'",
+ "bob",
+ "/bin/echo \"bob'",
+ false,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"%u\"",
+ "%u",
+ "/bin/echo '_u'",
+ true,
+ true,
+ false,
+ },
+ {
+ "/bin/echo \"$(ls)\"",
+ "%u",
+ "/bin/echo \"$(ls)\"",
+ false,
+ false,
+ false,
+ },
+ {
+ "/bin/echo %u",
+ "\\",
+ "/bin/echo '\\'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo '%u'",
+ "\\",
+ "/bin/echo '\\'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"%u\"",
+ "\\",
+ "/bin/echo '\\'",
+ true,
+ false,
+ false,
+ },
+ {
+ "/bin/echo \"%u\" %u",
+ "\\",
+ "/bin/echo '\\' FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo '%u' \"%u\" %u",
+ "\\",
+ "/bin/echo '\\' \"FallbackUsername\" FallbackUsername",
+ true,
+ false,
+ true,
+ },
+ {
+ "/bin/echo '%u' \"%u\"",
+ "bob",
+ "/bin/echo 'bob' \"FallbackUsername\"",
+ true,
+ false,
+ true,
+ },
+ };
+
+ _test_talloc_string_sub_unsafe(state,
+ expansions,
+ ARRAY_SIZE(expansions),
+ unsafe_characters);
+}
+
+static void test_talloc_string_sub_unsafe_all_mixes(void **state)
+{
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
+ size_t i;
+
+ for (i = 0; i < 32; i++) {
+ char in[100] = { 0, };
+ char out[100] = { 0, };
+ struct cmd_expansion expansions[] = {
+ {
+ in,
+ "bob",
+ out,
+ true,
+ false,
+ false,
+ },
+ };
+ bool vsq = i & 1;
+ bool vdq = i & 2;
+ bool v = i & 4;
+ bool sq = i & 8;
+ bool dq = i & 16;
+ char *inp = in;
+ char *outp = out;
+ if (vsq) {
+ inp = stpcpy(inp, "'%u' ");
+ outp = stpcpy(outp, "'bob' ");
+ debug_message("vsq ");
+ }
+ if (vdq) {
+ inp = stpcpy(inp, "\"%u\" ");
+ outp = stpcpy(outp, (vsq || sq) ? "\"FallbackUsername\" " : "'bob' ");
+ debug_message("vdq ");
+ if (vsq || sq) {
+ expansions[0].mixed_fallback = true;
+ }
+ }
+ if (v) {
+ inp = stpcpy(inp, "%u ");
+ outp = stpcpy(outp, (vsq || vdq || sq || dq) ? "FallbackUsername " : "'bob' ");
+ debug_message("v ");
+ if (vsq || vdq || sq || dq) {
+ expansions[0].mixed_fallback = true;
+ }
+ }
+ if (sq) {
+ inp = stpcpy(inp, "' ");
+ outp = stpcpy(outp, "' ");
+ debug_message("sq ");
+ }
+ if (dq) {
+ inp = stpcpy(inp, "\" ");
+ outp = stpcpy(outp, "\" ");
+ debug_message("dq ");
+ }
+ debug_message("(i: %zu)\n", i);
+ *inp = '\0';
+ *outp = '\0';
+ expansions[0].modified = strcmp(in, out) != 0;
+
+ _test_talloc_string_sub_unsafe(state,
+ expansions,
+ ARRAY_SIZE(expansions),
+ unsafe_characters);
+ }
+}
+
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_talloc_string_sub_unsafe),
+ cmocka_unit_test(test_talloc_string_sub_unsafe_minimal_unsafe_chars),
+ cmocka_unit_test(test_talloc_string_sub_unsafe_all_mixes),
+ };
+ if (!isatty(1)) {
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ }
+ return cmocka_run_group_tests(tests,
+ setup_talloc_context,
+ teardown_talloc_context);
+}
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index 9dff0e8925d..c9c04f1aaed 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -420,3 +420,9 @@ else:
deps='cmocka replace talloc stable_sort',
local_include=False,
for_selftest=True)
+
+ bld.SAMBA3_BINARY('test_string_sub',
+ source='tests/test_string_sub.c',
+ deps='''cmocka replace talloc samba-util
+ ''',
+ for_selftest=True)
diff --git a/selftest/tests.py b/selftest/tests.py
index 104fa65f672..c92676f66eb 100644
--- a/selftest/tests.py
+++ b/selftest/tests.py
@@ -569,6 +569,8 @@ plantestsuite("samba.unittests.sys_rw", "none",
[os.path.join(bindir(), "default/lib/util/test_sys_rw")])
plantestsuite("samba.unittests.stable_sort", "none",
[os.path.join(bindir(), "default/lib/util/test_stable_sort")])
+plantestsuite("samba.unittests.test_string_sub", "none",
+ [os.path.join(bindir(), "test_string_sub")])
plantestsuite("samba.unittests.ntlm_check", "none",
[os.path.join(bindir(), "default/libcli/auth/test_ntlm_check")])
plantestsuite("samba.unittests.gnutls", "none",
--
2.53.0
From 8ee5805b49b6f5f413e627c8b19d10559bbb2aa2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Sun, 15 Mar 2026 19:15:14 +0100
Subject: [PATCH 55/66] CVE-2026-4480: s3:printing: mask and/or single quote
jobname passed as %J to "print command"
Fix an unauthenticated remote code execution vulnerability with
printing set to anything *but* cups and iprint, for example "lprng",
so that "print command" is executed upon job submission. If the
client-controlled job name is handed to the "print command" via %J,
rpcd_spoolssd passes this to the shell without escaping critical
characters.
Using single quotes (directly) around %J, '%J' would avoid the
problem, we now try to autodetect if we can use '%J' implicitly
or we fallback to a fixed "__CVE-2026-4480_FallbackJobname__"
string instead of the client provided jobname.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/printing/print_generic.c | 107 +++++++++++++++++++++++++++----
1 file changed, 94 insertions(+), 13 deletions(-)
diff --git a/source3/printing/print_generic.c b/source3/printing/print_generic.c
index 7c7a14de045..2f642af3f4b 100644
--- a/source3/printing/print_generic.c
+++ b/source3/printing/print_generic.c
@@ -19,6 +19,7 @@
#include "includes.h"
#include "lib/util/util_file.h"
+#include "lib/util/util_str_escape.h"
#include "printing.h"
#include "smbd/proto.h"
#include "source3/lib/substitute.h"
@@ -207,6 +208,52 @@ static int generic_queue_get(const char *printer_name,
return qcount;
}
+static const char *replace_print_cmd_J(TALLOC_CTX *mem_ctx,
+ const char *orig_cmd,
+ const char *unsafe_jobname,
+ const char *fallback_jobname)
+{
+ char *cmd = NULL;
+ bool modified = false;
+ bool masked = false;
+ bool mixed_fallback = false;
+
+ /*
+ * This replaces unsafe characters with '_'.
+ * We also mask forward and backslash here.
+ *
+ * Then it replaces %J with an single quoted
+ * version of the masked jobname or it falls
+ * back to fallback_jobname is the print command
+ * uses strange mixed quoting.
+ */
+
+#define JOBNAME_UNSAFE_CHARACTERS \
+ STRING_SUB_UNSAFE_CHARACTERS "/\\"
+
+ cmd = talloc_string_sub_unsafe(mem_ctx,
+ orig_cmd,
+ 'J',
+ unsafe_jobname,
+ JOBNAME_UNSAFE_CHARACTERS,
+ '_',
+ fallback_jobname,
+ &modified,
+ &masked,
+ &mixed_fallback);
+ if (cmd == NULL) {
+ return NULL;
+ }
+
+ /*
+ * The caller already checked talloc_string_sub_mixed_quoting()
+ * and warned the admin, so we don't check mixed_fallback
+ * here
+ */
+
+ return cmd;
+}
+
/****************************************************************************
Submit a file for printing - called from print_job_end()
****************************************************************************/
@@ -222,11 +269,12 @@ static int generic_job_submit(int snum, struct printjob *pjob,
char *print_directory = NULL;
char *wd = NULL;
char *p = NULL;
- char *jobname = NULL;
+ const char *print_cmd = NULL;
TALLOC_CTX *ctx = talloc_tos();
fstring job_page_count, job_size;
print_queue_struct *q = NULL;
print_status_struct status;
+ const char *jobname = "No Document Name";
/* we print from the directory path to give the best chance of
parsing the lpq output */
@@ -255,24 +303,48 @@ static int generic_job_submit(int snum, struct printjob *pjob,
return -1;
}
- jobname = talloc_strdup(ctx, pjob->jobname);
- if (!jobname) {
- ret = -1;
- goto out;
+ if (pjob->jobname[0] != '\0') {
+ jobname = pjob->jobname;
}
- jobname = talloc_string_sub(ctx, jobname, "'", "_");
- if (!jobname) {
- ret = -1;
- goto out;
+
+ print_cmd = lp_print_command(snum);
+ if (print_cmd != NULL) {
+ const char *invalid_jobname = "__CVE-2026-4480_FallbackJobname__";
+
+ if (talloc_string_sub_mixed_quoting(print_cmd, 'J')) {
+ /*
+ * The admin used a strange mixture of
+ * single and double quotes, fallback
+ * to InvalidDocumentName and warn about
+ * it, so that the admin can adjust to
+ * the use single quotes directly around %J,
+ * e.g. '%J'.
+ */
+ jobname = invalid_jobname;
+ D_WARNING("CVE-2026-4480: printer %s "
+ "strange quoting in 'print command', "
+ "falling back to jobname=%s, "
+ "use testparm to fix the configuration\n",
+ lp_printername(talloc_tos(), lp_sub, snum),
+ invalid_jobname);
+ }
+
+ print_cmd = replace_print_cmd_J(ctx,
+ print_cmd,
+ jobname,
+ invalid_jobname);
+ if (!print_cmd) {
+ ret = -1;
+ goto out;
+ }
}
fstr_sprintf(job_page_count, "%d", pjob->page_count);
fstr_sprintf(job_size, "%zu", pjob->size);
/* send it to the system spooler */
ret = print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
- lp_print_command(snum), NULL,
+ print_cmd, NULL,
"%s", p,
- "%J", jobname,
"%f", p,
"%z", job_size,
"%c", job_page_count,
@@ -293,17 +365,26 @@ static int generic_job_submit(int snum, struct printjob *pjob,
int i;
for (i = 0; i < ret; i++) {
if (strcmp(q[i].fs_file, p) == 0) {
+ char *le_jobname =
+ log_escape(talloc_tos(), jobname);
+
pjob->sysjob = q[i].sysjob;
DEBUG(5, ("new job %u (%s) matches sysjob %d\n",
- pjob->jobid, jobname, pjob->sysjob));
+ pjob->jobid, le_jobname, pjob->sysjob));
+
+ TALLOC_FREE(le_jobname);
break;
}
}
ret = 0;
}
if (pjob->sysjob == -1) {
+ char *le_jobname = log_escape(talloc_tos(), jobname);
+
DEBUG(2, ("failed to get sysjob for job %u (%s), tracking as "
- "Unix job\n", pjob->jobid, jobname));
+ "Unix job\n", pjob->jobid, le_jobname));
+
+ TALLOC_FREE(le_jobname);
}
--
2.53.0
From 27a1372c85d05f02fd1e7f975798314b95ee75d3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Fri, 8 May 2026 23:27:35 +0200
Subject: [PATCH 56/66] CVE-2026-4480: s3:testparm: warn about 'print command'
%J usage
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/utils/testparm.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c
index 306924ac7c8..c758313d466 100644
--- a/source3/utils/testparm.c
+++ b/source3/utils/testparm.c
@@ -928,6 +928,14 @@ static void do_per_share_checks(int s)
"parameter is ignored when using CUPS libraries.\n\n",
lp_servicename(talloc_tos(), lp_sub, s));
}
+ if (talloc_string_sub_mixed_quoting(lp_print_command(s), 'J')) {
+ fprintf(stderr,
+ "WARNING: Service %s defines a 'print command' "
+ "with mixed quoting and %%J.\n"
+ "CVE-2026-4480 changed the way %%J substitution works.\n"
+ "You should use single quotes (directly) around '%%J'.\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
vfs_objects = lp_vfs_objects(s);
if (vfs_objects && str_list_check(vfs_objects, "fruit")) {
--
2.53.0
From 498d3a2eba50e0d4eff50c99cd54a8a9af4e7639 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Mon, 11 May 2026 14:11:34 +0200
Subject: [PATCH 57/66] CVE-2026-4480: docs-xml/smbdotconf: clarify '%J' in
'print command'
Admins should use '%J'.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
docs-xml/smbdotconf/printing/printcommand.xml | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/docs-xml/smbdotconf/printing/printcommand.xml b/docs-xml/smbdotconf/printing/printcommand.xml
index c84e45f404d..d708287932a 100644
--- a/docs-xml/smbdotconf/printing/printcommand.xml
+++ b/docs-xml/smbdotconf/printing/printcommand.xml
@@ -21,8 +21,11 @@
<para>%p - the appropriate printer
name</para>
- <para>%J - the job
- name as transmitted by the client.</para>
+ <para>%J - the job name as transmitted by the client,
+ but with dangerous characters being replaced by _.
+ You should use single quotes (directly) around %J, e.g. '%J',
+ see CVE-2026-4480 for more details.
+ </para>
<para>%c - The number of printed pages
of the spooled job (if known).</para>
--
2.53.0
From 47e15af9f5d77208473b29bac5422665b79191f8 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:56:21 +0200
Subject: [PATCH 58/66] CVE-2026-4408: lib/util: introduce
strstr_for_invalid_account_characters()
This splits out the logic from samaccountname_bad_chars_check()
in source4/dsdb/samdb/ldb_modules/samldb.c, this will be used
in other places soon.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
lib/util/samba_util.h | 9 +++++++++
lib/util/util_str.c | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/lib/util/samba_util.h b/lib/util/samba_util.h
index 03dee5c6137..ea741b51c58 100644
--- a/lib/util/samba_util.h
+++ b/lib/util/samba_util.h
@@ -303,6 +303,15 @@ _PUBLIC_ bool set_boolean(const char *boolean_string, bool *boolean);
*/
_PUBLIC_ bool conv_str_bool(const char * str, bool * val);
+/**
+ * Returns a pointer to the first invalid character in name.
+ *
+ * Passing a NULL pointer as name is not allowed!
+ *
+ * This returns NULL for a valid account name.
+ **/
+_PUBLIC_ const char *strstr_for_invalid_account_characters(const char *name);
+
/**
* Convert a size specification like 16K into an integral number of bytes.
**/
diff --git a/lib/util/util_str.c b/lib/util/util_str.c
index 19acff4a983..c5987461fe6 100644
--- a/lib/util/util_str.c
+++ b/lib/util/util_str.c
@@ -267,3 +267,41 @@ _PUBLIC_ bool set_boolean(const char *boolean_string, bool *boolean)
}
return false;
}
+
+_PUBLIC_ const char *strstr_for_invalid_account_characters(const char *name)
+{
+ /*
+ * Return a pointer to the first invalid character in the
+ * sAMAccountName, or NULL if the whole name is valid.
+ *
+ * The rules here are based on
+ *
+ * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx
+ */
+ size_t i;
+
+ for (i = 0; name[i] != '\0'; i++) {
+ uint8_t c = name[i];
+ const char *p = NULL;
+
+ if (iscntrl(c)) {
+ return &name[i];
+ }
+
+ p = strchr("\"[]:;|=+*?<>/\\,", c);
+ if (p != NULL) {
+ return &name[i];
+ }
+ }
+
+ if (i == 0) {
+ return &name[i];
+ }
+
+ if (name[i - 1] == '.') {
+ i -= 1;
+ return &name[i];
+ }
+
+ return NULL;
+}
--
2.53.0
From cd1dc2df42eda36bf66e8d7d6f69e365e5d7d8a3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Mon, 11 May 2026 20:21:36 +0200
Subject: [PATCH 59/66] CVE-2026-4408: s3:samr-server: only allow
_samr_ValidatePassword as DC
This is only supported with 'rpc start on demand helpers = no',
as it needs ncacn_ip_tcp, but we better also restrict it to DCs.
Maybe only FreeIPA needs it as NT4 didn't support ncacn_ip_tcp.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/rpc_server/samr/srv_samr_nt.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/source3/rpc_server/samr/srv_samr_nt.c b/source3/rpc_server/samr/srv_samr_nt.c
index e0d0875bd5d..3937dbe3f32 100644
--- a/source3/rpc_server/samr/srv_samr_nt.c
+++ b/source3/rpc_server/samr/srv_samr_nt.c
@@ -7500,6 +7500,14 @@ NTSTATUS _samr_ValidatePassword(struct pipes_struct *p,
return NT_STATUS_ACCESS_DENIED;
}
+ if (lp_server_role() <= ROLE_DOMAIN_MEMBER) {
+ /*
+ * We only want this on DCs
+ */
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
if (r->in.level < 1 || r->in.level > 3) {
return NT_STATUS_INVALID_INFO_CLASS;
}
--
2.53.0
From 77c8330fb3b01c3d23f9aac611397334ef6f0007 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Wed, 18 Mar 2026 12:24:47 +0100
Subject: [PATCH 60/66] CVE-2026-4408: s3:samr-server: deny, mask and/or single
quote username to 'check password script'
We pass this on to the check password script, prevent remote command
execution.
We now try to autodetect if we could implicitly use '%u' for the
replacement and fallback to a fixed fallback username.
Admins should make use of SAMBA_CPS_ACCOUNT_NAME
instead of passing '%u' to 'check password script'
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Pair-Programmed-With: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/rpc_server/samr/srv_samr_chgpasswd.c | 110 +++++++++++++++++--
1 file changed, 101 insertions(+), 9 deletions(-)
diff --git a/source3/rpc_server/samr/srv_samr_chgpasswd.c b/source3/rpc_server/samr/srv_samr_chgpasswd.c
index 6c0c0da0cfc..9afb8799aea 100644
--- a/source3/rpc_server/samr/srv_samr_chgpasswd.c
+++ b/source3/rpc_server/samr/srv_samr_chgpasswd.c
@@ -54,6 +54,7 @@
#include "passdb.h"
#include "auth.h"
#include "lib/util/sys_rw.h"
+#include "lib/util/util_str_escape.h"
#include "librpc/rpc/dcerpc_samr.h"
#include "lib/crypto/gnutls_helpers.h"
@@ -1008,27 +1009,118 @@ static bool check_passwd_history(struct samu *sampass, const char *plaintext)
/***********************************************************
************************************************************/
+static NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
+ const char *orig_cmd,
+ const char *username,
+ char **cmd_out)
+{
+ const char *fallback_username = "__CVE-2026-4408_FallbackUsername__";
+ const char *inv = NULL;
+ char *cmd = NULL;
+ bool modified = false;
+ bool masked = false;
+ bool mixed_fallback = false;
+
+ *cmd_out = NULL;
+
+ if (username == NULL) {
+ return NT_STATUS_INVALID_USER_PRINCIPAL_NAME;
+ }
+
+ /*
+ * This catches invalid characters in account names
+ * which might be problematic passing to a shell script.
+ */
+ inv = strstr_for_invalid_account_characters(username);
+ if (inv != NULL) {
+ char *le_username = log_escape(tosctx, username);
+
+ DBG_WARNING("username '%s' has invalid or dangerous characters\n",
+ le_username);
+
+ TALLOC_FREE(le_username);
+
+ return NT_STATUS_INVALID_USER_PRINCIPAL_NAME;
+ }
+
+ /*
+ * This masks the remaining unsafe characters which
+ * are not already caught by strstr_for_invalid_account_characters()
+ * with '_'.
+ *
+ * Then it replaces %u with an single quoted
+ * and/or shell escaped version of the masked username.
+ */
+ cmd = talloc_string_sub_unsafe(tosctx,
+ orig_cmd,
+ 'u',
+ username,
+ STRING_SUB_UNSAFE_CHARACTERS,
+ '_',
+ fallback_username,
+ &modified,
+ &masked,
+ &mixed_fallback);
+ if (cmd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Now warn about unexpected values
+ */
+
+ if (mixed_fallback) {
+ D_WARNING("CVE-2026-4408: "
+ "strange quoting in 'check password script', "
+ "falling back to replace %%u with %s, "
+ "use testparm to fix the configuration\n",
+ fallback_username);
+ D_WARNING("CVE-2026-4408: "
+ "You should use '%%u', or SAMBA_CPS_ACCOUNT_NAME "
+ "inside of 'check password script'.\n");
+ } else if (masked) {
+ char *le_username = log_escape(tosctx, username);
+
+ D_WARNING("CVE-2026-4408: "
+ "replaced %%u with masked value instead of: %s\n",
+ le_username);
+ D_WARNING("CVE-2026-4408: "
+ "You should use SAMBA_CPS_ACCOUNT_NAME inside "
+ "'check password script' instead of %%u.\n");
+
+ TALLOC_FREE(le_username);
+ }
+
+ *cmd_out = cmd;
+ return NT_STATUS_OK;
+}
+
+
NTSTATUS check_password_complexity(const char *username,
const char *fullname,
const char *password,
enum samPwdChangeReason *samr_reject_reason)
{
+ int check_ret;
+ NTSTATUS status;
TALLOC_CTX *tosctx = talloc_tos();
const struct loadparm_substitution *lp_sub =
loadparm_s3_global_substitution();
- int check_ret;
- char *cmd;
+ const char *orig_cmd = NULL;
+ char *cmd = NULL;
- /* Use external script to check password complexity */
- if ((lp_check_password_script(tosctx, lp_sub) == NULL)
- || (*(lp_check_password_script(tosctx, lp_sub)) == '\0')){
+ orig_cmd = lp_check_password_script(tosctx, lp_sub);
+ if (orig_cmd == NULL || orig_cmd[0] == '\0') {
return NT_STATUS_OK;
}
- cmd = talloc_string_sub(tosctx, lp_check_password_script(tosctx, lp_sub), "%u",
- username);
- if (!cmd) {
- return NT_STATUS_PASSWORD_RESTRICTION;
+ /* note we don't use 'fullname' or 'password' here */
+ status = check_password_complexity_internal(tosctx,
+ orig_cmd,
+ username,
+ &cmd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
check_ret = setenv("SAMBA_CPS_ACCOUNT_NAME", username, 1);
--
2.53.0
From 5f898b13ad4361966c8d92a40f50b1a3c6a093ba Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Sat, 2 May 2026 22:12:38 +1200
Subject: [PATCH 61/66] CVE-2026-4408: s3:samr-server: make
check_password_complexity_internal() non-static, for easier testing
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/rpc_server/samr/srv_samr_chgpasswd.c | 8 ++++----
source3/rpc_server/samr/srv_samr_util.h | 5 +++++
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/source3/rpc_server/samr/srv_samr_chgpasswd.c b/source3/rpc_server/samr/srv_samr_chgpasswd.c
index 9afb8799aea..3f48da47a5b 100644
--- a/source3/rpc_server/samr/srv_samr_chgpasswd.c
+++ b/source3/rpc_server/samr/srv_samr_chgpasswd.c
@@ -1009,10 +1009,10 @@ static bool check_passwd_history(struct samu *sampass, const char *plaintext)
/***********************************************************
************************************************************/
-static NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
- const char *orig_cmd,
- const char *username,
- char **cmd_out)
+NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
+ const char *orig_cmd,
+ const char *username,
+ char **cmd_out)
{
const char *fallback_username = "__CVE-2026-4408_FallbackUsername__";
const char *inv = NULL;
diff --git a/source3/rpc_server/samr/srv_samr_util.h b/source3/rpc_server/samr/srv_samr_util.h
index 5e839ac77c0..a3a22012858 100644
--- a/source3/rpc_server/samr/srv_samr_util.h
+++ b/source3/rpc_server/samr/srv_samr_util.h
@@ -79,6 +79,11 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
uchar password_encrypted_with_nt_hash[516],
const uchar old_nt_hash_encrypted[16],
enum samPwdChangeReason *reject_reason);
+
+NTSTATUS check_password_complexity_internal(TALLOC_CTX *mem_ctx,
+ const char *_orig_cmd,
+ const char *username,
+ char **cmd_out);
NTSTATUS check_password_complexity(const char *username,
const char *fullname,
const char *password,
--
2.53.0
From 1054c2ffdc707cee9778dbc0ff4083ae60d9b359 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Sat, 2 May 2026 22:14:43 +1200
Subject: [PATCH 62/66] CVE-2026-4408: s3:torture: tests for password
complexity scripts
This tries to demonstrate the new logic for %u in
'check password script'.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
selftest/tests.py | 2 +
source3/torture/test_rpc_samr.c | 358 ++++++++++++++++++++++++++++++++
source3/torture/wscript_build | 6 +
3 files changed, 366 insertions(+)
create mode 100644 source3/torture/test_rpc_samr.c
diff --git a/selftest/tests.py b/selftest/tests.py
index c92676f66eb..84a4baa6e19 100644
--- a/selftest/tests.py
+++ b/selftest/tests.py
@@ -585,6 +585,8 @@ plantestsuite("samba.unittests.test_oLschema2ldif", "none",
[os.path.join(bindir(), "default/source4/utils/oLschema2ldif/test_oLschema2ldif")])
plantestsuite("samba.unittests.auth.sam", "none",
[os.path.join(bindir(), "test_auth_sam")])
+plantestsuite("samba.unittests.test_rpc_samr", "none",
+ [os.path.join(bindir(), "test_rpc_samr")])
if have_heimdal_support and not using_system_gssapi:
plantestsuite("samba.unittests.auth.heimdal_gensec_unwrap_des", "none",
[valgrindify(os.path.join(bindir(), "test_heimdal_gensec_unwrap_des"))])
diff --git a/source3/torture/test_rpc_samr.c b/source3/torture/test_rpc_samr.c
new file mode 100644
index 00000000000..8d4f3985246
--- /dev/null
+++ b/source3/torture/test_rpc_samr.c
@@ -0,0 +1,358 @@
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <cmocka.h>
+#include "includes.h"
+#include "talloc.h"
+#include "libcli/util/ntstatus.h"
+#include "../librpc/gen_ndr/samr.h"
+#include "rpc_server/samr/srv_samr_util.h"
+
+/* set SAMR_DEBUG_VERBOSE to true to print more. */
+#define SAMR_DEBUG_VERBOSE true
+
+#if SAMR_DEBUG_VERBOSE
+#define debug_message(...) print_message(__VA_ARGS__)
+#else
+#define debug_message(...) /* debug_message */
+#endif
+
+static int setup_talloc_context(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ *state = mem_ctx;
+ return 0;
+}
+
+static int teardown_talloc_context(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ TALLOC_FREE(mem_ctx);
+ return 0;
+}
+
+struct cmd_expansion {
+ const char *lp_cmd;
+ const char *username;
+ const char *result_cmd;
+ NTSTATUS result_code;
+};
+
+static struct cmd_expansion expansions[] = {
+ {
+ "/bin/echo '%u'",
+ "bob",
+ "/bin/echo 'bob'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "bob",
+ "/bin/echo 'bob'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "bob'",
+ "/bin/echo 'bob_'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "bob\'",
+ "/bin/echo 'bob_'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "bob'''",
+ "/bin/echo 'bob___'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "bob*",
+ NULL,
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
+ },
+ {
+ "/bin/echo %u",
+ "bob\"",
+ NULL,
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
+ },
+ {
+ "/bin/echo '%u",
+ "bob bob bob",
+ "/bin/echo '__CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo \"%u\"",
+ " ",
+ "/bin/echo ' '",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob",
+ "/bin/echo \"--uu=__CVE-2026-4408_FallbackUsername__\"",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob !0",
+ "/bin/echo \"--uu=__CVE-2026-4408_FallbackUsername__\"",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "!0",
+ "/bin/echo '!0'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo \"--uu=%u\"",
+ "bob \\",
+ NULL,
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "bob >> x",
+ NULL,
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
+ },
+ {
+ "/bin/echo '--uu=%u\"",
+ "bob",
+ "/bin/echo '--uu=__CVE-2026-4408_FallbackUsername__\"",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "bob",
+ "/bin/echo --uu='bob'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "bob",
+ "/bin/echo --uu'=__CVE-2026-4408_FallbackUsername__'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "`ls`",
+ "/bin/echo --uu'=__CVE-2026-4408_FallbackUsername__'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu'=%u'",
+ "$(ls)",
+ "/bin/echo --uu'=__CVE-2026-4408_FallbackUsername__'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu='%u'",
+ "$(ls)",
+ "/bin/echo --uu='_(ls)'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu=\"'%u'\"",
+ "bob",
+ "/bin/echo --uu=\"'bob'\"",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu='%u' --yy='%u' '%u' %u",
+ "bob",
+ "/bin/echo --uu='bob' --yy='bob' 'bob' __CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo --uu=%u%u'' %user 50%u",
+ "bob",
+ "/bin/echo --uu=__CVE-2026-4408_FallbackUsername____CVE-2026-4408_FallbackUsername__'' __CVE-2026-4408_FallbackUsername__ser 50__CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "!!",
+ "/bin/echo '!!'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ ">xxx",
+ NULL,
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
+ },
+ {
+ "/bin/echo %u",
+ "\\",
+ NULL,
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
+ },
+ {
+ "/bin/echo %u",
+ "3",
+ "/bin/echo '3'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo '%u'",
+ "3$",
+ "/bin/echo '3_'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo '%u'",
+ "comp$",
+ "/bin/echo 'comp_'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo '%u'",
+ "3$3",
+ "/bin/echo '3_3'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo '%u'",
+ "q $3",
+ "/bin/echo 'q _3'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -s '%u' %u",
+ "āāā",
+ "/bin/echo -s 'āāā' __CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -s '%u' %u",
+ "-āāā",
+ "/bin/echo -s '_āāā' __CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -s %u",
+ "āāā",
+ "/bin/echo -s 'āāā'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -s %u",
+ "a -a",
+ "/bin/echo -s 'a -a'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -s=%u %u",
+ "ā -a",
+ "/bin/echo -s='ā -a' 'ā -a'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -s=\"%u %u\"",
+ "ā -a",
+ "/bin/echo -s=\"__CVE-2026-4408_FallbackUsername__ __CVE-2026-4408_FallbackUsername__\"",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -m='fridge' %u",
+ "ā -x -ß",
+ "/bin/echo -m='fridge' __CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo -m='fridge' %u",
+ "-ā -a",
+ "/bin/echo -m='fridge' __CVE-2026-4408_FallbackUsername__",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "-n",
+ "/bin/echo '_n'",
+ NT_STATUS_OK
+ },
+ {
+ "/bin/echo %u",
+ "o'clock",
+ "/bin/echo 'o_clock'",
+ NT_STATUS_OK
+ },
+};
+
+static void test_expansions(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(expansions); i++) {
+ struct cmd_expansion t = expansions[i];
+ char *result_cmd = NULL;
+ NTSTATUS status;
+
+ status = check_password_complexity_internal(mem_ctx,
+ t.lp_cmd,
+ t.username,
+ &result_cmd);
+ if (NT_STATUS_IS_OK(t.result_code) && NT_STATUS_IS_OK(status)) {
+ int cmp;
+
+ cmp = strcmp(t.result_cmd, result_cmd);
+ if (cmp == 0) {
+ debug_message("[%zu] «%s» «%s» -> «%s», nstatus %s; AS EXPECTED\n",
+ i, t.lp_cmd,
+ t.username,
+ result_cmd,
+ nt_errstr(status));
+ } else {
+ debug_message("[%zu] «%s» «%s», nstatus %s; "
+ "expected «%s» got «%s»\033[1;31m BAD! \033[0m\n",
+ i, t.lp_cmd,
+ t.username,
+ nt_errstr(status),
+ t.result_cmd,
+ result_cmd);
+ }
+ assert_int_equal(cmp, 0);
+ } else if (NT_STATUS_EQUAL(status, t.result_code)) {
+ debug_message("[%zu] «%s» «%s», nstatus %s FAILED AS EXPECTED\n",
+ i, t.lp_cmd,
+ t.username,
+ nt_errstr(status));
+ } else {
+ debug_message("[%zu] «%s» «%s» -> «%s», nstatus %s; "
+ "EXPECTED result «%s» ntstatus %s; \033[1;31m BAD! \033[0m\n",
+ i, t.lp_cmd,
+ t.username,
+ result_cmd,
+ nt_errstr(status),
+ t.result_cmd,
+ nt_errstr(t.result_code));
+ assert_int_equal(true, false);
+ }
+ }
+ debug_message("ALL correct\n");
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_expansions),
+ };
+ if (!isatty(1)) {
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ }
+ return cmocka_run_group_tests(tests,
+ setup_talloc_context,
+ teardown_talloc_context);
+}
diff --git a/source3/torture/wscript_build b/source3/torture/wscript_build
index 1d2520099e3..d04008b3df1 100644
--- a/source3/torture/wscript_build
+++ b/source3/torture/wscript_build
@@ -133,3 +133,9 @@ bld.SAMBA3_BINARY('vfstest',
SMBREADLINE
''',
for_selftest=True)
+
+bld.SAMBA3_BINARY('test_rpc_samr',
+ source='test_rpc_samr.c',
+ deps='''RPC_SERVICE cmocka
+ ''',
+ for_selftest=True)
--
2.53.0
From cd0f499a2478ce5676aa1aefe378d50f11026517 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Fri, 8 May 2026 23:27:35 +0200
Subject: [PATCH 63/66] CVE-2026-4408: s3:testparm: warn about 'check password
script' %u usage
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
source3/utils/testparm.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c
index c758313d466..49dc39b2cab 100644
--- a/source3/utils/testparm.c
+++ b/source3/utils/testparm.c
@@ -359,6 +359,7 @@ static int do_global_checks(void)
const char **lp_ptr = NULL;
const struct loadparm_substitution *lp_sub =
loadparm_s3_global_substitution();
+ const char *check_pw_script = NULL;
int ival;
fprintf(stderr, "\n");
@@ -831,6 +832,17 @@ static int do_global_checks(void)
#endif
}
+ check_pw_script = lp_check_password_script(talloc_tos(), lp_sub);
+ if (talloc_string_sub_mixed_quoting(check_pw_script, 'u')) {
+ fprintf(stderr,
+ "WARNING: You are using 'check password script' "
+ "with mixed quoting and %%u.\n"
+ "CVE-2026-4408 changed the way %%u substitution works. \n"
+ "You should use the SAMBA_CPS_ACCOUNT_NAME "
+ "environment variable exported to the script, or\n"
+ "at least use single quotes (directly) around '%%u'.\n\n");
+ }
+
return ret;
}
--
2.53.0
From 7c6d55ab1924e93a7c67ab563a7162260cf394eb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Mon, 11 May 2026 13:52:52 +0200
Subject: [PATCH 64/66] CVE-2026-4408: docs-xml/smbdotconf: clarify '%u' in
'check password script'
Admins should use SAMBA_CPS_ACCOUNT_NAME.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
docs-xml/smbdotconf/security/checkpasswordscript.xml | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/docs-xml/smbdotconf/security/checkpasswordscript.xml b/docs-xml/smbdotconf/security/checkpasswordscript.xml
index 18aa2c6d290..dd162d89f08 100644
--- a/docs-xml/smbdotconf/security/checkpasswordscript.xml
+++ b/docs-xml/smbdotconf/security/checkpasswordscript.xml
@@ -20,8 +20,8 @@
<itemizedlist>
<listitem><para>
- SAMBA_CPS_ACCOUNT_NAME is always present and contains the sAMAccountName of user,
- the is the same as the %u substitutions in the none AD DC case.
+ SAMBA_CPS_ACCOUNT_NAME is always present and contains the sAMAccountName of user.
+ It is the same as the '%u' substitutions in the non AD DC case.
</para></listitem>
<listitem><para>
@@ -33,6 +33,12 @@
</para></listitem>
</itemizedlist>
+ <para>Even on a non AD DC SAMBA_CPS_ACCOUNT_NAME is the preferred way to access the
+ account name, as it contains the raw value provided by the client. If that's not
+ possible you should use single quotes (directly) around %u, e.g. /path/to/somescript '%u',
+ see CVE-2026-4408 for more details.
+ </para>
+
<para>Note: In the example directory is a sample program called <command moreinfo="none">crackcheck</command>
that uses cracklib to check the password quality.</para>
--
2.53.0
From fbf23d3f6e19dea6331c446e80cebf3018c7e332 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Fri, 17 Apr 2026 10:45:58 +0200
Subject: [PATCH 65/66] third_party/ngtcp2: import v1.22.1 for CVE-2026-40170
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
For CVE-2026-40170 see:
https://github.com/ngtcp2/ngtcp2/security/advisories/GHSA-f523-465f-8c8f
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16059
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit 706dc118b3bdbe03ada53939c1634ab13c91455a)
Autobuild-User(v4-23-test): Björn Jacke <bjacke@samba.org>
Autobuild-Date(v4-23-test): Mon Apr 27 12:59:57 UTC 2026 on atb-devel-224
---
third_party/ngtcp2/crypto/CMakeLists.txt | 6 +
third_party/ngtcp2/crypto/Makefile.am | 4 +
.../ngtcp2/crypto/boringssl/boringssl.c | 31 +-
.../libngtcp2_crypto_boringssl.pc.in | 1 +
third_party/ngtcp2/crypto/gnutls/gnutls.c | 117 +-
.../gnutls/libngtcp2_crypto_gnutls.pc.in | 1 +
.../ngtcp2/crypto/includes/CMakeLists.txt | 2 +-
.../ngtcp2/crypto/includes/Makefile.am | 4 +
.../crypto/includes/ngtcp2/ngtcp2_crypto.h | 122 +-
third_party/ngtcp2/crypto/ossl/CMakeLists.txt | 18 +-
.../crypto/ossl/libngtcp2_crypto_ossl.pc.in | 1 +
third_party/ngtcp2/crypto/ossl/ossl.c | 162 +-
.../picotls/libngtcp2_crypto_picotls.pc.in | 1 +
third_party/ngtcp2/crypto/picotls/picotls.c | 39 +-
third_party/ngtcp2/crypto/quictls/.gitignore | 1 +
.../ngtcp2/crypto/quictls/CMakeLists.txt | 32 +-
third_party/ngtcp2/crypto/quictls/Makefile.am | 16 +-
.../quictls/libngtcp2_crypto_libressl.pc.in | 34 +
.../quictls/libngtcp2_crypto_quictls.pc.in | 1 +
third_party/ngtcp2/crypto/quictls/quictls.c | 80 +-
third_party/ngtcp2/crypto/shared.c | 259 ++-
third_party/ngtcp2/crypto/shared.h | 20 -
.../wolfssl/libngtcp2_crypto_wolfssl.pc.in | 1 +
third_party/ngtcp2/crypto/wolfssl/wolfssl.c | 64 +-
third_party/ngtcp2/lib/CMakeLists.txt | 21 +-
third_party/ngtcp2/lib/Makefile.am | 10 +-
third_party/ngtcp2/lib/config.cmake.in | 3 +
.../ngtcp2/lib/includes/ngtcp2/ngtcp2.h | 748 ++++++-
third_party/ngtcp2/lib/ngtcp2_acktr.c | 36 +-
third_party/ngtcp2/lib/ngtcp2_acktr.h | 28 +-
third_party/ngtcp2/lib/ngtcp2_addr.c | 19 +-
third_party/ngtcp2/lib/ngtcp2_addr.h | 8 +-
third_party/ngtcp2/lib/ngtcp2_balloc.c | 14 +-
third_party/ngtcp2/lib/ngtcp2_bbr.c | 474 +++--
third_party/ngtcp2/lib/ngtcp2_bbr.h | 26 +-
third_party/ngtcp2/lib/ngtcp2_buf.c | 14 +-
third_party/ngtcp2/lib/ngtcp2_buf.h | 11 +-
third_party/ngtcp2/lib/ngtcp2_callbacks.c | 75 +
third_party/ngtcp2/lib/ngtcp2_callbacks.h | 73 +
third_party/ngtcp2/lib/ngtcp2_cc.c | 283 ++-
third_party/ngtcp2/lib/ngtcp2_cc.h | 78 +-
third_party/ngtcp2/lib/ngtcp2_cid.c | 41 +-
third_party/ngtcp2/lib/ngtcp2_cid.h | 34 +-
third_party/ngtcp2/lib/ngtcp2_conn.c | 1875 ++++++++++++-----
third_party/ngtcp2/lib/ngtcp2_conn.h | 126 +-
third_party/ngtcp2/lib/ngtcp2_conn_info.c | 50 +
third_party/ngtcp2/lib/ngtcp2_conn_info.h | 45 +
third_party/ngtcp2/lib/ngtcp2_conn_stat.h | 39 +
third_party/ngtcp2/lib/ngtcp2_conv.c | 34 +-
third_party/ngtcp2/lib/ngtcp2_crypto.c | 24 +-
third_party/ngtcp2/lib/ngtcp2_crypto.h | 4 +-
third_party/ngtcp2/lib/ngtcp2_dcidtr.c | 17 +-
third_party/ngtcp2/lib/ngtcp2_dcidtr.h | 15 +-
third_party/ngtcp2/lib/ngtcp2_frame_chain.c | 73 +-
third_party/ngtcp2/lib/ngtcp2_frame_chain.h | 68 +-
third_party/ngtcp2/lib/ngtcp2_gaptr.c | 20 +-
third_party/ngtcp2/lib/ngtcp2_ksl.c | 361 ++--
third_party/ngtcp2/lib/ngtcp2_ksl.h | 117 +-
third_party/ngtcp2/lib/ngtcp2_log.c | 604 +++---
third_party/ngtcp2/lib/ngtcp2_log.h | 69 +-
third_party/ngtcp2/lib/ngtcp2_macro.h | 7 +
third_party/ngtcp2/lib/ngtcp2_map.c | 271 ++-
third_party/ngtcp2/lib/ngtcp2_map.h | 16 +-
third_party/ngtcp2/lib/ngtcp2_net.h | 8 +-
third_party/ngtcp2/lib/ngtcp2_objalloc.h | 4 +-
third_party/ngtcp2/lib/ngtcp2_path.c | 6 -
third_party/ngtcp2/lib/ngtcp2_path.h | 8 -
third_party/ngtcp2/lib/ngtcp2_pcg.c | 88 +
third_party/ngtcp2/lib/ngtcp2_pcg.h | 54 +
third_party/ngtcp2/lib/ngtcp2_pkt.c | 398 +++-
third_party/ngtcp2/lib/ngtcp2_pkt.h | 181 +-
third_party/ngtcp2/lib/ngtcp2_ppe.c | 4 +-
third_party/ngtcp2/lib/ngtcp2_pv.c | 19 +-
third_party/ngtcp2/lib/ngtcp2_pv.h | 24 +-
third_party/ngtcp2/lib/ngtcp2_qlog.c | 185 +-
third_party/ngtcp2/lib/ngtcp2_qlog.h | 2 +-
third_party/ngtcp2/lib/ngtcp2_range.c | 22 +-
third_party/ngtcp2/lib/ngtcp2_ratelim.c | 84 +
third_party/ngtcp2/lib/ngtcp2_ratelim.h | 59 +
third_party/ngtcp2/lib/ngtcp2_ringbuf.c | 14 +-
third_party/ngtcp2/lib/ngtcp2_ringbuf.h | 4 +-
third_party/ngtcp2/lib/ngtcp2_rob.c | 50 +-
third_party/ngtcp2/lib/ngtcp2_rob.h | 8 +-
third_party/ngtcp2/lib/ngtcp2_rst.c | 48 +-
third_party/ngtcp2/lib/ngtcp2_rst.h | 7 +-
third_party/ngtcp2/lib/ngtcp2_rtb.c | 210 +-
third_party/ngtcp2/lib/ngtcp2_rtb.h | 22 +-
third_party/ngtcp2/lib/ngtcp2_settings.c | 7 +
third_party/ngtcp2/lib/ngtcp2_settings.h | 7 +
third_party/ngtcp2/lib/ngtcp2_str.c | 187 +-
third_party/ngtcp2/lib/ngtcp2_str.h | 35 +-
third_party/ngtcp2/lib/ngtcp2_strm.c | 210 +-
third_party/ngtcp2/lib/ngtcp2_strm.h | 40 +-
.../ngtcp2/lib/ngtcp2_transport_params.c | 21 +-
.../ngtcp2/lib/ngtcp2_transport_params.h | 14 +-
third_party/ngtcp2/lib/ngtcp2_vec.c | 39 +-
third_party/ngtcp2/lib/ngtcp2_vec.h | 31 +-
third_party/ngtcp2/lib/ngtcp2_window_filter.c | 2 +-
third_party/ngtcp2/wscript | 4 +
99 files changed, 5992 insertions(+), 2962 deletions(-)
create mode 100644 third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_libressl.pc.in
create mode 100644 third_party/ngtcp2/lib/ngtcp2_callbacks.c
create mode 100644 third_party/ngtcp2/lib/ngtcp2_callbacks.h
create mode 100644 third_party/ngtcp2/lib/ngtcp2_conn_info.c
create mode 100644 third_party/ngtcp2/lib/ngtcp2_conn_info.h
create mode 100644 third_party/ngtcp2/lib/ngtcp2_pcg.c
create mode 100644 third_party/ngtcp2/lib/ngtcp2_pcg.h
create mode 100644 third_party/ngtcp2/lib/ngtcp2_ratelim.c
create mode 100644 third_party/ngtcp2/lib/ngtcp2_ratelim.h
diff --git a/third_party/ngtcp2/crypto/CMakeLists.txt b/third_party/ngtcp2/crypto/CMakeLists.txt
index c947837b31e..5e428fa812e 100644
--- a/third_party/ngtcp2/crypto/CMakeLists.txt
+++ b/third_party/ngtcp2/crypto/CMakeLists.txt
@@ -31,6 +31,12 @@ elseif(ENABLE_OPENSSL)
message(WARNING "libngtcp2_crypto_quictls library is disabled due to lack of good quictls")
endif()
+if(HAVE_LIBRESSL)
+ add_subdirectory(quictls)
+elseif(ENABLE_OPENSSL)
+ message(WARNING "libngtcp2_crypto_libressl library is disabled due to lack of good LibreSSL")
+endif()
+
if(HAVE_GNUTLS)
add_subdirectory(gnutls)
elseif(ENABLE_GNUTLS)
diff --git a/third_party/ngtcp2/crypto/Makefile.am b/third_party/ngtcp2/crypto/Makefile.am
index 8d2f7600f17..27224b9dcc1 100644
--- a/third_party/ngtcp2/crypto/Makefile.am
+++ b/third_party/ngtcp2/crypto/Makefile.am
@@ -30,6 +30,10 @@ if HAVE_QUICTLS
SUBDIRS += quictls
endif
+if HAVE_LIBRESSL
+SUBDIRS += quictls
+endif
+
if HAVE_GNUTLS
SUBDIRS += gnutls
endif
diff --git a/third_party/ngtcp2/crypto/boringssl/boringssl.c b/third_party/ngtcp2/crypto/boringssl/boringssl.c
index 283063f738e..e43faa76850 100644
--- a/third_party/ngtcp2/crypto/boringssl/boringssl.c
+++ b/third_party/ngtcp2/crypto/boringssl/boringssl.c
@@ -39,6 +39,7 @@
#include <openssl/chacha.h>
#include <openssl/rand.h>
+#include "ngtcp2_macro.h"
#include "shared.h"
typedef enum ngtcp2_crypto_boringssl_cipher_type {
@@ -401,7 +402,7 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx,
const uint8_t *sample) {
- static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ static const uint8_t PLAINTEXT[16] = {0};
ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle;
uint32_t counter;
@@ -419,7 +420,7 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
#else /* !defined(WORDS_BIGENDIAN) */
memcpy(&counter, sample, sizeof(counter));
#endif /* !defined(WORDS_BIGENDIAN) */
- CRYPTO_chacha_20(dest, PLAINTEXT, sizeof(PLAINTEXT) - 1, ctx->key,
+ CRYPTO_chacha_20(dest, PLAINTEXT, sizeof(PLAINTEXT), ctx->key,
sample + sizeof(counter), counter);
return 0;
default:
@@ -435,7 +436,8 @@ int ngtcp2_crypto_read_write_crypto_data(
int rv;
int err;
- if (SSL_provide_quic_data(
+ if (datalen &&
+ SSL_provide_quic_data(
ssl,
ngtcp2_crypto_boringssl_from_ngtcp2_encryption_level(encryption_level),
data, datalen) != 1) {
@@ -464,6 +466,16 @@ int ngtcp2_crypto_read_write_crypto_data(
}
goto retry;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
+ case SSL_ERROR_WANT_CERTIFICATE_VERIFY:
+ /* It might be better to return this error, but ngtcp2 does
+ not need to know whether handshake has been interrupted or
+ not. We expect that necessary plumbing should be done by
+ application when handshake is interrupted (e.g., via
+ SSL_PRIVATE_KEY_METHOD). If it does not work, we will
+ reconsider this. */
+ return 0;
default:
return -1;
}
@@ -567,6 +579,19 @@ int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
return 0;
}
+int ngtcp2_crypto_get_path_challenge_data2_cb(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data->data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
if (RAND_bytes(data, datalen) != 1) {
return -1;
diff --git a/third_party/ngtcp2/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in b/third_party/ngtcp2/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in
index 737970a8c6a..cf6ebbca5c2 100644
--- a/third_party/ngtcp2/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in
+++ b/third_party/ngtcp2/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in
@@ -31,3 +31,4 @@ URL: https://github.com/ngtcp2/ngtcp2
Version: @VERSION@
Libs: -L${libdir} -lngtcp2_crypto_boringssl
Cflags: -I${includedir}
+Requires.private: libngtcp2
diff --git a/third_party/ngtcp2/crypto/gnutls/gnutls.c b/third_party/ngtcp2/crypto/gnutls/gnutls.c
index 4e6990f87ef..cf7e6ca8934 100644
--- a/third_party/ngtcp2/crypto/gnutls/gnutls.c
+++ b/third_party/ngtcp2/crypto/gnutls/gnutls.c
@@ -35,6 +35,7 @@
#include <gnutls/crypto.h>
#include <string.h>
+#include "ngtcp2_macro.h"
#include "shared.h"
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
@@ -219,14 +220,15 @@ int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
gnutls_cipher_algorithm_t cipher =
(gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
gnutls_aead_cipher_hd_t hd;
- gnutls_datum_t _key;
(void)noncelen;
- _key.data = (void *)key;
- _key.size = (unsigned int)ngtcp2_crypto_aead_keylen(aead);
-
- if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0) {
+ if (gnutls_aead_cipher_init(
+ &hd, cipher,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)key,
+ .size = (unsigned int)ngtcp2_crypto_aead_keylen(aead),
+ }) != 0) {
return -1;
}
@@ -241,14 +243,15 @@ int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
gnutls_cipher_algorithm_t cipher =
(gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
gnutls_aead_cipher_hd_t hd;
- gnutls_datum_t _key;
(void)noncelen;
- _key.data = (void *)key;
- _key.size = (unsigned int)ngtcp2_crypto_aead_keylen(aead);
-
- if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0) {
+ if (gnutls_aead_cipher_init(
+ &hd, cipher,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)key,
+ .size = (unsigned int)ngtcp2_crypto_aead_keylen(aead),
+ }) != 0) {
return -1;
}
@@ -266,15 +269,17 @@ void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
const ngtcp2_crypto_cipher *cipher,
const uint8_t *key) {
- gnutls_cipher_algorithm_t _cipher =
+ gnutls_cipher_algorithm_t cph =
(gnutls_cipher_algorithm_t)(intptr_t)cipher->native_handle;
gnutls_cipher_hd_t hd;
- gnutls_datum_t _key;
- _key.data = (void *)key;
- _key.size = (unsigned int)gnutls_cipher_get_key_size(_cipher);
-
- if (gnutls_cipher_init(&hd, _cipher, &_key, NULL) != 0) {
+ if (gnutls_cipher_init(
+ &hd, cph,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)key,
+ .size = (unsigned int)gnutls_cipher_get_key_size(cph),
+ },
+ NULL) != 0) {
return -1;
}
@@ -294,10 +299,17 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
const uint8_t *salt, size_t saltlen) {
gnutls_mac_algorithm_t prf =
(gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
- gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
- gnutls_datum_t _salt = {(void *)salt, (unsigned int)saltlen};
- if (gnutls_hkdf_extract(prf, &_secret, &_salt, dest) != 0) {
+ if (gnutls_hkdf_extract(prf,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)secret,
+ .size = (unsigned int)secretlen,
+ },
+ &(gnutls_datum_t){
+ .data = (uint8_t *)salt,
+ .size = (unsigned int)saltlen,
+ },
+ dest) != 0) {
return -1;
}
@@ -310,10 +322,17 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
size_t infolen) {
gnutls_mac_algorithm_t prf =
(gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
- gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
- gnutls_datum_t _info = {(void *)info, (unsigned int)infolen};
- if (gnutls_hkdf_expand(prf, &_secret, &_info, dest, destlen) != 0) {
+ if (gnutls_hkdf_expand(prf,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)secret,
+ .size = (unsigned int)secretlen,
+ },
+ &(gnutls_datum_t){
+ .data = (uint8_t *)info,
+ .size = (unsigned int)infolen,
+ },
+ dest, destlen) != 0) {
return -1;
}
@@ -328,18 +347,32 @@ int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
(gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
size_t keylen = ngtcp2_crypto_md_hashlen(md);
uint8_t key[64];
- gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
- gnutls_datum_t _key = {(void *)key, (unsigned int)keylen};
- gnutls_datum_t _salt = {(void *)salt, (unsigned int)saltlen};
- gnutls_datum_t _info = {(void *)info, (unsigned int)infolen};
assert(keylen <= sizeof(key));
- if (gnutls_hkdf_extract(prf, &_secret, &_salt, key) != 0) {
+ if (gnutls_hkdf_extract(prf,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)secret,
+ .size = (unsigned int)secretlen,
+ },
+ &(gnutls_datum_t){
+ .data = (uint8_t *)salt,
+ .size = (unsigned int)saltlen,
+ },
+ key) != 0) {
return -1;
}
- if (gnutls_hkdf_expand(prf, &_key, &_info, dest, destlen) != 0) {
+ if (gnutls_hkdf_expand(prf,
+ &(gnutls_datum_t){
+ .data = (uint8_t *)key,
+ .size = (unsigned int)keylen,
+ },
+ &(gnutls_datum_t){
+ .data = (uint8_t *)info,
+ .size = (unsigned int)infolen,
+ },
+ dest, destlen) != 0) {
return -1;
}
@@ -402,13 +435,11 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
switch (cipher) {
case GNUTLS_CIPHER_AES_128_CBC:
case GNUTLS_CIPHER_AES_256_CBC: {
- uint8_t iv[16];
- uint8_t buf[16];
-
/* Emulate one block AES-ECB by invalidating the effect of IV */
- memset(iv, 0, sizeof(iv));
+ static const uint8_t iv[16] = {0};
+ uint8_t buf[16];
- gnutls_cipher_set_iv(hd, iv, sizeof(iv));
+ gnutls_cipher_set_iv(hd, (uint8_t *)iv, sizeof(iv));
if (gnutls_cipher_encrypt2(hd, sample, 16, buf, sizeof(buf)) != 0) {
return -1;
@@ -418,14 +449,14 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
} break;
case GNUTLS_CIPHER_CHACHA20_32: {
- static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ static const uint8_t PLAINTEXT[16] = {0};
uint8_t buf[5 + 16];
size_t buflen = sizeof(buf);
gnutls_cipher_set_iv(hd, (void *)sample, 16);
- if (gnutls_cipher_encrypt2(hd, PLAINTEXT, sizeof(PLAINTEXT) - 1, buf,
- buflen) != 0) {
+ if (gnutls_cipher_encrypt2(hd, PLAINTEXT, sizeof(PLAINTEXT), buf, buflen) !=
+ 0) {
return -1;
}
@@ -542,6 +573,20 @@ int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
return 0;
}
+int ngtcp2_crypto_get_path_challenge_data2_cb(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (gnutls_rnd(GNUTLS_RND_RANDOM, data->data,
+ NGTCP2_PATH_CHALLENGE_DATALEN) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
if (gnutls_rnd(GNUTLS_RND_RANDOM, data, datalen) != 0) {
return -1;
diff --git a/third_party/ngtcp2/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in b/third_party/ngtcp2/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
index 890e89d45f8..3be801a7e85 100644
--- a/third_party/ngtcp2/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
+++ b/third_party/ngtcp2/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
@@ -31,3 +31,4 @@ URL: https://github.com/ngtcp2/ngtcp2
Version: @VERSION@
Libs: -L${libdir} -lngtcp2_crypto_gnutls
Cflags: -I${includedir}
+Requires.private: libngtcp2, gnutls
diff --git a/third_party/ngtcp2/crypto/includes/CMakeLists.txt b/third_party/ngtcp2/crypto/includes/CMakeLists.txt
index 8415158c7e4..888c4991bee 100644
--- a/third_party/ngtcp2/crypto/includes/CMakeLists.txt
+++ b/third_party/ngtcp2/crypto/includes/CMakeLists.txt
@@ -25,7 +25,7 @@ install(FILES
ngtcp2/ngtcp2_crypto.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
-if(HAVE_QUICTLS)
+if(HAVE_QUICTLS OR HAVE_LIBRESSL)
install(FILES
ngtcp2/ngtcp2_crypto_quictls.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
diff --git a/third_party/ngtcp2/crypto/includes/Makefile.am b/third_party/ngtcp2/crypto/includes/Makefile.am
index 4aebf426a3a..63b22af85a9 100644
--- a/third_party/ngtcp2/crypto/includes/Makefile.am
+++ b/third_party/ngtcp2/crypto/includes/Makefile.am
@@ -28,6 +28,10 @@ if HAVE_QUICTLS
nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_quictls.h
endif
+if HAVE_LIBRESSL
+nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_quictls.h
+endif
+
if HAVE_GNUTLS
nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_gnutls.h
endif
diff --git a/third_party/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/third_party/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
index 003ec6b4c3f..28eeeb5ead8 100644
--- a/third_party/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
+++ b/third_party/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h
@@ -524,11 +524,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
* completes. It is allowed to call this function with |datalen| ==
* 0. In this case, no additional read operation is done.
*
+ * This function is implemented per TLS backend. See
+ * :ref:`tls-integration` for more details.
+ *
* This function returns 0 if it succeeds, or a negative error code.
* The generic error code is -1 if a specific error code is not
* suitable. The error codes less than -10000 are specific to
- * underlying TLS implementation. For quictls, the error codes are
- * defined in *ngtcp2_crypto_quictls.h*.
+ * underlying TLS implementation. Refer to the implementation
+ * specific header files for error codes.
*/
NGTCP2_EXTERN int
ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
@@ -542,11 +545,22 @@ ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
* `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed
* to :member:`ngtcp2_callbacks.recv_crypto_data` field.
*
+ * For quictls and OpenSSL, the following error codes are treated as
+ * success:
+ *
+ * - -10001 (e.g., :macro:`NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_X509_LOOKUP`)
+ * - -10002 (e.g., :macro:`NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_CLIENT_HELLO_CB`)
+ *
+ * To continue the interrupted handshake, call
+ * `ngtcp2_conn_continue_handshake`.
+ *
+ * See :ref:`tls-integration` for more details.
+ *
* If this function is used, the TLS implementation specific error
* codes described in `ngtcp2_crypto_read_write_crypto_data` are
- * treated as if it returns -1. Do not use this function if an
- * application wishes to use the TLS implementation specific error
- * codes.
+ * treated as if it returns -1 except for those that are listed above.
+ * Do not use this function if an application wishes to use the TLS
+ * implementation specific error codes.
*/
NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb(
ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, uint64_t offset,
@@ -583,7 +597,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for
* Retry token generated by `ngtcp2_crypto_generate_retry_token`.
*/
-#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xB6
/**
* @macro
@@ -591,7 +605,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
* :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY2` is the magic byte for
* Retry token generated by `ngtcp2_crypto_generate_retry_token2`.
*/
-#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY2 0xb7
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY2 0xB7
/**
* @macro
@@ -627,7 +641,10 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
* @macro
*
* :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length
- * of a token generated by `ngtcp2_crypto_generate_regular_token`.
+ * of a token generated by `ngtcp2_crypto_generate_regular_token`.
+ * `ngtcp2_crypto_generate_regular_token2` generates a token of length
+ * at most :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes + the
+ * length of the provided opaque data.
*/
#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \
(/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
@@ -787,6 +804,77 @@ NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
size_t secretlen, const ngtcp2_sockaddr *remote_addr,
ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_regular_token2` generates a token in the
+ * buffer pointed by |token| that is sent with NEW_TOKEN frame. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` + |datalen| bytes long.
+ * The successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length
+ * |secretlen| is a keying material to generate keys to encrypt the
+ * token. |remote_addr| of length |remote_addrlen| is an address of
+ * client. |ts| is the timestamp when the token is generated. |data|
+ * of length |datalen| is an opaque data embedded in the token.
+ * |datalen| must be less than or equal to 256.
+ *
+ * Calling this function with |datalen| = 0 is equivalent to calling
+ * `ngtcp2_crypto_generate_regular_token`.
+ *
+ * To get the opaque data after successful verification, use
+ * `ngtcp2_crypto_verify_regular_token2`.
+ * `ngtcp2_crypto_verify_regular_token` can verify the token with
+ * |datalen| > 0, but it discards the opaque data.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token2(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const void *data, size_t datalen, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_regular_token2` verifies a regular token
+ * stored in the buffer pointed by |token| of length |tokenlen|.
+ * |secret| of length |secretlen| is a keying material to generate
+ * keys to decrypt the token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |timeout| is the period
+ * during which the token is valid. |ts| is the current timestamp.
+ * |data| is the pointer to the buffer of length at least
+ * |max_datalen| bytes. If the token is verified successfully, the
+ * opaque data embedded in the token is copied to the buffer pointed
+ * by |data|.
+ *
+ * If |tokenlen| is less than
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN`, this function returns
+ * :macro:`NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN`.
+ *
+ * If the length of opaque data is larger than |max_datalen|, the
+ * verification still succeeds, but nothing is written to the buffer
+ * pointed by |data|, and this function returns 0. In other words,
+ * the opaque data is discarded.
+ *
+ * This function returns the number of the opaque data written to the
+ * buffer pointed by |data| if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN`
+ * A token is badly formatted; or verifying the integrity
+ * protection failed.
+ * :macro:`NGTCP2_CRYPTO_ERR_VERIFY_TOKEN`
+ * A token validity has expired.
+ * :macro:`NGTCP2_CRYPTO_ERR_INTERNAL`
+ * Internal error occurred.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_verify_regular_token2(
+ void *data, size_t max_datalen, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+
/**
* @function
*
@@ -904,11 +992,29 @@ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
*
* This function can be directly passed to
* :member:`ngtcp2_callbacks.get_path_challenge_data` field.
+ *
+ * Deprecated since v1.22.0. Use
+ * `ngtcp2_crypto_get_path_challenge_data2_cb` instead.
*/
NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn,
uint8_t *data,
void *user_data);
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_get_path_challenge_data2_cb` writes unpredictable
+ * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data|
+ * which is sent with PATH_CHALLENGE frame.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.get_path_challenge_data2` field.
+ *
+ * This function has been available since v1.22.0.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data2_cb(
+ ngtcp2_conn *conn, ngtcp2_path_challenge_data *data, void *user_data);
+
/**
* @function
*
diff --git a/third_party/ngtcp2/crypto/ossl/CMakeLists.txt b/third_party/ngtcp2/crypto/ossl/CMakeLists.txt
index da2ef2df763..1a108ada0a4 100644
--- a/third_party/ngtcp2/crypto/ossl/CMakeLists.txt
+++ b/third_party/ngtcp2/crypto/ossl/CMakeLists.txt
@@ -29,14 +29,11 @@ set(ngtcp2_crypto_ossl_SOURCES
)
set(ngtcp2_crypto_ossl_INCLUDE_DIRS
- "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
- "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
- "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
- "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
- "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
- "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto"
- "${CMAKE_CURRENT_BINARY_DIR}/../../crypto"
- "${OPENSSL_INCLUDE_DIRS}"
+ "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib>"
+ "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/crypto/includes>"
+ "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/crypto/includes>"
+ "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/crypto>"
+ "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/crypto>"
)
foreach(name libngtcp2_crypto_ossl.pc)
@@ -55,9 +52,10 @@ if(ENABLE_SHARED_LIB)
)
target_include_directories(ngtcp2_crypto_ossl PUBLIC
${ngtcp2_crypto_ossl_INCLUDE_DIRS})
- target_link_libraries(ngtcp2_crypto_ossl ngtcp2 ${OPENSSL_LIBRARIES})
+ target_link_libraries(ngtcp2_crypto_ossl PUBLIC ngtcp2 OpenSSL::SSL)
install(TARGETS ngtcp2_crypto_ossl
+ EXPORT "${PROJECT_NAME}Targets"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
@@ -77,8 +75,10 @@ if(ENABLE_STATIC_LIB)
"-DNGTCP2_STATICLIB")
target_include_directories(ngtcp2_crypto_ossl_static PUBLIC
${ngtcp2_crypto_ossl_INCLUDE_DIRS})
+ target_link_libraries(ngtcp2_crypto_ossl_static PUBLIC ngtcp2_static OpenSSL::SSL)
install(TARGETS ngtcp2_crypto_ossl_static
+ EXPORT "${PROJECT_NAME}Targets"
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
endif()
diff --git a/third_party/ngtcp2/crypto/ossl/libngtcp2_crypto_ossl.pc.in b/third_party/ngtcp2/crypto/ossl/libngtcp2_crypto_ossl.pc.in
index 1397b4bfb42..39c254a309b 100644
--- a/third_party/ngtcp2/crypto/ossl/libngtcp2_crypto_ossl.pc.in
+++ b/third_party/ngtcp2/crypto/ossl/libngtcp2_crypto_ossl.pc.in
@@ -31,3 +31,4 @@ URL: https://github.com/ngtcp2/ngtcp2
Version: @VERSION@
Libs: -L${libdir} -lngtcp2_crypto_ossl
Cflags: -I${includedir}
+Requires.private: libngtcp2, openssl
diff --git a/third_party/ngtcp2/crypto/ossl/ossl.c b/third_party/ngtcp2/crypto/ossl/ossl.c
index 061d6acd21d..fa393c8ece5 100644
--- a/third_party/ngtcp2/crypto/ossl/ossl.c
+++ b/third_party/ngtcp2/crypto/ossl/ossl.c
@@ -41,70 +41,40 @@
#include "ngtcp2_macro.h"
#include "shared.h"
-static int crypto_initialized;
+#if defined(OPENSSL_NO_CHACHA) || defined(OPENSSL_NO_POLY1305)
+# define NGTCP2_NO_CHACHA_POLY1305
+#endif /* defined(OPENSSL_NO_CHACHA) || \
+ defined(OPENSSL_NO_POLY1305) */
+
static EVP_CIPHER *crypto_aes_128_gcm;
static EVP_CIPHER *crypto_aes_256_gcm;
-static EVP_CIPHER *crypto_chacha20_poly1305;
static EVP_CIPHER *crypto_aes_128_ccm;
-static EVP_CIPHER *crypto_aes_128_ctr;
-static EVP_CIPHER *crypto_aes_256_ctr;
+static EVP_CIPHER *crypto_aes_128_ecb;
+static EVP_CIPHER *crypto_aes_256_ecb;
+#ifndef NGTCP2_NO_CHACHA_POLY1305
+static EVP_CIPHER *crypto_chacha20_poly1305;
static EVP_CIPHER *crypto_chacha20;
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
static EVP_MD *crypto_sha256;
static EVP_MD *crypto_sha384;
static EVP_KDF *crypto_hkdf;
int ngtcp2_crypto_ossl_init(void) {
+ /* We do not care whether the pre-fetch succeeds or not. If it
+ fails, it returns NULL, which is still the default value, and our
+ code should still work with it. */
crypto_aes_128_gcm = EVP_CIPHER_fetch(NULL, "AES-128-GCM", NULL);
- if (crypto_aes_128_gcm == NULL) {
- return -1;
- }
-
crypto_aes_256_gcm = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL);
- if (crypto_aes_256_gcm == NULL) {
- return -1;
- }
-
- crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL);
- if (crypto_chacha20_poly1305 == NULL) {
- return -1;
- }
-
crypto_aes_128_ccm = EVP_CIPHER_fetch(NULL, "AES-128-CCM", NULL);
- if (crypto_aes_128_ccm == NULL) {
- return -1;
- }
-
- crypto_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL);
- if (crypto_aes_128_ctr == NULL) {
- return -1;
- }
-
- crypto_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL);
- if (crypto_aes_256_ctr == NULL) {
- return -1;
- }
-
+ crypto_aes_128_ecb = EVP_CIPHER_fetch(NULL, "AES-128-ECB", NULL);
+ crypto_aes_256_ecb = EVP_CIPHER_fetch(NULL, "AES-256-ECB", NULL);
+#ifndef NGTCP2_NO_CHACHA_POLY1305
+ crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL);
crypto_chacha20 = EVP_CIPHER_fetch(NULL, "ChaCha20", NULL);
- if (crypto_chacha20 == NULL) {
- return -1;
- }
-
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
crypto_sha256 = EVP_MD_fetch(NULL, "sha256", NULL);
- if (crypto_sha256 == NULL) {
- return -1;
- }
-
crypto_sha384 = EVP_MD_fetch(NULL, "sha384", NULL);
- if (crypto_sha384 == NULL) {
- return -1;
- }
-
crypto_hkdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
- if (crypto_hkdf == NULL) {
- return -1;
- }
-
- crypto_initialized = 1;
return 0;
}
@@ -125,6 +95,7 @@ static const EVP_CIPHER *crypto_aead_aes_256_gcm(void) {
return EVP_aes_256_gcm();
}
+#ifndef NGTCP2_NO_CHACHA_POLY1305
static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) {
if (crypto_chacha20_poly1305) {
return crypto_chacha20_poly1305;
@@ -132,6 +103,7 @@ static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) {
return EVP_chacha20_poly1305();
}
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) {
if (crypto_aes_128_ccm) {
@@ -141,22 +113,23 @@ static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) {
return EVP_aes_128_ccm();
}
-static const EVP_CIPHER *crypto_cipher_aes_128_ctr(void) {
- if (crypto_aes_128_ctr) {
- return crypto_aes_128_ctr;
+static const EVP_CIPHER *crypto_cipher_aes_128_ecb(void) {
+ if (crypto_aes_128_ecb) {
+ return crypto_aes_128_ecb;
}
- return EVP_aes_128_ctr();
+ return EVP_aes_128_ecb();
}
-static const EVP_CIPHER *crypto_cipher_aes_256_ctr(void) {
- if (crypto_aes_256_ctr) {
- return crypto_aes_256_ctr;
+static const EVP_CIPHER *crypto_cipher_aes_256_ecb(void) {
+ if (crypto_aes_256_ecb) {
+ return crypto_aes_256_ecb;
}
- return EVP_aes_256_ctr();
+ return EVP_aes_256_ecb();
}
+#ifndef NGTCP2_NO_CHACHA_POLY1305
static const EVP_CIPHER *crypto_cipher_chacha20(void) {
if (crypto_chacha20) {
return crypto_chacha20;
@@ -164,6 +137,7 @@ static const EVP_CIPHER *crypto_cipher_chacha20(void) {
return EVP_chacha20();
}
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
static const EVP_MD *crypto_md_sha256(void) {
if (crypto_sha256) {
@@ -189,13 +163,21 @@ static EVP_KDF *crypto_kdf_hkdf(void) {
return EVP_KDF_fetch(NULL, "hkdf", NULL);
}
+static void crypto_kdf_hkdf_free(EVP_KDF *kdf) {
+ if (kdf && crypto_hkdf != kdf) {
+ EVP_KDF_free(kdf);
+ }
+}
+
static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) {
switch (EVP_CIPHER_nid(aead)) {
case NID_aes_128_gcm:
case NID_aes_256_gcm:
return EVP_GCM_TLS_TAG_LEN;
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case NID_chacha20_poly1305:
return EVP_CHACHAPOLY_TLS_TAG_LEN;
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case NID_aes_128_ccm:
return EVP_CCM_TLS_TAG_LEN;
default:
@@ -216,7 +198,7 @@ ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_aead_aes_128_gcm());
ctx->md.native_handle = (void *)crypto_md_sha256();
- ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ctr();
+ ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ecb();
ctx->max_encryption = 0;
ctx->max_decryption_failure = 0;
return ctx;
@@ -239,8 +221,10 @@ static const EVP_CIPHER *crypto_cipher_id_get_aead(uint32_t cipher_id) {
return crypto_aead_aes_128_gcm();
case TLS1_3_CK_AES_256_GCM_SHA384:
return crypto_aead_aes_256_gcm();
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return crypto_aead_chacha20_poly1305();
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return crypto_aead_aes_128_ccm();
default:
@@ -253,8 +237,10 @@ static uint64_t crypto_cipher_id_get_aead_max_encryption(uint32_t cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_256_GCM_SHA384:
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
default:
@@ -268,8 +254,10 @@ crypto_cipher_id_get_aead_max_decryption_failure(uint32_t cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_256_GCM_SHA384:
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
default:
@@ -281,11 +269,13 @@ static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) {
switch (cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_128_CCM_SHA256:
- return crypto_cipher_aes_128_ctr();
+ return crypto_cipher_aes_128_ecb();
case TLS1_3_CK_AES_256_GCM_SHA384:
- return crypto_cipher_aes_256_ctr();
+ return crypto_cipher_aes_256_ecb();
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return crypto_cipher_chacha20();
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
default:
return NULL;
}
@@ -294,7 +284,9 @@ static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) {
static const EVP_MD *crypto_cipher_id_get_md(uint32_t cipher_id) {
switch (cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return crypto_md_sha256();
case TLS1_3_CK_AES_256_GCM_SHA384:
@@ -308,7 +300,9 @@ static int supported_cipher_id(uint32_t cipher_id) {
switch (cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_256_GCM_SHA384:
+#ifndef NGTCP2_NO_CHACHA_POLY1305
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */
case TLS1_3_CK_AES_128_CCM_SHA256:
return 1;
default:
@@ -697,9 +691,7 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
};
int rv = 0;
- if (!crypto_initialized) {
- EVP_KDF_free(kdf);
- }
+ crypto_kdf_hkdf_free(kdf);
if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) {
rv = -1;
@@ -730,9 +722,7 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
};
int rv = 0;
- if (!crypto_initialized) {
- EVP_KDF_free(kdf);
- }
+ crypto_kdf_hkdf_free(kdf);
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
rv = -1;
@@ -763,9 +753,7 @@ int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
};
int rv = 0;
- if (!crypto_initialized) {
- EVP_KDF_free(kdf);
- }
+ crypto_kdf_hkdf_free(kdf);
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
rv = -1;
@@ -850,16 +838,31 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx,
const uint8_t *sample) {
- static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ static const uint8_t PLAINTEXT[16] = {0};
EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
int len;
(void)hp;
- if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
- !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) ||
- !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) {
- return -1;
+ switch (EVP_CIPHER_CTX_nid(actx)) {
+ case NID_aes_128_ecb:
+ case NID_aes_256_ecb:
+ if (!EVP_EncryptUpdate(actx, dest, &len, sample, NGTCP2_HP_SAMPLELEN)) {
+ return -1;
+ }
+
+ break;
+ case NID_chacha20:
+ if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
+ !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT)) ||
+ !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT), &len)) {
+ return -1;
+ }
+
+ break;
+ default:
+ assert(0);
+ abort();
}
return 0;
@@ -994,6 +997,19 @@ int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
return 0;
}
+int ngtcp2_crypto_get_path_challenge_data2_cb(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data->data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
if (RAND_bytes(data, (int)datalen) != 1) {
return -1;
diff --git a/third_party/ngtcp2/crypto/picotls/libngtcp2_crypto_picotls.pc.in b/third_party/ngtcp2/crypto/picotls/libngtcp2_crypto_picotls.pc.in
index 1cb24f19388..3cef316f48a 100644
--- a/third_party/ngtcp2/crypto/picotls/libngtcp2_crypto_picotls.pc.in
+++ b/third_party/ngtcp2/crypto/picotls/libngtcp2_crypto_picotls.pc.in
@@ -31,3 +31,4 @@ URL: https://github.com/ngtcp2/ngtcp2
Version: @VERSION@
Libs: -L${libdir} -lngtcp2_crypto_picotls
Cflags: -I${includedir}
+Requires.private: libngtcp2, openssl
diff --git a/third_party/ngtcp2/crypto/picotls/picotls.c b/third_party/ngtcp2/crypto/picotls/picotls.c
index 98ebf9e876c..bd29a541277 100644
--- a/third_party/ngtcp2/crypto/picotls/picotls.c
+++ b/third_party/ngtcp2/crypto/picotls/picotls.c
@@ -35,6 +35,7 @@
#include <picotls.h>
#include <picotls/openssl.h>
+#include "ngtcp2_macro.h"
#include "shared.h"
ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
@@ -49,7 +50,7 @@ ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
ngtcp2_crypto_aead_init(&ctx->aead, (void *)&ptls_openssl_aes128gcm);
ctx->md.native_handle = (void *)&ptls_openssl_sha256;
- ctx->hp.native_handle = (void *)&ptls_openssl_aes128ctr;
+ ctx->hp.native_handle = (void *)&ptls_openssl_aes128ecb;
ctx->max_encryption = 0;
ctx->max_decryption_failure = 0;
return ctx;
@@ -103,11 +104,11 @@ crypto_cipher_suite_get_aead_max_decryption_failure(ptls_cipher_suite_t *cs) {
static const ptls_cipher_algorithm_t *
crypto_cipher_suite_get_hp(ptls_cipher_suite_t *cs) {
if (cs->aead == &ptls_openssl_aes128gcm) {
- return &ptls_openssl_aes128ctr;
+ return &ptls_openssl_aes128ecb;
}
if (cs->aead == &ptls_openssl_aes256gcm) {
- return &ptls_openssl_aes256ctr;
+ return &ptls_openssl_aes256ecb;
}
#ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
@@ -237,6 +238,11 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
return -1;
}
+ if (cipher->native_handle == &ptls_openssl_aes128ecb ||
+ cipher->native_handle == &ptls_openssl_aes256ecb) {
+ ptls_cipher_init(actx, NULL);
+ }
+
cipher_ctx->native_handle = actx;
return 0;
@@ -351,13 +357,20 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx,
const uint8_t *sample) {
+ static const uint8_t PLAINTEXT[16] = {0};
ptls_cipher_context_t *actx = hp_ctx->native_handle;
- static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
(void)hp;
+ if (hp->native_handle == &ptls_openssl_aes128ecb ||
+ hp->native_handle == &ptls_openssl_aes256ecb) {
+ ptls_cipher_encrypt(actx, dest, sample, NGTCP2_HP_SAMPLELEN);
+
+ return 0;
+ }
+
ptls_cipher_init(actx, sample);
- ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT) - 1);
+ ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT));
return 0;
}
@@ -376,7 +389,7 @@ int ngtcp2_crypto_read_write_crypto_data(
ptls_buffer_init(&sendbuf, (void *)"", 0);
- assert(epoch == ptls_get_read_epoch(cptls->ptls));
+ assert(datalen == 0 || epoch == ptls_get_read_epoch(cptls->ptls));
rv = ptls_handle_message(cptls->ptls, &sendbuf, epoch_offsets, epoch, data,
datalen, &cptls->handshake_properties);
@@ -492,6 +505,17 @@ int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
return 0;
}
+int ngtcp2_crypto_get_path_challenge_data2_cb(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ptls_openssl_random_bytes(data->data, NGTCP2_PATH_CHALLENGE_DATALEN);
+
+ return 0;
+}
+
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
ptls_openssl_random_bytes(data, datalen);
@@ -499,8 +523,7 @@ int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
}
void ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls) {
- cptls->ptls = NULL;
- memset(&cptls->handshake_properties, 0, sizeof(cptls->handshake_properties));
+ *cptls = (ngtcp2_crypto_picotls_ctx){0};
}
static int set_additional_extensions(ptls_handshake_properties_t *hsprops,
diff --git a/third_party/ngtcp2/crypto/quictls/.gitignore b/third_party/ngtcp2/crypto/quictls/.gitignore
index 6f4bcf87cbd..e6f94f30474 100644
--- a/third_party/ngtcp2/crypto/quictls/.gitignore
+++ b/third_party/ngtcp2/crypto/quictls/.gitignore
@@ -1 +1,2 @@
+/libngtcp2_crypto_libressl.pc
/libngtcp2_crypto_quictls.pc
diff --git a/third_party/ngtcp2/crypto/quictls/CMakeLists.txt b/third_party/ngtcp2/crypto/quictls/CMakeLists.txt
index c1296b2cad1..b94e1f0c647 100644
--- a/third_party/ngtcp2/crypto/quictls/CMakeLists.txt
+++ b/third_party/ngtcp2/crypto/quictls/CMakeLists.txt
@@ -39,25 +39,31 @@ set(ngtcp2_crypto_quictls_INCLUDE_DIRS
"${OPENSSL_INCLUDE_DIRS}"
)
-foreach(name libngtcp2_crypto_quictls.pc)
+if(HAVE_LIBRESSL)
+ set(ngtcp2_crypto_lib "ngtcp2_crypto_libressl")
+else()
+ set(ngtcp2_crypto_lib "ngtcp2_crypto_quictls")
+endif()
+
+foreach(name lib${ngtcp2_crypto_lib}.pc)
configure_file("${name}.in" "${name}" @ONLY)
endforeach()
# Public shared library
if(ENABLE_SHARED_LIB)
- add_library(ngtcp2_crypto_quictls SHARED ${ngtcp2_crypto_quictls_SOURCES})
- set_target_properties(ngtcp2_crypto_quictls PROPERTIES
+ add_library(${ngtcp2_crypto_lib} SHARED ${ngtcp2_crypto_quictls_SOURCES})
+ set_target_properties(${ngtcp2_crypto_lib} PROPERTIES
COMPILE_FLAGS "${WARNCFLAGS}"
VERSION ${CRYPTO_QUICTLS_LT_VERSION}
SOVERSION ${CRYPTO_QUICTLS_LT_SOVERSION}
C_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON
)
- target_include_directories(ngtcp2_crypto_quictls PUBLIC
+ target_include_directories(${ngtcp2_crypto_lib} PUBLIC
${ngtcp2_crypto_quictls_INCLUDE_DIRS})
- target_link_libraries(ngtcp2_crypto_quictls ngtcp2 ${OPENSSL_LIBRARIES})
+ target_link_libraries(${ngtcp2_crypto_lib} ngtcp2 ${OPENSSL_LIBRARIES})
- install(TARGETS ngtcp2_crypto_quictls
+ install(TARGETS ${ngtcp2_crypto_lib}
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
@@ -65,22 +71,22 @@ endif()
if(ENABLE_STATIC_LIB)
# Public static library
- add_library(ngtcp2_crypto_quictls_static STATIC ${ngtcp2_crypto_quictls_SOURCES})
- set_target_properties(ngtcp2_crypto_quictls_static PROPERTIES
+ add_library(${ngtcp2_crypto_lib}_static STATIC ${ngtcp2_crypto_quictls_SOURCES})
+ set_target_properties(${ngtcp2_crypto_lib}_static PROPERTIES
COMPILE_FLAGS "${WARNCFLAGS}"
VERSION ${CRYPTO_QUICTLS_LT_VERSION}
SOVERSION ${CRYPTO_QUICTLS_LT_SOVERSION}
- ARCHIVE_OUTPUT_NAME ngtcp2_crypto_quictls${STATIC_LIB_SUFFIX}
+ ARCHIVE_OUTPUT_NAME ${ngtcp2_crypto_lib}${STATIC_LIB_SUFFIX}
C_VISIBILITY_PRESET hidden
)
- target_compile_definitions(ngtcp2_crypto_quictls_static PUBLIC
+ target_compile_definitions(${ngtcp2_crypto_lib}_static PUBLIC
"-DNGTCP2_STATICLIB")
- target_include_directories(ngtcp2_crypto_quictls_static PUBLIC
+ target_include_directories(${ngtcp2_crypto_lib}_static PUBLIC
${ngtcp2_crypto_quictls_INCLUDE_DIRS})
- install(TARGETS ngtcp2_crypto_quictls_static
+ install(TARGETS ${ngtcp2_crypto_lib}_static
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
endif()
-install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_quictls.pc"
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${ngtcp2_crypto_lib}.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/third_party/ngtcp2/crypto/quictls/Makefile.am b/third_party/ngtcp2/crypto/quictls/Makefile.am
index d15dcbb8796..93670f8b828 100644
--- a/third_party/ngtcp2/crypto/quictls/Makefile.am
+++ b/third_party/ngtcp2/crypto/quictls/Makefile.am
@@ -31,9 +31,18 @@ AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
AM_LDFLAGS = ${LIBTOOL_LDFLAGS}
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libngtcp2_crypto_quictls.pc
-DISTCLEANFILES = $(pkgconfig_DATA)
+if HAVE_LIBRESSL
+pkgconfig_DATA = libngtcp2_crypto_libressl.pc
+lib_LTLIBRARIES = libngtcp2_crypto_libressl.la
+
+libngtcp2_crypto_libressl_la_SOURCES = quictls.c ../shared.c ../shared.h
+libngtcp2_crypto_libressl_la_LDFLAGS = -no-undefined \
+ -version-info $(CRYPTO_LIBRESSL_LT_CURRENT):$(CRYPTO_LIBRESSL_LT_REVISION):$(CRYPTO_LIBRESSL_LT_AGE)
+libngtcp2_crypto_libressl_la_LIBADD = $(top_builddir)/lib/libngtcp2.la \
+ @OPENSSL_LIBS@
+else
+pkgconfig_DATA = libngtcp2_crypto_quictls.pc
lib_LTLIBRARIES = libngtcp2_crypto_quictls.la
libngtcp2_crypto_quictls_la_SOURCES = quictls.c ../shared.c ../shared.h
@@ -41,3 +50,6 @@ libngtcp2_crypto_quictls_la_LDFLAGS = -no-undefined \
-version-info $(CRYPTO_QUICTLS_LT_CURRENT):$(CRYPTO_QUICTLS_LT_REVISION):$(CRYPTO_QUICTLS_LT_AGE)
libngtcp2_crypto_quictls_la_LIBADD = $(top_builddir)/lib/libngtcp2.la \
@OPENSSL_LIBS@
+endif
+
+DISTCLEANFILES = $(pkgconfig_DATA)
diff --git a/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_libressl.pc.in b/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_libressl.pc.in
new file mode 100644
index 00000000000..903287916ce
--- /dev/null
+++ b/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_libressl.pc.in
@@ -0,0 +1,34 @@
+# 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.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_libressl
+Description: ngtcp2 LibreSSL crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_libressl
+Cflags: -I${includedir}
+Requires.private: libngtcp2, openssl
diff --git a/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_quictls.pc.in b/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_quictls.pc.in
index 991d2a7e79b..6887c1743f2 100644
--- a/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_quictls.pc.in
+++ b/third_party/ngtcp2/crypto/quictls/libngtcp2_crypto_quictls.pc.in
@@ -31,3 +31,4 @@ URL: https://github.com/ngtcp2/ngtcp2
Version: @VERSION@
Libs: -L${libdir} -lngtcp2_crypto_quictls
Cflags: -I${includedir}
+Requires.private: libngtcp2, openssl
diff --git a/third_party/ngtcp2/crypto/quictls/quictls.c b/third_party/ngtcp2/crypto/quictls/quictls.c
index 592e5a86535..1076d97bfc0 100644
--- a/third_party/ngtcp2/crypto/quictls/quictls.c
+++ b/third_party/ngtcp2/crypto/quictls/quictls.c
@@ -40,6 +40,7 @@
# include <openssl/core_names.h>
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+#include "ngtcp2_macro.h"
#include "shared.h"
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
@@ -48,8 +49,8 @@ static EVP_CIPHER *crypto_aes_128_gcm;
static EVP_CIPHER *crypto_aes_256_gcm;
static EVP_CIPHER *crypto_chacha20_poly1305;
static EVP_CIPHER *crypto_aes_128_ccm;
-static EVP_CIPHER *crypto_aes_128_ctr;
-static EVP_CIPHER *crypto_aes_256_ctr;
+static EVP_CIPHER *crypto_aes_128_ecb;
+static EVP_CIPHER *crypto_aes_256_ecb;
static EVP_CIPHER *crypto_chacha20;
static EVP_MD *crypto_sha256;
static EVP_MD *crypto_sha384;
@@ -76,13 +77,13 @@ int ngtcp2_crypto_quictls_init(void) {
return -1;
}
- crypto_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL);
- if (crypto_aes_128_ctr == NULL) {
+ crypto_aes_128_ecb = EVP_CIPHER_fetch(NULL, "AES-128-ECB", NULL);
+ if (crypto_aes_128_ecb == NULL) {
return -1;
}
- crypto_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL);
- if (crypto_aes_256_ctr == NULL) {
+ crypto_aes_256_ecb = EVP_CIPHER_fetch(NULL, "AES-256-ECB", NULL);
+ if (crypto_aes_256_ecb == NULL) {
return -1;
}
@@ -143,20 +144,20 @@ static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) {
return EVP_aes_128_ccm();
}
-static const EVP_CIPHER *crypto_cipher_aes_128_ctr(void) {
- if (crypto_aes_128_ctr) {
- return crypto_aes_128_ctr;
+static const EVP_CIPHER *crypto_cipher_aes_128_ecb(void) {
+ if (crypto_aes_128_ecb) {
+ return crypto_aes_128_ecb;
}
- return EVP_aes_128_ctr();
+ return EVP_aes_128_ecb();
}
-static const EVP_CIPHER *crypto_cipher_aes_256_ctr(void) {
- if (crypto_aes_256_ctr) {
- return crypto_aes_256_ctr;
+static const EVP_CIPHER *crypto_cipher_aes_256_ecb(void) {
+ if (crypto_aes_256_ecb) {
+ return crypto_aes_256_ecb;
}
- return EVP_aes_256_ctr();
+ return EVP_aes_256_ecb();
}
static const EVP_CIPHER *crypto_cipher_chacha20(void) {
@@ -195,8 +196,8 @@ static EVP_KDF *crypto_kdf_hkdf(void) {
# define crypto_aead_aes_256_gcm EVP_aes_256_gcm
# define crypto_aead_chacha20_poly1305 EVP_chacha20_poly1305
# define crypto_aead_aes_128_ccm EVP_aes_128_ccm
-# define crypto_cipher_aes_128_ctr EVP_aes_128_ctr
-# define crypto_cipher_aes_256_ctr EVP_aes_256_ctr
+# define crypto_cipher_aes_128_ecb EVP_aes_128_ecb
+# define crypto_cipher_aes_256_ecb EVP_aes_256_ecb
# define crypto_cipher_chacha20 EVP_chacha20
# define crypto_md_sha256 EVP_sha256
# define crypto_md_sha384 EVP_sha384
@@ -231,7 +232,7 @@ ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_aead_aes_128_gcm());
ctx->md.native_handle = (void *)crypto_md_sha256();
- ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ctr();
+ ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ecb();
ctx->max_encryption = 0;
ctx->max_decryption_failure = 0;
return ctx;
@@ -296,9 +297,9 @@ static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) {
switch (cipher_id) {
case TLS1_3_CK_AES_128_GCM_SHA256:
case TLS1_3_CK_AES_128_CCM_SHA256:
- return crypto_cipher_aes_128_ctr();
+ return crypto_cipher_aes_128_ecb();
case TLS1_3_CK_AES_256_GCM_SHA384:
- return crypto_cipher_aes_256_ctr();
+ return crypto_cipher_aes_256_ecb();
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return crypto_cipher_chacha20();
default:
@@ -778,16 +779,31 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx,
const uint8_t *sample) {
- static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ static const uint8_t PLAINTEXT[16] = {0};
EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
int len;
(void)hp;
- if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
- !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) ||
- !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) {
- return -1;
+ switch (EVP_CIPHER_CTX_nid(actx)) {
+ case NID_aes_128_ecb:
+ case NID_aes_256_ecb:
+ if (!EVP_EncryptUpdate(actx, dest, &len, sample, NGTCP2_HP_SAMPLELEN)) {
+ return -1;
+ }
+
+ break;
+ case NID_chacha20:
+ if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
+ !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT)) ||
+ !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT), &len)) {
+ return -1;
+ }
+
+ break;
+ default:
+ assert(0);
+ abort();
}
return 0;
@@ -800,7 +816,8 @@ int ngtcp2_crypto_read_write_crypto_data(
int rv;
int err;
- if (SSL_provide_quic_data(
+ if (datalen &&
+ SSL_provide_quic_data(
ssl,
ngtcp2_crypto_quictls_from_ngtcp2_encryption_level(encryption_level),
data, datalen) != 1) {
@@ -920,6 +937,19 @@ int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
return 0;
}
+int ngtcp2_crypto_get_path_challenge_data2_cb(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data->data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
if (RAND_bytes(data, (int)datalen) != 1) {
return -1;
diff --git a/third_party/ngtcp2/crypto/shared.c b/third_party/ngtcp2/crypto/shared.c
index 98cd4de7e80..f1bb9026721 100644
--- a/third_party/ngtcp2/crypto/shared.c
+++ b/third_party/ngtcp2/crypto/shared.c
@@ -37,6 +37,22 @@
#include "ngtcp2_macro.h"
#include "ngtcp2_net.h"
+/*
+ * NGTCP2_INITIAL_SALT_V1 is a salt value which is used to derive
+ * initial secret. It is used for QUIC v1.
+ */
+static const uint8_t NGTCP2_INITIAL_SALT_V1[] = {
+ 0x38, 0x76, 0x2C, 0xF7, 0xF5, 0x59, 0x34, 0xB3, 0x4D, 0x17,
+ 0x9A, 0xE6, 0xA4, 0xC8, 0x0C, 0xAD, 0xCC, 0xBB, 0x7F, 0x0A};
+
+/*
+ * NGTCP2_INITIAL_SALT_V2 is a salt value which is used to derive
+ * initial secret. It is used for QUIC v2.
+ */
+static const uint8_t NGTCP2_INITIAL_SALT_V2[] = {
+ 0x0D, 0xED, 0xE3, 0xDE, 0xF7, 0x00, 0xA6, 0xDB, 0x81, 0x93,
+ 0x81, 0xBE, 0x6E, 0x26, 0x9D, 0xCB, 0xF9, 0xBD, 0x2E, 0xD9};
+
ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
void *md_native_handle) {
md->native_handle = md_native_handle;
@@ -53,9 +69,9 @@ int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
*p++ = (uint8_t)(destlen / 256);
*p++ = (uint8_t)(destlen % 256);
- *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen);
- memcpy(p, LABEL, sizeof(LABEL) - 1);
- p += sizeof(LABEL) - 1;
+ *p++ = (uint8_t)(ngtcp2_strlen_lit(LABEL) + labellen);
+ memcpy(p, LABEL, ngtcp2_strlen_lit(LABEL));
+ p += ngtcp2_strlen_lit(LABEL);
memcpy(p, label, labellen);
p += labellen;
*p++ = 0;
@@ -87,12 +103,12 @@ int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret,
switch (version) {
case NGTCP2_PROTO_VER_V1:
default:
- salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
- saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
+ salt = NGTCP2_INITIAL_SALT_V1;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V1);
break;
case NGTCP2_PROTO_VER_V2:
- salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2;
- saltlen = sizeof(NGTCP2_INITIAL_SALT_V2) - 1;
+ salt = NGTCP2_INITIAL_SALT_V2;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V2);
break;
}
@@ -111,10 +127,12 @@ int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret,
if (ngtcp2_crypto_hkdf_expand_label(
client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, initial_secret,
- NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL, sizeof(CLABEL) - 1) != 0 ||
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL,
+ ngtcp2_strlen_lit(CLABEL)) != 0 ||
ngtcp2_crypto_hkdf_expand_label(
server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, initial_secret,
- NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL, sizeof(SLABEL) - 1) != 0) {
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL,
+ ngtcp2_strlen_lit(SLABEL)) != 0) {
return -1;
}
@@ -148,19 +166,19 @@ int ngtcp2_crypto_derive_packet_protection_key(
switch (version) {
case NGTCP2_PROTO_VER_V2:
key_label = KEY_LABEL_V2;
- key_labellen = sizeof(KEY_LABEL_V2) - 1;
+ key_labellen = ngtcp2_strlen_lit(KEY_LABEL_V2);
iv_label = IV_LABEL_V2;
- iv_labellen = sizeof(IV_LABEL_V2) - 1;
+ iv_labellen = ngtcp2_strlen_lit(IV_LABEL_V2);
hp_key_label = HP_KEY_LABEL_V2;
- hp_key_labellen = sizeof(HP_KEY_LABEL_V2) - 1;
+ hp_key_labellen = ngtcp2_strlen_lit(HP_KEY_LABEL_V2);
break;
default:
key_label = KEY_LABEL_V1;
- key_labellen = sizeof(KEY_LABEL_V1) - 1;
+ key_labellen = ngtcp2_strlen_lit(KEY_LABEL_V1);
iv_label = IV_LABEL_V1;
- iv_labellen = sizeof(IV_LABEL_V1) - 1;
+ iv_labellen = ngtcp2_strlen_lit(IV_LABEL_V1);
hp_key_label = HP_KEY_LABEL_V1;
- hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1;
+ hp_key_labellen = ngtcp2_strlen_lit(HP_KEY_LABEL_V1);
}
if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
@@ -194,11 +212,11 @@ int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, uint32_t version,
switch (version) {
case NGTCP2_PROTO_VER_V2:
label = LABEL_V2;
- labellen = sizeof(LABEL_V2) - 1;
+ labellen = ngtcp2_strlen_lit(LABEL_V2);
break;
default:
label = LABEL;
- labellen = sizeof(LABEL) - 1;
+ labellen = ngtcp2_strlen_lit(LABEL);
}
if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen,
@@ -461,7 +479,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
if (ngtcp2_conn_is_server(conn) &&
crypto_set_local_transport_params(conn, tls) != 0) {
- goto fail;
+ /* Just return -1 because aead_ctx and hp_ctx are now owned by
+ conn. */
+ return -1;
}
break;
@@ -590,11 +610,11 @@ int ngtcp2_crypto_derive_and_install_initial_key(
case NGTCP2_PROTO_VER_V1:
default:
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
- retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ retry_noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V1);
break;
case NGTCP2_PROTO_VER_V2:
retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2;
- retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ retry_noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V2);
break;
}
@@ -843,7 +863,7 @@ int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token,
if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN,
ngtcp2_crypto_md_sha256(&md), secret, secretlen,
cid->data, cid->datalen, info,
- sizeof(info) - 1) != 0) {
+ ngtcp2_strlen_lit(info)) != 0) {
return -1;
}
@@ -863,8 +883,8 @@ static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
uint8_t *p;
assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret));
- assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info));
- assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info));
+ assert(info_prefixlen + ngtcp2_strlen_lit(key_info_suffix) <= sizeof(info));
+ assert(info_prefixlen + ngtcp2_strlen_lit(iv_info_suffix) <= sizeof(info));
if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt,
saltlen) != 0) {
@@ -874,8 +894,8 @@ static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
memcpy(info, info_prefix, info_prefixlen);
p = info + info_prefixlen;
- memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1);
- p += sizeof(key_info_suffix) - 1;
+ memcpy(p, key_info_suffix, ngtcp2_strlen_lit(key_info_suffix));
+ p += ngtcp2_strlen_lit(key_info_suffix);
if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret),
info, (size_t)(p - info)) != 0) {
@@ -884,8 +904,8 @@ static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
p = info + info_prefixlen;
- memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1);
- p += sizeof(iv_info_suffix) - 1;
+ memcpy(p, iv_info_suffix, ngtcp2_strlen_lit(iv_info_suffix));
+ p += ngtcp2_strlen_lit(iv_info_suffix);
if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret),
info, (size_t)(p - info)) != 0) {
@@ -918,8 +938,8 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
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[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
+ uint8_t plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN +
+ sizeof(ngtcp2_tstamp)] = {0};
uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
uint8_t key[16];
uint8_t iv[12];
@@ -938,8 +958,6 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
assert((size_t)remote_addrlen <= sizeof(ngtcp2_sockaddr_union));
- memset(plaintext, 0, sizeof(plaintext));
-
*p++ = (uint8_t)odcid->datalen;
memcpy(p, odcid->data, odcid->datalen);
p += NGTCP2_MAX_CIDLEN;
@@ -961,10 +979,10 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
assert(sizeof(key) == keylen);
assert(sizeof(iv) == ivlen);
- if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
- rand_data, sizeof(rand_data),
- retry_token_info_prefix,
- sizeof(retry_token_info_prefix) - 1) != 0) {
+ if (crypto_derive_token_key(
+ key, keylen, iv, ivlen, &md, secret, secretlen, rand_data,
+ sizeof(rand_data), retry_token_info_prefix,
+ ngtcp2_strlen_lit(retry_token_info_prefix)) != 0) {
return -1;
}
@@ -1038,10 +1056,10 @@ int ngtcp2_crypto_verify_retry_token(
assert(sizeof(key) == keylen);
assert(sizeof(iv) == ivlen);
- if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
- rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
- retry_token_info_prefix,
- sizeof(retry_token_info_prefix) - 1) != 0) {
+ if (crypto_derive_token_key(
+ key, keylen, iv, ivlen, &md, secret, secretlen, rand_data,
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, retry_token_info_prefix,
+ ngtcp2_strlen_lit(retry_token_info_prefix)) != 0) {
return -1;
}
@@ -1100,7 +1118,7 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token2(
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) {
uint8_t plaintext[sizeof(ngtcp2_sockaddr_union) + /* cid len = */ 1 +
- NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
+ NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)] = {0};
uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
uint8_t key[16];
uint8_t iv[12];
@@ -1117,8 +1135,6 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token2(
assert((size_t)remote_addrlen <= sizeof(ngtcp2_sockaddr_union));
- memset(plaintext, 0, sizeof(plaintext));
-
memcpy(p, remote_addr, (size_t)remote_addrlen);
p += sizeof(ngtcp2_sockaddr_union);
*p++ = (uint8_t)odcid->datalen;
@@ -1141,10 +1157,10 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token2(
assert(sizeof(key) == keylen);
assert(sizeof(iv) == ivlen);
- if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
- rand_data, sizeof(rand_data),
- retry_token_info_prefix2,
- sizeof(retry_token_info_prefix2) - 1) != 0) {
+ if (crypto_derive_token_key(
+ key, keylen, iv, ivlen, &md, secret, secretlen, rand_data,
+ sizeof(rand_data), retry_token_info_prefix2,
+ ngtcp2_strlen_lit(retry_token_info_prefix2)) != 0) {
return -1;
}
@@ -1219,10 +1235,10 @@ int ngtcp2_crypto_verify_retry_token2(
assert(sizeof(key) == keylen);
assert(sizeof(iv) == ivlen);
- if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
- rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
- retry_token_info_prefix2,
- sizeof(retry_token_info_prefix2) - 1) != 0) {
+ if (crypto_derive_token_key(
+ key, keylen, iv, ivlen, &md, secret, secretlen, rand_data,
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, retry_token_info_prefix2,
+ ngtcp2_strlen_lit(retry_token_info_prefix2)) != 0) {
return NGTCP2_CRYPTO_ERR_INTERNAL;
}
@@ -1305,13 +1321,22 @@ static size_t crypto_generate_regular_token_aad(uint8_t *dest,
return addrlen;
}
+/* NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN is the maximum length of
+ opaque data embedded in a regular token. */
+#define NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN 256
+
+/* NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN is the maximum length
+ of plaintext included in a regular token. */
+#define NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN \
+ (sizeof(ngtcp2_tstamp) + NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN)
+
static const uint8_t regular_token_info_prefix[] = "regular_token";
-ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+static ngtcp2_ssize crypto_generate_regular_token(
uint8_t *token, const uint8_t *secret, size_t secretlen,
const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
- ngtcp2_tstamp ts) {
- uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ const void *data, size_t datalen, ngtcp2_tstamp ts) {
+ uint8_t plaintext[NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN];
uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
uint8_t key[16];
uint8_t iv[12];
@@ -1328,9 +1353,18 @@ ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
int rv;
(void)remote_addrlen;
+ if (datalen > NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_DATALEN) {
+ return -1;
+ }
+
memcpy(p, &ts_be, sizeof(ts_be));
p += sizeof(ts_be);
+ if (datalen) {
+ memcpy(p, data, datalen);
+ p += datalen;
+ }
+
plaintextlen = (size_t)(p - plaintext);
if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
@@ -1346,10 +1380,10 @@ ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
assert(sizeof(key) == keylen);
assert(sizeof(iv) == ivlen);
- if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
- rand_data, sizeof(rand_data),
- regular_token_info_prefix,
- sizeof(regular_token_info_prefix) - 1) != 0) {
+ if (crypto_derive_token_key(
+ key, keylen, iv, ivlen, &md, secret, secretlen, rand_data,
+ sizeof(rand_data), regular_token_info_prefix,
+ ngtcp2_strlen_lit(regular_token_info_prefix)) != 0) {
return -1;
}
@@ -1378,13 +1412,11 @@ ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
return p - token;
}
-int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
- const uint8_t *secret, size_t secretlen,
- const ngtcp2_sockaddr *remote_addr,
- ngtcp2_socklen remote_addrlen,
- ngtcp2_duration timeout,
- ngtcp2_tstamp ts) {
- uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+static ngtcp2_ssize crypto_verify_regular_token(
+ void *data, size_t max_datalen, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
+ uint8_t plaintext[NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_PLAINTEXTLEN];
uint8_t key[16];
uint8_t iv[12];
size_t keylen;
@@ -1397,13 +1429,14 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
const uint8_t *rand_data;
const uint8_t *ciphertext;
size_t ciphertextlen;
+ size_t datalen;
int rv;
ngtcp2_tstamp gen_ts;
(void)remote_addrlen;
- if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
+ if (tokenlen < NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) {
- return -1;
+ return NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN;
}
rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
@@ -1413,23 +1446,27 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
ngtcp2_crypto_aead_aes_128_gcm(&aead);
ngtcp2_crypto_md_sha256(&md);
+ if (ciphertextlen > sizeof(plaintext) + aead.max_overhead) {
+ return NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN;
+ }
+
keylen = ngtcp2_crypto_aead_keylen(&aead);
ivlen = ngtcp2_crypto_aead_noncelen(&aead);
assert(sizeof(key) == keylen);
assert(sizeof(iv) == ivlen);
- if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
- rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
- regular_token_info_prefix,
- sizeof(regular_token_info_prefix) - 1) != 0) {
- return -1;
+ if (crypto_derive_token_key(
+ key, keylen, iv, ivlen, &md, secret, secretlen, rand_data,
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, regular_token_info_prefix,
+ ngtcp2_strlen_lit(regular_token_info_prefix)) != 0) {
+ return NGTCP2_CRYPTO_ERR_INTERNAL;
}
aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
- return -1;
+ return NGTCP2_CRYPTO_ERR_INTERNAL;
}
rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
@@ -1438,19 +1475,74 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
if (rv != 0) {
- return -1;
+ return NGTCP2_CRYPTO_ERR_UNREADABLE_TOKEN;
}
memcpy(&gen_ts, plaintext, sizeof(gen_ts));
gen_ts = ngtcp2_ntohl64(gen_ts);
if (gen_ts + timeout <= ts) {
+ return NGTCP2_CRYPTO_ERR_VERIFY_TOKEN;
+ }
+
+ if (max_datalen == 0) {
+ return 0;
+ }
+
+ datalen = ciphertextlen - aead.max_overhead - sizeof(gen_ts);
+ if (datalen > max_datalen) {
+ return 0;
+ }
+
+ memcpy(data, plaintext + sizeof(gen_ts), datalen);
+
+ return (ngtcp2_ssize)datalen;
+}
+
+ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts) {
+ return crypto_generate_regular_token(token, secret, secretlen, remote_addr,
+ remote_addrlen, NULL, 0, ts);
+}
+
+int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen,
+ ngtcp2_duration timeout,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize datalen =
+ crypto_verify_regular_token(NULL, 0, token, tokenlen, secret, secretlen,
+ remote_addr, remote_addrlen, timeout, ts);
+
+ if (datalen < 0) {
return -1;
}
+ assert(0 == datalen);
+
return 0;
}
+ngtcp2_ssize ngtcp2_crypto_generate_regular_token2(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const void *data, size_t datalen, ngtcp2_tstamp ts) {
+ return crypto_generate_regular_token(token, secret, secretlen, remote_addr,
+ remote_addrlen, data, datalen, ts);
+}
+
+ngtcp2_ssize ngtcp2_crypto_verify_regular_token2(
+ void *data, size_t max_datalen, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
+ return crypto_verify_regular_token(data, max_datalen, token, tokenlen, secret,
+ secretlen, remote_addr, remote_addrlen,
+ timeout, ts);
+}
+
ngtcp2_ssize ngtcp2_crypto_write_connection_close(
uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
@@ -1523,11 +1615,11 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
case NGTCP2_PROTO_VER_V1:
default:
key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
- noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V1);
break;
case NGTCP2_PROTO_VER_V2:
key = (const uint8_t *)NGTCP2_RETRY_KEY_V2;
- noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V2);
break;
}
@@ -1637,8 +1729,21 @@ int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn,
(void)offset;
(void)user_data;
- if (ngtcp2_crypto_read_write_crypto_data(conn, encryption_level, data,
- datalen) != 0) {
+ rv =
+ ngtcp2_crypto_read_write_crypto_data(conn, encryption_level, data, datalen);
+ if (rv != 0) {
+ switch (rv) {
+ case /* NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_CLIENT_HELLO_CB */ -10001:
+ case /* NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_X509_LOOKUP */ -10002:
+ /* These errors are not unrecoverable error, and they just
+ indicate that handshake has been interrupted. ngtcp2 does
+ not mind whether handshake is interrupted or not. Just
+ return 0 in this case. There are OSSL version and they have
+ the same enum value, therefore we cannot enumerate them
+ here. */
+ return 0;
+ }
+
rv = ngtcp2_conn_get_tls_error(conn);
if (rv) {
return rv;
diff --git a/third_party/ngtcp2/crypto/shared.h b/third_party/ngtcp2/crypto/shared.h
index 34158d3d02d..c517b7f2249 100644
--- a/third_party/ngtcp2/crypto/shared.h
+++ b/third_party/ngtcp2/crypto/shared.h
@@ -31,26 +31,6 @@
#include <ngtcp2/ngtcp2_crypto.h>
-/**
- * @macro
- *
- * :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to
- * derive initial secret. It is used for QUIC v1.
- */
-#define NGTCP2_INITIAL_SALT_V1 \
- "\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` is a salt value which is used to
- * derive initial secret. It is used for QUIC v2.
- */
-#define NGTCP2_INITIAL_SALT_V2 \
- "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd" \
- "\x2e\xd9"
-
/* Maximum key usage (encryption) limits */
#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
diff --git a/third_party/ngtcp2/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in b/third_party/ngtcp2/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in
index 720c7849c0f..e9e670c44f9 100644
--- a/third_party/ngtcp2/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in
+++ b/third_party/ngtcp2/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in
@@ -31,3 +31,4 @@ URL: https://github.com/ngtcp2/ngtcp2
Version: @VERSION@
Libs: -L${libdir} -lngtcp2_crypto_wolfssl
Cflags: -I${includedir}
+Requires.private: libngtcp2, wolfssl
diff --git a/third_party/ngtcp2/crypto/wolfssl/wolfssl.c b/third_party/ngtcp2/crypto/wolfssl/wolfssl.c
index bc9d9d84a86..fa2b147b6ba 100644
--- a/third_party/ngtcp2/crypto/wolfssl/wolfssl.c
+++ b/third_party/ngtcp2/crypto/wolfssl/wolfssl.c
@@ -34,6 +34,7 @@
#include <wolfssl/ssl.h>
#include <wolfssl/quic.h>
+#include "ngtcp2_macro.h"
#include "shared.h"
#define PRINTF_DEBUG 0
@@ -55,7 +56,7 @@ ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_EVP_aes_128_gcm());
ctx->md.native_handle = (void *)wolfSSL_EVP_sha256();
- ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ctr();
+ ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ecb();
ctx->max_encryption = 0;
ctx->max_decryption_failure = 0;
return ctx;
@@ -106,6 +107,21 @@ static int supported_aead(const WOLFSSL_EVP_CIPHER *aead) {
wolfSSL_quic_aead_is_chacha20(aead) || wolfSSL_quic_aead_is_ccm(aead);
}
+static const WOLFSSL_EVP_CIPHER *
+crypto_aead_get_hp(const WOLFSSL_EVP_CIPHER *aead) {
+ switch (wolfSSL_EVP_CIPHER_nid(aead)) {
+ case NID_aes_128_gcm:
+ case NID_aes_128_ccm:
+ return wolfSSL_EVP_aes_128_ecb();
+ case NID_aes_256_gcm:
+ return wolfSSL_EVP_aes_256_ecb();
+ case NID_chacha20_poly1305:
+ return wolfSSL_EVP_chacha20();
+ default:
+ return NULL;
+ }
+}
+
ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
void *tls_native_handle) {
WOLFSSL *ssl = tls_native_handle;
@@ -121,7 +137,7 @@ ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
ngtcp2_crypto_aead_init(&ctx->aead, (void *)aead);
ctx->md.native_handle = (void *)wolfSSL_quic_get_md(ssl);
- ctx->hp.native_handle = (void *)wolfSSL_quic_get_hp(ssl);
+ ctx->hp.native_handle = (void *)crypto_aead_get_hp(aead);
ctx->max_encryption = crypto_aead_get_aead_max_encryption(aead);
ctx->max_decryption_failure =
crypto_aead_get_aead_max_decryption_failure(aead);
@@ -288,20 +304,33 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
const ngtcp2_crypto_cipher_ctx *hp_ctx,
const uint8_t *sample) {
- static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ static const uint8_t PLAINTEXT[16] = {0};
WOLFSSL_EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
int len;
(void)hp;
- if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) !=
- WOLFSSL_SUCCESS ||
- wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT,
- sizeof(PLAINTEXT) - 1) != WOLFSSL_SUCCESS ||
- wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len) !=
- WOLFSSL_SUCCESS) {
- DEBUG_MSG("WOLFSSL: hp_mask FAILED\n");
- return -1;
+ switch (wolfSSL_EVP_CIPHER_CTX_nid(actx)) {
+ case NID_aes_128_ecb:
+ case NID_aes_256_ecb:
+ if (!wolfSSL_EVP_CipherUpdate(actx, dest, &len, sample,
+ NGTCP2_HP_SAMPLELEN)) {
+ return -1;
+ }
+
+ break;
+ case NID_chacha20:
+ if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) !=
+ WOLFSSL_SUCCESS ||
+ wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT,
+ sizeof(PLAINTEXT)) != WOLFSSL_SUCCESS ||
+ wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT), &len) !=
+ WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: hp_mask FAILED\n");
+ return -1;
+ }
+
+ break;
}
return 0;
@@ -442,6 +471,19 @@ int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
return 0;
}
+int ngtcp2_crypto_get_path_challenge_data2_cb(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ DEBUG_MSG("WOLFSSL: get path challenge data\n");
+ if (wolfSSL_RAND_bytes(data->data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
DEBUG_MSG("WOLFSSL: get random\n");
if (wolfSSL_RAND_bytes(data, (int)datalen) != 1) {
diff --git a/third_party/ngtcp2/lib/CMakeLists.txt b/third_party/ngtcp2/lib/CMakeLists.txt
index dc390e723b5..d16eba05a3e 100644
--- a/third_party/ngtcp2/lib/CMakeLists.txt
+++ b/third_party/ngtcp2/lib/CMakeLists.txt
@@ -67,12 +67,17 @@ set(ngtcp2_SOURCES
ngtcp2_unreachable.c
ngtcp2_transport_params.c
ngtcp2_settings.c
+ ngtcp2_callbacks.c
ngtcp2_dcidtr.c
+ ngtcp2_pcg.c
+ ngtcp2_ratelim.c
+ ngtcp2_conn_info.c
)
set(ngtcp2_INCLUDE_DIRS
- "${CMAKE_CURRENT_SOURCE_DIR}/includes"
- "${CMAKE_CURRENT_BINARY_DIR}/includes"
+ "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/includes>"
+ "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/includes>"
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
set(NGTCP2_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
@@ -103,11 +108,7 @@ if(ENABLE_SHARED_LIB)
C_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON
)
- foreach(include_DIR IN LISTS ngtcp2_INCLUDE_DIRS)
- target_include_directories(ngtcp2 PUBLIC $<BUILD_INTERFACE:${include_DIR}>)
- endforeach()
-
- target_include_directories(ngtcp2 PUBLIC $<INSTALL_INTERFACE:./include>)
+ target_include_directories(ngtcp2 PUBLIC ${ngtcp2_INCLUDE_DIRS})
install(TARGETS ngtcp2
EXPORT ${NGTCP2_TARGETS_EXPORT_NAME}
@@ -126,11 +127,7 @@ if(ENABLE_STATIC_LIB)
C_VISIBILITY_PRESET hidden
)
target_compile_definitions(ngtcp2_static PUBLIC "-DNGTCP2_STATICLIB")
- foreach(include_DIR IN LISTS ngtcp2_INCLUDE_DIRS)
- target_include_directories(ngtcp2_static PUBLIC $<BUILD_INTERFACE:${include_DIR}>)
- endforeach()
-
- target_include_directories(ngtcp2_static PUBLIC $<INSTALL_INTERFACE:./include>)
+ target_include_directories(ngtcp2_static PUBLIC ${ngtcp2_INCLUDE_DIRS})
install(TARGETS ngtcp2_static
EXPORT ${NGTCP2_TARGETS_EXPORT_NAME}
diff --git a/third_party/ngtcp2/lib/Makefile.am b/third_party/ngtcp2/lib/Makefile.am
index c5231115c3d..8748ca5fb43 100644
--- a/third_party/ngtcp2/lib/Makefile.am
+++ b/third_party/ngtcp2/lib/Makefile.am
@@ -75,7 +75,11 @@ OBJECTS = \
ngtcp2_unreachable.c \
ngtcp2_transport_params.c \
ngtcp2_settings.c \
- ngtcp2_dcidtr.c
+ ngtcp2_callbacks.c \
+ ngtcp2_dcidtr.c \
+ ngtcp2_pcg.c \
+ ngtcp2_ratelim.c \
+ ngtcp2_conn_info.c
HFILES = \
ngtcp2_pkt.h \
@@ -120,7 +124,11 @@ HFILES = \
ngtcp2_unreachable.h \
ngtcp2_transport_params.h \
ngtcp2_settings.h \
+ ngtcp2_callbacks.h \
ngtcp2_dcidtr.h \
+ ngtcp2_pcg.h \
+ ngtcp2_ratelim.h \
+ ngtcp2_conn_info.h \
ngtcp2_conn_stat.h \
ngtcp2_pktns_id.h \
ngtcp2_tstamp.h
diff --git a/third_party/ngtcp2/lib/config.cmake.in b/third_party/ngtcp2/lib/config.cmake.in
index 435a4d4a500..fffcd517289 100644
--- a/third_party/ngtcp2/lib/config.cmake.in
+++ b/third_party/ngtcp2/lib/config.cmake.in
@@ -1,3 +1,6 @@
include(CMakeFindDependencyMacro)
+if("@HAVE_OSSL@")
+ find_dependency(OpenSSL)
+endif()
include("${CMAKE_CURRENT_LIST_DIR}/@NGTCP2_TARGETS_EXPORT_NAME@.cmake")
diff --git a/third_party/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/third_party/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
index d7a27b9213b..c71ff364e30 100644
--- a/third_party/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
+++ b/third_party/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
@@ -262,7 +262,7 @@ typedef struct ngtcp2_mem {
*
* :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1.
*/
-#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u)
+#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001U)
/**
* @macro
@@ -270,7 +270,7 @@ typedef struct ngtcp2_mem {
* :macro:`NGTCP2_PROTO_VER_V2` is the QUIC version 2. See
* :rfc:`9369`.
*/
-#define NGTCP2_PROTO_VER_V2 ((uint32_t)0x6b3343cfu)
+#define NGTCP2_PROTO_VER_V2 ((uint32_t)0x6B3343CFU)
/**
* @macro
@@ -294,7 +294,7 @@ typedef struct ngtcp2_mem {
* :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved
* version.
*/
-#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au
+#define NGTCP2_RESERVED_VERSION_MASK 0x0A0A0A0AU
/**
* @macrosection
@@ -306,15 +306,29 @@ typedef struct ngtcp2_mem {
* @macro
*
* :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP
- * datagram payload size that the local endpoint transmits.
+ * datagram payload size that the local endpoint transmits without
+ * Path MTU Discovery (PMTUD) or the custom settings (see
+ * :member:`ngtcp2_settings.max_tx_udp_payload_size` and
+ * :member:`ngtcp2_settings.no_tx_udp_payload_size_shaping`).
*/
#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200
/**
* @macro
*
- * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP
+ * :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE` is the maximum UDP datagram
+ * payload size that this library can output.
+ */
+#define NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE 65527
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` was the maximum UDP
* datagram payload size that Path MTU Discovery can discover.
+ *
+ * Deprecated since v1.17.0. Path MTU Discovery is not capped to this
+ * value anymore.
*/
#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452
@@ -363,7 +377,7 @@ typedef struct ngtcp2_mem {
* integrity tag of Retry packet. It is used for QUIC v1.
*/
#define NGTCP2_RETRY_KEY_V1 \
- "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+ "\xBE\x0C\x69\x0B\x9F\x66\x57\x5A\x1D\x76\x6B\x54\xE3\x68\xC8\x4E"
/**
* @macro
@@ -371,7 +385,7 @@ typedef struct ngtcp2_mem {
* :macro:`NGTCP2_RETRY_NONCE_V1` is nonce used when generating
* integrity tag of Retry packet. It is used for QUIC v1.
*/
-#define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
+#define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xD3\x5D\x63\x2B\xF2\x23\x98\x25\xBB"
/**
* @macro
@@ -381,7 +395,7 @@ typedef struct ngtcp2_mem {
* :rfc:`9369`.
*/
#define NGTCP2_RETRY_KEY_V2 \
- "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92"
+ "\x8F\xB4\xB0\x1B\x56\xAC\x48\xE2\x60\xFB\xCB\xCE\xAD\x7C\xCC\x92"
/**
* @macro
@@ -390,7 +404,7 @@ typedef struct ngtcp2_mem {
* integrity tag of Retry packet. It is used for QUIC v2. See
* :rfc:`9369`.
*/
-#define NGTCP2_RETRY_NONCE_V2 "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a"
+#define NGTCP2_RETRY_NONCE_V2 "\xD8\x69\x69\xBC\x2D\x7C\x6D\x99\x90\xEF\xB0\x4A"
/**
* @macro
@@ -798,7 +812,7 @@ typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
*
* :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_PKT_FLAG_NONE 0x00u
+#define NGTCP2_PKT_FLAG_NONE 0x00U
/**
* @macro
@@ -806,7 +820,7 @@ typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
* :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet
* header.
*/
-#define NGTCP2_PKT_FLAG_LONG_FORM 0x01u
+#define NGTCP2_PKT_FLAG_LONG_FORM 0x01U
/**
* @macro
@@ -814,14 +828,14 @@ typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
* :macro:`NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR` indicates that Fixed Bit
* (aka QUIC bit) is not set.
*/
-#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02u
+#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02U
/**
* @macro
*
* :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set.
*/
-#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04u
+#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04U
/**
* @enum
@@ -873,7 +887,7 @@ typedef enum ngtcp2_pkt_type {
*
* :macro:`NGTCP2_NO_ERROR` is QUIC transport error code ``NO_ERROR``.
*/
-#define NGTCP2_NO_ERROR 0x0u
+#define NGTCP2_NO_ERROR 0x0U
/**
* @macro
@@ -881,7 +895,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_INTERNAL_ERROR` is QUIC transport error code
* ``INTERNAL_ERROR``.
*/
-#define NGTCP2_INTERNAL_ERROR 0x1u
+#define NGTCP2_INTERNAL_ERROR 0x1U
/**
* @macro
@@ -889,7 +903,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_CONNECTION_REFUSED` is QUIC transport error code
* ``CONNECTION_REFUSED``.
*/
-#define NGTCP2_CONNECTION_REFUSED 0x2u
+#define NGTCP2_CONNECTION_REFUSED 0x2U
/**
* @macro
@@ -897,7 +911,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_FLOW_CONTROL_ERROR` is QUIC transport error code
* ``FLOW_CONTROL_ERROR``.
*/
-#define NGTCP2_FLOW_CONTROL_ERROR 0x3u
+#define NGTCP2_FLOW_CONTROL_ERROR 0x3U
/**
* @macro
@@ -905,7 +919,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_STREAM_LIMIT_ERROR` is QUIC transport error code
* ``STREAM_LIMIT_ERROR``.
*/
-#define NGTCP2_STREAM_LIMIT_ERROR 0x4u
+#define NGTCP2_STREAM_LIMIT_ERROR 0x4U
/**
* @macro
@@ -913,7 +927,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_STREAM_STATE_ERROR` is QUIC transport error code
* ``STREAM_STATE_ERROR``.
*/
-#define NGTCP2_STREAM_STATE_ERROR 0x5u
+#define NGTCP2_STREAM_STATE_ERROR 0x5U
/**
* @macro
@@ -921,7 +935,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_FINAL_SIZE_ERROR` is QUIC transport error code
* ``FINAL_SIZE_ERROR``.
*/
-#define NGTCP2_FINAL_SIZE_ERROR 0x6u
+#define NGTCP2_FINAL_SIZE_ERROR 0x6U
/**
* @macro
@@ -929,7 +943,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_FRAME_ENCODING_ERROR` is QUIC transport error code
* ``FRAME_ENCODING_ERROR``.
*/
-#define NGTCP2_FRAME_ENCODING_ERROR 0x7u
+#define NGTCP2_FRAME_ENCODING_ERROR 0x7U
/**
* @macro
@@ -937,7 +951,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_TRANSPORT_PARAMETER_ERROR` is QUIC transport error
* code ``TRANSPORT_PARAMETER_ERROR``.
*/
-#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u
+#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8U
/**
* @macro
@@ -945,7 +959,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_CONNECTION_ID_LIMIT_ERROR` is QUIC transport error
* code ``CONNECTION_ID_LIMIT_ERROR``.
*/
-#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u
+#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9U
/**
* @macro
@@ -953,7 +967,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_PROTOCOL_VIOLATION` is QUIC transport error code
* ``PROTOCOL_VIOLATION``.
*/
-#define NGTCP2_PROTOCOL_VIOLATION 0xau
+#define NGTCP2_PROTOCOL_VIOLATION 0xAU
/**
* @macro
@@ -961,7 +975,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_INVALID_TOKEN` is QUIC transport error code
* ``INVALID_TOKEN``.
*/
-#define NGTCP2_INVALID_TOKEN 0xbu
+#define NGTCP2_INVALID_TOKEN 0xBU
/**
* @macro
@@ -969,7 +983,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_APPLICATION_ERROR` is QUIC transport error code
* ``APPLICATION_ERROR``.
*/
-#define NGTCP2_APPLICATION_ERROR 0xcu
+#define NGTCP2_APPLICATION_ERROR 0xCU
/**
* @macro
@@ -977,7 +991,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_CRYPTO_BUFFER_EXCEEDED` is QUIC transport error code
* ``CRYPTO_BUFFER_EXCEEDED``.
*/
-#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu
+#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xDU
/**
* @macro
@@ -985,7 +999,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_KEY_UPDATE_ERROR` is QUIC transport error code
* ``KEY_UPDATE_ERROR``.
*/
-#define NGTCP2_KEY_UPDATE_ERROR 0xeu
+#define NGTCP2_KEY_UPDATE_ERROR 0xEU
/**
* @macro
@@ -993,7 +1007,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_AEAD_LIMIT_REACHED` is QUIC transport error code
* ``AEAD_LIMIT_REACHED``.
*/
-#define NGTCP2_AEAD_LIMIT_REACHED 0xfu
+#define NGTCP2_AEAD_LIMIT_REACHED 0xFU
/**
* @macro
@@ -1001,7 +1015,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_NO_VIABLE_PATH` is QUIC transport error code
* ``NO_VIABLE_PATH``.
*/
-#define NGTCP2_NO_VIABLE_PATH 0x10u
+#define NGTCP2_NO_VIABLE_PATH 0x10U
/**
* @macro
@@ -1009,7 +1023,7 @@ typedef enum ngtcp2_pkt_type {
* :macro:`NGTCP2_CRYPTO_ERROR` is QUIC transport error code
* ``CRYPTO_ERROR``.
*/
-#define NGTCP2_CRYPTO_ERROR 0x100u
+#define NGTCP2_CRYPTO_ERROR 0x100U
/**
* @macro
@@ -1173,6 +1187,9 @@ typedef struct ngtcp2_pkt_hd {
* @struct
*
* :type:`ngtcp2_pkt_stateless_reset` represents Stateless Reset.
+ *
+ * Deprecated since v1.22.0. Use :type:`ngtcp2_pkt_stateless_reset2`
+ * instead.
*/
typedef struct ngtcp2_pkt_stateless_reset {
/**
@@ -1190,6 +1207,40 @@ typedef struct ngtcp2_pkt_stateless_reset {
size_t randlen;
} ngtcp2_pkt_stateless_reset;
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_stateless_reset_token` stores stateless reset token.
+ *
+ * This struct has been available since v1.22.0.
+ */
+typedef struct ngtcp2_stateless_reset_token {
+ uint8_t data[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_stateless_reset_token;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_stateless_reset2` represents Stateless Reset.
+ *
+ * This struct has been available since v1.22.0.
+ */
+typedef struct ngtcp2_pkt_stateless_reset2 {
+ /**
+ * :member:`token` contains stateless reset token.
+ */
+ ngtcp2_stateless_reset_token token;
+ /**
+ * :member:`rand` points a buffer which contains random bytes
+ * section.
+ */
+ const uint8_t *rand;
+ /**
+ * :member:`randlen` is the number of random bytes.
+ */
+ size_t randlen;
+} ngtcp2_pkt_stateless_reset2;
+
/**
* @macrosection
*
@@ -1236,7 +1287,7 @@ typedef struct ngtcp2_pkt_stateless_reset {
* :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1` is TLS
* extension type of quic_transport_parameters.
*/
-#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39u
+#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39U
#ifdef NGTCP2_USE_GENERIC_SOCKADDR
# ifndef NGTCP2_AF_INET
@@ -1267,7 +1318,7 @@ typedef struct ngtcp2_sockaddr_in {
} ngtcp2_sockaddr_in;
typedef struct ngtcp2_in6_addr {
- uint8_t in6_addr[16];
+ uint8_t s6_addr[16];
} ngtcp2_in6_addr;
typedef struct ngtcp2_sockaddr_in6 {
@@ -1561,7 +1612,8 @@ typedef struct ngtcp2_transport_params {
} ngtcp2_transport_params;
#define NGTCP2_CONN_INFO_V1 1
-#define NGTCP2_CONN_INFO_VERSION NGTCP2_CONN_INFO_V1
+#define NGTCP2_CONN_INFO_V2 2
+#define NGTCP2_CONN_INFO_VERSION NGTCP2_CONN_INFO_V2
/**
* @struct
@@ -1600,6 +1652,52 @@ typedef struct ngtcp2_conn_info {
* packets which have not been acknowledged.
*/
uint64_t bytes_in_flight;
+ /* The following fields have been added since NGTCP2_CONN_INFO_V2. */
+ /**
+ * :member:`pkt_sent` is the number of QUIC packets sent. This
+ * field has been available since v1.16.0.
+ */
+ uint64_t pkt_sent;
+ /**
+ * :member:`bytes_sent` is the number of bytes (the sum of QUIC
+ * packet length) sent. This field has been available since
+ * v1.16.0.
+ */
+ uint64_t bytes_sent;
+ /**
+ * :member:`pkt_recv` is the number of QUIC packets received,
+ * excluding discarded ones. This field has been available since
+ * v1.16.0.
+ */
+ uint64_t pkt_recv;
+ /**
+ * :member:`bytes_recv` is the number of bytes (the sum of QUIC
+ * packet length) received, excluding discarded ones. This field
+ * has been available since v1.16.0.
+ */
+ uint64_t bytes_recv;
+ /**
+ * :member:`pkt_lost` is the number of QUIC packets that are
+ * considered lost, excluding PMTUD packets. This field has been
+ * available since v1.16.0.
+ */
+ uint64_t pkt_lost;
+ /**
+ * :member:`bytes_lost` is the number of bytes (the sum of QUIC
+ * packet length) lost, excluding PMTUD packets. This field has
+ * been available since v1.16.0.
+ */
+ uint64_t bytes_lost;
+ /**
+ * :member:`ping_recv` is the number of PING frames received. This
+ * field has been available since v1.16.0.
+ */
+ uint64_t ping_recv;
+ /**
+ * :member:`pkt_discarded` is the number of QUIC packets discarded.
+ * This field has been available since v1.16.0.
+ */
+ uint64_t pkt_discarded;
} ngtcp2_conn_info;
/**
@@ -1642,14 +1740,14 @@ typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...);
*
* :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00u
+#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00U
/**
* @macro
*
* :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the
* final call to :type:`ngtcp2_qlog_write` in the current connection.
*/
-#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01u
+#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01U
/**
* @struct
@@ -1704,7 +1802,8 @@ typedef enum ngtcp2_token_type {
#define NGTCP2_SETTINGS_V1 1
#define NGTCP2_SETTINGS_V2 2
-#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_V2
+#define NGTCP2_SETTINGS_V3 3
+#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_V3
/**
* @struct
@@ -1738,7 +1837,9 @@ typedef struct ngtcp2_settings {
ngtcp2_printf log_printf;
/**
* :member:`max_tx_udp_payload_size` is the maximum size of UDP
- * datagram payload that the local endpoint transmits.
+ * datagram payload that the local endpoint transmits. This must be
+ * larger than or equal to :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`, and
+ * less then or equal to :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE`.
*/
size_t max_tx_udp_payload_size;
/**
@@ -1802,8 +1903,8 @@ typedef struct ngtcp2_settings {
uint64_t max_stream_window;
/**
* :member:`ack_thresh` is the minimum number of the received ACK
- * eliciting packets that trigger the immediate acknowledgement from
- * the local endpoint.
+ * eliciting packets that triggers the immediate acknowledgement
+ * from the local endpoint.
*/
size_t ack_thresh;
/**
@@ -1903,12 +2004,13 @@ typedef struct ngtcp2_settings {
/**
* :member:`pmtud_probes` is the array of UDP datagram payload size
* to probe during Path MTU Discovery. The discovery is done in the
- * order appeared in this array. The size must be strictly larger
- * than 1200, otherwise the behavior is undefined. The maximum
- * value in this array should be set to
- * :member:`max_tx_udp_payload_size`. If this field is not set, the
- * predefined PMTUD probes are made. This field has been available
- * since v1.4.0.
+ * order appeared in this array. The payload size must be strictly
+ * larger than :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`, and less than
+ * or equal to :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE`. Otherwise
+ * the behavior is undefined. The maximum value in this array
+ * should be set to :member:`max_tx_udp_payload_size`. If this
+ * field is not set, the predefined PMTUD probes are made. This
+ * field has been available since v1.4.0.
*/
const uint16_t *pmtud_probes;
/**
@@ -1917,6 +2019,23 @@ typedef struct ngtcp2_settings {
* field has been available since v1.4.0.
*/
size_t pmtud_probeslen;
+ /* The following fields have been added since NGTCP2_SETTINGS_V3. */
+ /**
+ * :member:`glitch_ratelim_burst` is the maximum number of tokens
+ * available to "glitch" rate limiter. "glitch" is a suspicious
+ * activity from a remote endpoint. If detected, certain amount of
+ * tokens are consumed. If no tokens are available to consume, the
+ * connection is closed. The rate of token generation is specified
+ * by :member:`glitch_ratelim_rate`. This field has been available
+ * since v1.15.0.
+ */
+ uint64_t glitch_ratelim_burst;
+ /**
+ * :member:`glitch_ratelim_rate` is the number of tokens generated
+ * per second. See :member:`glitch_ratelim_burst` for "glitch" rate
+ * limiter. This field has been available since v1.15.0.
+ */
+ uint64_t glitch_ratelim_rate;
} ngtcp2_settings;
/**
@@ -2362,11 +2481,42 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest,
* :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
* |randlen| is strictly less than
* :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN`.
+ *
+ * Deprecated since v1.22.0. Use `ngtcp2_pkt_write_stateless_reset2`
+ * instead.
*/
NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset(
uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token,
const uint8_t *rand, size_t randlen);
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_stateless_reset2` writes Stateless Reset packet
+ * in the buffer pointed by |dest| whose length is |destlen|. |token|
+ * must store the Stateless Reset Token. |rand| specifies the random
+ * octets preceding Stateless Reset Token. The length of |rand| is
+ * specified by |randlen| which must be at least
+ * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` bytes long.
+ *
+ * If |randlen| is too long to write them all in the buffer, |rand| is
+ * written to the buffer as much as possible, and is truncated.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |randlen| is strictly less than
+ * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN`.
+ *
+ * This function has been available since v1.22.0.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset2(
+ uint8_t *dest, size_t destlen, const ngtcp2_stateless_reset_token *token,
+ const uint8_t *rand, size_t randlen);
+
/**
* @function
*
@@ -2662,7 +2812,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
*
* :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00u
+#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00U
/**
* @macro
@@ -2670,7 +2820,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
* :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of
* data is final piece of an incoming stream.
*/
-#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01u
+#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01U
/**
* @macro
@@ -2679,7 +2829,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
* data contains data received in 0-RTT packet, and the handshake has
* not completed yet, which means that the data might be replayed.
*/
-#define NGTCP2_STREAM_DATA_FLAG_0RTT 0x02u
+#define NGTCP2_STREAM_DATA_FLAG_0RTT 0x02U
/**
* @functypedef
@@ -2734,7 +2884,7 @@ typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id,
*
* :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00u
+#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00U
/**
* @macro
@@ -2742,7 +2892,7 @@ typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id,
* :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` indicates that
* app_error_code parameter is set.
*/
-#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01u
+#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01U
/**
* @functypedef
@@ -2823,6 +2973,9 @@ typedef int (*ngtcp2_acked_stream_data_offset)(
* The implementation of this callback should return 0 if it succeeds.
* Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
* call return immediately.
+ *
+ * Deprecated since v1.22.0. Use :type:`ngtcp2_recv_stateless_reset2`
+ * instead.
*/
typedef int (*ngtcp2_recv_stateless_reset)(ngtcp2_conn *conn,
const ngtcp2_pkt_stateless_reset *sr,
@@ -2887,6 +3040,9 @@ typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
* The callback function must return 0 if it succeeds. Returning
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
* immediately.
+ *
+ * Deprecated since v1.22.0. Use
+ * :type:`ngtcp2_get_new_connection_id2` instead.
*/
typedef int (*ngtcp2_get_new_connection_id)(ngtcp2_conn *conn, ngtcp2_cid *cid,
uint8_t *token, size_t cidlen,
@@ -2951,7 +3107,7 @@ typedef int (*ngtcp2_update_key)(
*
* :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00u
+#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00U
/**
* @macro
@@ -2960,7 +3116,7 @@ typedef int (*ngtcp2_update_key)(
* validation involving server preferred address. This flag is only
* set for client.
*/
-#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01u
+#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01U
/**
* @macro
@@ -2969,7 +3125,29 @@ typedef int (*ngtcp2_update_key)(
* server should send NEW_TOKEN frame for the new remote address.
* This flag is only set for server.
*/
-#define NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN 0x02u
+#define NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN 0x02U
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_begin_path_validation` is a callback function which
+ * is called when the path validation has started. |flags| is zero or
+ * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_*
+ * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |path| is the path that is
+ * being validated. |fallback_path|, if not NULL, is the path that is
+ * used when this validation fails.
+ *
+ * Currently, the flags may only contain
+ * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR`.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_begin_path_validation)(ngtcp2_conn *conn, uint32_t flags,
+ const ngtcp2_path *path,
+ const ngtcp2_path *fallback_path,
+ void *user_data);
/**
* @functypedef
@@ -2978,9 +3156,8 @@ typedef int (*ngtcp2_update_key)(
* an application the outcome of path validation. |flags| is zero or
* more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_*
* <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |path| is the path that was
- * validated. |old_path| is the path that is previously used before a
- * local endpoint has migrated to |path| if |old_path| is not NULL.
- * If |res| is
+ * validated. |fallback_path|, if not NULL, is the path that is used
+ * if the path validation failed. 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`,
@@ -2992,7 +3169,7 @@ typedef int (*ngtcp2_update_key)(
*/
typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags,
const ngtcp2_path *path,
- const ngtcp2_path *old_path,
+ const ngtcp2_path *fallback_path,
ngtcp2_path_validation_result res,
void *user_data);
@@ -3063,6 +3240,9 @@ typedef enum ngtcp2_connection_id_status_type {
* The callback function must return 0 if it succeeds. Returning
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
* immediately.
+ *
+ * Deprecated since v1.22.0. Use :type:`ngtcp2_connection_id_status2`
+ * instead.
*/
typedef int (*ngtcp2_connection_id_status)(
ngtcp2_conn *conn, ngtcp2_connection_id_status_type type, uint64_t seq,
@@ -3118,7 +3298,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
*
* :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_DATAGRAM_FLAG_NONE 0x00u
+#define NGTCP2_DATAGRAM_FLAG_NONE 0x00U
/**
* @macro
@@ -3127,7 +3307,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
* received in 0-RTT packet, and the handshake has not completed yet,
* which means that the data might be replayed.
*/
-#define NGTCP2_DATAGRAM_FLAG_0RTT 0x01u
+#define NGTCP2_DATAGRAM_FLAG_0RTT 0x01U
/**
* @functypedef
@@ -3190,6 +3370,9 @@ typedef int (*ngtcp2_lost_datagram)(ngtcp2_conn *conn, uint64_t dgram_id,
* The callback function must return 0 if it succeeds. Returning
* :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
* immediately.
+ *
+ * Deprecated since v1.22.0. Use
+ * :type:`ngtcp2_get_path_challenge_data2` instead.
*/
typedef int (*ngtcp2_get_path_challenge_data)(ngtcp2_conn *conn, uint8_t *data,
void *user_data);
@@ -3261,8 +3444,99 @@ typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_encryption_level level,
typedef int (*ngtcp2_tls_early_data_rejected)(ngtcp2_conn *conn,
void *user_data);
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_stateless_reset2` is a callback function which
+ * is called when Stateless Reset packet is received. The stateless
+ * reset details are given in |sr|.
+ *
+ * The implementation of this callback should return 0 if it succeeds.
+ * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
+ * call return immediately.
+ *
+ * This type has been available since v1.22.0
+ */
+typedef int (*ngtcp2_recv_stateless_reset2)(
+ ngtcp2_conn *conn, const ngtcp2_pkt_stateless_reset2 *sr, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_get_new_connection_id2` is a callback function to ask
+ * an application for new connection ID. Application must generate
+ * new unused connection ID with the exact |cidlen| bytes, and store
+ * it in |cid|. It also has to generate a stateless reset token, and
+ * store it in |token|.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ *
+ * This type has been available since v1.22.0
+ */
+typedef int (*ngtcp2_get_new_connection_id2)(
+ ngtcp2_conn *conn, ngtcp2_cid *cid, ngtcp2_stateless_reset_token *token,
+ size_t cidlen, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_connection_id_status2` is a callback function which
+ * is called when the status of Destination Connection ID changes.
+ *
+ * |token| is the associated stateless reset token, and it is ``NULL``
+ * if no token is present.
+ *
+ * |type| is the one of the value defined in
+ * :type:`ngtcp2_connection_id_status_type`. The new value might be
+ * added in the future release.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ *
+ * This type has been available since v1.22.0
+ */
+typedef int (*ngtcp2_connection_id_status2)(
+ ngtcp2_conn *conn, ngtcp2_connection_id_status_type type, uint64_t seq,
+ const ngtcp2_cid *cid, const ngtcp2_stateless_reset_token *token,
+ void *user_data);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_path_challenge_data` stores path challenge data.
+ *
+ * This type has been available since v1.22.0.
+ */
+typedef struct ngtcp2_path_challenge_data {
+ uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
+} ngtcp2_path_challenge_data;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_get_path_challenge_data2` is a callback function to
+ * ask an application for new data that is sent in PATH_CHALLENGE
+ * frame. Application must generate new unpredictable, exactly
+ * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes of random data, and
+ * store them into |data|.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ *
+ * This type has been available since v1.22.0.
+ */
+typedef int (*ngtcp2_get_path_challenge_data2)(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data,
+ void *user_data);
+
#define NGTCP2_CALLBACKS_V1 1
-#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V1
+#define NGTCP2_CALLBACKS_V2 2
+#define NGTCP2_CALLBACKS_V3 3
+#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V3
/**
* @struct
@@ -3349,6 +3623,11 @@ typedef struct ngtcp2_callbacks {
* :member:`recv_stateless_reset` is a callback function which is
* invoked when Stateless Reset packet is received. This callback
* function is optional.
+ *
+ * Deprecated since v1.22.0. Use :member:`recv_stateless_reset2`
+ * instead. If both :member:`recv_stateless_reset` and
+ * :member:`recv_stateless_reset2` are set, the latter has the
+ * precedence.
*/
ngtcp2_recv_stateless_reset recv_stateless_reset;
/**
@@ -3379,8 +3658,14 @@ typedef struct ngtcp2_callbacks {
ngtcp2_rand rand;
/**
* :member:`get_new_connection_id` is a callback function which is
- * invoked when the library needs new connection ID. This callback
- * function must be specified.
+ * invoked when the library needs new connection ID. Either this
+ * callback function or :member:`get_new_connection_id2` must be
+ * specified.
+ *
+ * Deprecated since v1.22.0. Use :member:`get_new_connection_id2`
+ * instead. If both :member:`get_new_connection_id` and
+ * :member:`get_new_connection_id2` are set, the latter has the
+ * precedence.
*/
ngtcp2_get_new_connection_id get_new_connection_id;
/**
@@ -3441,6 +3726,10 @@ typedef struct ngtcp2_callbacks {
* when the new Destination Connection ID is activated, or the
* activated Destination Connection ID is now deactivated. This
* callback function is optional.
+ *
+ * Deprecated since v1.22.0. Use :member:`dcid_status2` instead.
+ * If both :member:`dcid_status` and :member:`dcid_status2` are set,
+ * the latter has the precedence.
*/
ngtcp2_connection_id_status dcid_status;
/**
@@ -3491,6 +3780,9 @@ typedef struct ngtcp2_callbacks {
* :member:`get_path_challenge_data` is a callback function which is
* invoked when the library needs new data sent along with
* PATH_CHALLENGE frame. This callback must be specified.
+ *
+ * Deprecated since v1.22.0. Use :member:`get_path_challenge_data2`
+ * instead.
*/
ngtcp2_get_path_challenge_data get_path_challenge_data;
/**
@@ -3527,6 +3819,42 @@ typedef struct ngtcp2_callbacks {
* is only used by client.
*/
ngtcp2_tls_early_data_rejected tls_early_data_rejected;
+ /* The following fields have been added since NGTCP2_CALLBACKS_V2. */
+ /**
+ * :member:`begin_path_validation` is a callback function which is
+ * invoked when a path validation has started. This field is
+ * available since v1.14.0.
+ */
+ ngtcp2_begin_path_validation begin_path_validation;
+ /* The following fields have been added since NGTCP2_CALLBACKS_V3. */
+ /**
+ * :member:`recv_stateless_reset2` is a callback function which is
+ * invoked when Stateless Reset packet is received. This callback
+ * function is optional. This field is available since v1.22.0.
+ */
+ ngtcp2_recv_stateless_reset2 recv_stateless_reset2;
+ /**
+ * :member:`get_new_connection_id2` is a callback function which is
+ * invoked when the library needs new connection ID. This callback
+ * function must be specified. This field is available since
+ * v1.22.0.
+ */
+ ngtcp2_get_new_connection_id2 get_new_connection_id2;
+ /**
+ * :member:`dcid_status2` is a callback function which is invoked
+ * when the new Destination Connection ID is activated, or the
+ * activated Destination Connection ID is now deactivated. This
+ * callback function is optional. This field is available since
+ * v1.22.0.
+ */
+ ngtcp2_connection_id_status2 dcid_status2;
+ /**
+ * :member:`get_path_challenge_data2` is a callback function which
+ * is invoked when the library needs new data sent along with
+ * PATH_CHALLENGE frame. This callback must be specified. This
+ * field is available since v1.22.0.
+ */
+ ngtcp2_get_path_challenge_data2 get_path_challenge_data2;
} ngtcp2_callbacks;
/**
@@ -3742,6 +4070,21 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(
ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts);
+/**
+ * @function
+ *
+ * `ngtcp2_conn_continue_handshake` resumes handshake interrupted by
+ * TLS stack routine (e.g., private key operation offloading,
+ * certificate lookup, etc).
+ *
+ * This function returns 0 if it succeeds. In general, this function
+ * returns the same set of error codes from `ngtcp2_conn_read_pkt`.
+ *
+ * This function has been available since v1.22.0.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_continue_handshake(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
/**
* @function
*
@@ -4061,9 +4404,7 @@ NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
* `ngtcp2_conn_get_expiry` returns the next expiry time. It returns
* ``UINT64_MAX`` if there is no next expiry.
*
- * Call `ngtcp2_conn_handle_expiry` and then
- * `ngtcp2_conn_writev_stream` (or `ngtcp2_conn_writev_datagram`) when
- * the expiry time has passed.
+ * Call `ngtcp2_conn_handle_expiry` when the expiry time has passed.
*/
NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
@@ -4071,6 +4412,20 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
* @function
*
* `ngtcp2_conn_handle_expiry` handles expired timer.
+ *
+ * If it returns :macro:`NGTCP2_ERR_IDLE_CLOSE`, it means that an idle
+ * timer has fired for this particular connection. In this case, drop
+ * the connection without calling
+ * `ngtcp2_conn_write_connection_close`. If it returns any of the
+ * other negative error codes, close the connection by sending the
+ * terminal packet produced by `ngtcp2_conn_write_connection_close`.
+ * Otherwise, schedule `ngtcp2_conn_writev_stream` call. An
+ * application may call any number of additional
+ * `ngtcp2_conn_read_pkt` and `ngtcp2_conn_handle_expiry` before
+ * calling `ngtcp2_conn_writev_stream`. After calling
+ * `ngtcp2_conn_writev_stream`, new expiry is set. The application
+ * should call `ngtcp2_conn_get_expiry` to get a new deadline and set
+ * the timer.
*/
NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
ngtcp2_tstamp ts);
@@ -4389,7 +4744,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
*
* :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00u
+#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00U
/**
* @macro
@@ -4397,7 +4752,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
* :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may
* come, and should be coalesced into the same packet if possible.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01u
+#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01U
/**
* @macro
@@ -4405,7 +4760,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
* :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that a passed data
* is the final part of a stream.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u
+#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02U
/**
* @macro
@@ -4416,7 +4771,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
* finalizing it. PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE
* only packets and PMTUD packets are excluded.
*/
-#define NGTCP2_WRITE_STREAM_FLAG_PADDING 0x04u
+#define NGTCP2_WRITE_STREAM_FLAG_PADDING 0x04U
/**
* @function
@@ -4601,7 +4956,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
*
* :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set.
*/
-#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00u
+#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00U
/**
* @macro
@@ -4609,7 +4964,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
* :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data
* may come, and should be coalesced into the same packet if possible.
*/
-#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u
+#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01U
/**
* @macro
@@ -4620,7 +4975,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
* finalizing it. PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE
* only packets and PMTUD packets are excluded.
*/
-#define NGTCP2_WRITE_DATAGRAM_FLAG_PADDING 0x02u
+#define NGTCP2_WRITE_DATAGRAM_FLAG_PADDING 0x02U
/**
* @function
@@ -4857,6 +5212,8 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest);
*
* :type:`ngtcp2_cid_token` is the convenient struct to store
* Connection ID, its associated path, and stateless reset token.
+ *
+ * Deprecated since v1.22.0. Use :type:`ngtcp2_cid_token2` instead.
*/
typedef struct ngtcp2_cid_token {
/**
@@ -4896,10 +5253,65 @@ typedef struct ngtcp2_cid_token {
* sizeof(:type:`ngtcp2_cid_token`) * n bytes available, where n is
* the return value of `ngtcp2_conn_get_active_dcid` with |dest| ==
* NULL.
+ *
+ * Deprecated since v1.22.0. Use `ngtcp2_conn_get_active_dcid2`
+ * instead.
*/
NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn,
ngtcp2_cid_token *dest);
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cid_token2` is the convenient struct to store
+ * Connection ID, its associated path, and stateless reset token.
+ *
+ * This type has been available since v1.22.0.
+ */
+typedef struct ngtcp2_cid_token2 {
+ /**
+ * :member:`seq` is the sequence number of this Connection ID.
+ */
+ uint64_t seq;
+ /**
+ * :member:`cid` is Connection ID.
+ */
+ ngtcp2_cid cid;
+ /**
+ * :member:`ps` is the path which this Connection ID is associated
+ * with.
+ */
+ ngtcp2_path_storage ps;
+ /**
+ * :member:`token` is the stateless reset token for this Connection
+ * ID.
+ */
+ ngtcp2_stateless_reset_token token;
+ /**
+ * :member:`token_present` is nonzero if token contains stateless
+ * reset token.
+ */
+ uint8_t token_present;
+} ngtcp2_cid_token2;
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_active_dcid2` writes the all active Destination
+ * Connection IDs and their tokens to |dest|. Before handshake
+ * completes, this function returns 0. If |dest| is NULL, this
+ * function does not write anything, and returns the number of
+ * Destination Connection IDs that would otherwise be written to the
+ * provided buffer. The buffer pointed by |dest| must have
+ * sizeof(:type:`ngtcp2_cid_token2`) * n bytes available, where n is
+ * the return value of `ngtcp2_conn_get_active_dcid2` with |dest| ==
+ * NULL.
+ *
+ * This function has been available since v1.22.0.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid2(ngtcp2_conn *conn,
+ ngtcp2_cid_token2 *dest);
+
/**
* @function
*
@@ -5430,7 +5842,7 @@ NGTCP2_EXTERN void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr,
* @function
*
* `ngtcp2_conn_write_connection_close` writes a packet which contains
- * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed
+ * 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
@@ -5447,16 +5859,18 @@ NGTCP2_EXTERN void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr,
*
* If :member:`ccerr->type <ngtcp2_ccerr.type>` ==
* :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_TRANSPORT`, this
- * function sends CONNECTION_CLOSE (type 0x1c) frame. If
+ * function sends CONNECTION_CLOSE (type 0x1C) frame. If
* :member:`ccerr->type <ngtcp2_ccerr.type>` ==
* :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_APPLICATION`, it sends
- * CONNECTION_CLOSE (type 0x1d) frame. Otherwise, it does not produce
+ * CONNECTION_CLOSE (type 0x1D) frame. Otherwise, it does not produce
* any data, and returns 0.
*
* |destlen| could be shorten by some factors (e.g., server side
* amplification limit). This function returns
* :macro:`NGTCP2_ERR_NOBUF` if the resulting buffer is too small even
- * if the given buffer has enough space.
+ * if the given buffer has enough space. This can happen if sending a
+ * packet would exceed a transmission limit (e.g., for amplification
+ * attack protection).
*
* This function must not be called from inside the callback
* functions.
@@ -5471,7 +5885,8 @@ NGTCP2_EXTERN void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr,
* :macro:`NGTCP2_ERR_NOMEM`
* Out of memory
* :macro:`NGTCP2_ERR_NOBUF`
- * Buffer is too small
+ * Buffer is too small or packet would exceed the transmission
+ * limit (e.g., for amplification attack protection).
* :macro:`NGTCP2_ERR_INVALID_STATE`
* The current state does not allow sending CONNECTION_CLOSE
* frame.
@@ -5535,6 +5950,26 @@ NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn,
int64_t stream_id,
void *stream_user_data);
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_stream_user_data` returns stream_user_data
+ * associated to the stream identified by |stream_id|. If the stream
+ * is not found, or no stream data is associated to the stream, this
+ * function returns NULL.
+ *
+ * The stream_user_data can be associated to the stream by one of the
+ * following functions:
+ *
+ * - `ngtcp2_conn_open_bidi_stream`
+ * - `ngtcp2_conn_open_uni_stream`
+ * - `ngtcp2_conn_set_stream_user_data`
+ *
+ * This function has been available since v1.17.0.
+ */
+NGTCP2_EXTERN void *ngtcp2_conn_get_stream_user_data(ngtcp2_conn *conn,
+ int64_t stream_id);
+
/**
* @function
*
@@ -5568,6 +6003,119 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn);
NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn,
int64_t stream_id);
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_write_pkt` is a callback function to write a single
+ * packet in the buffer pointed by |dest| of length |destlen|. The
+ * implementation should use `ngtcp2_conn_write_pkt`,
+ * `ngtcp2_conn_writev_stream`, `ngtcp2_conn_writev_datagram`, or
+ * their variants to write the packet. |path|, |pi|, |dest|,
+ * |destlen|, and |ts| should be directly passed to those functions.
+ * If the callback succeeds, it should return the number of bytes
+ * written to the buffer. In general, this callback function should
+ * return the value that the above mentioned functions returned except
+ * for the following error codes:
+ *
+ * - :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`
+ * - :macro:`NGTCP2_ERR_STREAM_SHUT_WR`
+ * - :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
+ *
+ * Those error codes should be handled by an application. If any
+ * error occurred outside those functions, return
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. If no packet is produced,
+ * return 0.
+ *
+ * Because GSO requires that the aggregated packets have the same
+ * length, :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` (or
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` if
+ * `ngtcp2_conn_writev_datagram` is used) is recommended.
+ *
+ * This callback function has been available since v1.15.0.
+ */
+typedef ngtcp2_ssize (*ngtcp2_write_pkt)(ngtcp2_conn *conn, ngtcp2_path *path,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_aggregate_pkt` is a helper function to write
+ * multiple packets in the provided buffer, which is suitable to be
+ * sent at once in GSO. This function returns the number of bytes
+ * written to the buffer pointed by |buf| of length |buflen|.
+ * |buflen| must be at least
+ * `ngtcp2_conn_get_path_max_tx_udp_payload_size(conn)
+ * <ngtcp2_conn_get_path_max_tx_udp_payload_size>` bytes long. It is
+ * recommended to pass the buffer at least
+ * `ngtcp2_conn_get_max_tx_udp_payload_size(conn)
+ * <ngtcp2_conn_get_max_tx_udp_payload_size>` bytes in order to send a
+ * PMTUD packet. This function only writes multiple packets if the
+ * first packet is `ngtcp2_conn_get_path_max_tx_udp_payload_size(conn)
+ * <ngtcp2_conn_get_path_max_tx_udp_payload_size>` bytes long. The
+ * application can adjust the length of the buffer to limit the number
+ * of packets to aggregate (or use `ngtcp2_conn_write_aggregate_pkt2`
+ * to control the number of packets to write directly). If this
+ * function returns positive integer, all packets share the same
+ * :type:`ngtcp2_path` and :type:`ngtcp2_pkt_info` values, and they
+ * are assigned to the objects pointed by |path| and |pi|
+ * respectively. The length of all packets other than the last packet
+ * is assigned to |*pgsolen|. The length of last packet is equal to
+ * or less than |*pgsolen|. |write_pkt| must write a single packet.
+ * After all packets are written, this function calls
+ * `ngtcp2_conn_update_pkt_tx_time`.
+ *
+ * This function is equivalent to call
+ * `ngtcp2_conn_write_aggregate_pkt2` with |buflen| = min(|buflen|,
+ * `ngtcp2_conn_get_send_quantum(conn)
+ * <ngtcp2_conn_get_send_quantum>`) and |num_pkts| = 0 followed by
+ * `ngtcp2_conn_update_pkt_tx_time(conn)
+ * <ngtcp2_conn_update_pkt_tx_time>`.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * a negative error code returned by |write_pkt|.
+ *
+ * This function has been available since v1.15.0.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
+ ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_aggregate_pkt2` behaves like
+ * `ngtcp2_conn_write_aggregate_pkt`, but it accepts |num_pkts| to
+ * specify the maximum number of packets to write. If |num_pkts| is
+ * 0, this function writes packets as much as possible. The actual
+ * number of packets to write is determined by the connection state
+ * (e.g., the congestion controller, data available to send) and the
+ * length of packet produced. It also does not clamp |buflen|, and
+ * does not call `ngtcp2_conn_update_pkt_tx_time`.
+ *
+ * This function offers more flexibility and optimization chances to
+ * an application. It can experiment different GSO buffer size
+ * strategy and number of GSO writes per event loop.
+ *
+ * This function has been available since v1.17.0.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt2_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
+ ngtcp2_write_pkt write_pkt, size_t num_pkts, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_timestamp` returns the latest timestamp that is
+ * known to |conn|.
+ *
+ * This function has been available since v1.16.0.
+ */
+NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_timestamp(const ngtcp2_conn *conn);
+
/**
* @function
*
@@ -5650,15 +6198,19 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps);
* values. First this function fills |settings| with 0, and set the
* default value to the following fields:
*
- * * :type:`cc_algo <ngtcp2_settings.cc_algo>` =
+ * * :member:`cc_algo <ngtcp2_settings.cc_algo>` =
* :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC`
- * * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` =
+ * * :member:`initial_rtt <ngtcp2_settings.initial_rtt>` =
* :macro:`NGTCP2_DEFAULT_INITIAL_RTT`
- * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
- * * :type:`max_tx_udp_payload_size
+ * * :member:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
+ * * :member:`max_tx_udp_payload_size
* <ngtcp2_settings.max_tx_udp_payload_size>` = 1452
- * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
+ * * :member:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
* ``UINT64_MAX``
+ * * :member:`glitch_ratelim_burst
+ * <ngtcp2_settings.glitch_ratelim_burst>` = 10000
+ * * :member:`glitch_ratelim_rate
+ * <ngtcp2_settings.glitch_ratelim_rate>` = 330
*/
NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version,
ngtcp2_settings *settings);
@@ -5670,15 +6222,15 @@ NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version,
* default values. First this function fills |params| with 0, and set
* the default value to the following fields:
*
- * * :type:`max_udp_payload_size
+ * * :member:`max_udp_payload_size
* <ngtcp2_transport_params.max_udp_payload_size>` =
* :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE`
- * * :type:`ack_delay_exponent
+ * * :member:`ack_delay_exponent
* <ngtcp2_transport_params.ack_delay_exponent>` =
* :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT`
- * * :type:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` =
+ * * :member:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` =
* :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY`
- * * :type:`active_connection_id_limit
+ * * :member:`active_connection_id_limit
* <ngtcp2_transport_params.active_connection_id_limit>` =
* :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT`
*/
@@ -5950,6 +6502,28 @@ NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
#define ngtcp2_conn_get_conn_info(CONN, CINFO) \
ngtcp2_conn_get_conn_info_versioned((CONN), NGTCP2_CONN_INFO_VERSION, (CINFO))
+/*
+ * `ngtcp2_conn_write_aggregate_pkt` is a wrapper around
+ * `ngtcp2_conn_write_aggregate_pkt_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_conn_write_aggregate_pkt(CONN, PATH, PI, BUF, BUFLEN, PGSOLEN, \
+ WRITE_PKT, TS) \
+ ngtcp2_conn_write_aggregate_pkt_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \
+ (WRITE_PKT), (TS))
+
+/*
+ * `ngtcp2_conn_write_aggregate_pkt2` is a wrapper around
+ * `ngtcp2_conn_write_aggregate_pkt2_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_conn_write_aggregate_pkt2(CONN, PATH, PI, BUF, BUFLEN, PGSOLEN, \
+ WRITE_PKT, NUM_PKTS, TS) \
+ ngtcp2_conn_write_aggregate_pkt2_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \
+ (WRITE_PKT), (NUM_PKTS), (TS))
+
/*
* `ngtcp2_settings_default` is a wrapper around
* `ngtcp2_settings_default_versioned` to set the correct struct
diff --git a/third_party/ngtcp2/lib/ngtcp2_acktr.c b/third_party/ngtcp2/lib/ngtcp2_acktr.c
index 776dc0c2c3e..4a9ac5cf0aa 100644
--- a/third_party/ngtcp2/lib/ngtcp2_acktr.c
+++ b/third_party/ngtcp2/lib/ngtcp2_acktr.c
@@ -34,9 +34,11 @@ ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent)
static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num,
ngtcp2_tstamp tstamp) {
- ent->pkt_num = pkt_num;
- ent->len = 1;
- ent->tstamp = tstamp;
+ *ent = (ngtcp2_acktr_entry){
+ .pkt_num = pkt_num,
+ .len = 1,
+ .tstamp = tstamp,
+ };
}
int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
@@ -219,8 +221,10 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
int64_t largest_ack) {
ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks.rb);
- ent->largest_ack = largest_ack;
- ent->pkt_num = pkt_num;
+ *ent = (ngtcp2_acktr_ack_entry){
+ .largest_ack = largest_ack,
+ .pkt_num = pkt_num,
+ };
return ent;
}
@@ -316,9 +320,9 @@ void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
}
void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
- acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK |
- NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK |
- NGTCP2_ACKTR_FLAG_CANCEL_TIMER);
+ acktr->flags &=
+ (uint16_t)~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK |
+ NGTCP2_ACKTR_FLAG_CANCEL_TIMER);
acktr->first_unacked_ts = UINT64_MAX;
acktr->rx_npkt = 0;
}
@@ -333,16 +337,14 @@ void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK;
}
-ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
- ngtcp2_frame *fr, uint8_t type,
- ngtcp2_tstamp ts,
- ngtcp2_duration ack_delay,
- uint64_t ack_delay_exponent) {
+int ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, ngtcp2_ack *ack,
+ uint8_t type, ngtcp2_tstamp ts,
+ ngtcp2_duration ack_delay,
+ uint64_t ack_delay_exponent) {
int64_t last_pkt_num;
ngtcp2_ack_range *range;
ngtcp2_ksl_it it;
ngtcp2_acktr_entry *rpkt;
- ngtcp2_ack *ack = &fr->ack;
ngtcp2_tstamp largest_ack_ts;
size_t num_acks;
@@ -351,13 +353,13 @@ ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
}
if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) {
- return NULL;
+ return -1;
}
it = ngtcp2_acktr_get(acktr);
if (ngtcp2_ksl_it_end(&it)) {
ngtcp2_acktr_commit_ack(acktr);
- return NULL;
+ return -1;
}
num_acks = ngtcp2_ksl_len(&acktr->ents);
@@ -420,7 +422,7 @@ ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1);
}
- return fr;
+ return 0;
}
void ngtcp2_acktr_increase_ecn_counts(ngtcp2_acktr *acktr,
diff --git a/third_party/ngtcp2/lib/ngtcp2_acktr.h b/third_party/ngtcp2/lib/ngtcp2_acktr.h
index cf75a774db3..026c2cabe87 100644
--- a/third_party/ngtcp2/lib/ngtcp2_acktr.h
+++ b/third_party/ngtcp2/lib/ngtcp2_acktr.h
@@ -97,16 +97,16 @@ typedef struct ngtcp2_acktr_ack_entry {
} ngtcp2_acktr_ack_entry;
/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */
-#define NGTCP2_ACKTR_FLAG_NONE 0x00u
+#define NGTCP2_ACKTR_FLAG_NONE 0x00U
/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
acknowledgement is required. */
-#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u
+#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01U
/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending
protected packet to be acknowledged. */
-#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u
+#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02U
/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
expired and canceled. */
-#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u
+#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100U
ngtcp2_static_ringbuf_def(acks, 32, sizeof(ngtcp2_acktr_ack_entry))
@@ -120,8 +120,6 @@ typedef struct ngtcp2_acktr {
packet number. */
ngtcp2_ksl ents;
ngtcp2_log *log;
- /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */
- uint16_t flags;
/* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added
first time after the last outgoing ACK frame. */
ngtcp2_tstamp first_unacked_ts;
@@ -148,6 +146,9 @@ typedef struct ngtcp2_acktr {
uint64_t ce;
} ack;
} ecn;
+
+ /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */
+ uint16_t flags;
} ngtcp2_acktr;
/*
@@ -235,19 +236,18 @@ void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr);
/*
* ngtcp2_acktr_create_ack_frame creates ACK frame in the object
- * pointed by |fr|, and returns |fr| if there are any received packets
- * to acknowledge. If there are no packets to acknowledge, this
- * function returns NULL. fr->ack.ranges must be able to contain at
+ * pointed by |ack|, and returns 0 if it successfully creates ACK
+ * frame in |ack|. If there are no packets to acknowledge, this
+ * function returns -1. |ack|->ranges must be able to contain at
* least NGTCP2_MAX_ACK_RANGES elements.
*
* Call ngtcp2_acktr_commit_ack after a created ACK frame is
* successfully serialized into a packet.
*/
-ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr,
- ngtcp2_frame *fr, uint8_t type,
- ngtcp2_tstamp ts,
- ngtcp2_duration ack_delay,
- uint64_t ack_delay_exponent);
+int ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, ngtcp2_ack *ack,
+ uint8_t type, ngtcp2_tstamp ts,
+ ngtcp2_duration ack_delay,
+ uint64_t ack_delay_exponent);
/*
* ngtcp2_acktr_increase_ecn_counts increases ECN counts from |pi|.
diff --git a/third_party/ngtcp2/lib/ngtcp2_addr.c b/third_party/ngtcp2/lib/ngtcp2_addr.c
index 1fb273d494e..58694e3836e 100644
--- a/third_party/ngtcp2/lib/ngtcp2_addr.c
+++ b/third_party/ngtcp2/lib/ngtcp2_addr.c
@@ -31,8 +31,11 @@
ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
ngtcp2_socklen addrlen) {
- dest->addrlen = addrlen;
- dest->addr = (ngtcp2_sockaddr *)addr;
+ *dest = (ngtcp2_addr){
+ .addr = (ngtcp2_sockaddr *)addr,
+ .addrlen = addrlen,
+ };
+
return dest;
}
@@ -58,14 +61,12 @@ int ngtcp2_sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
switch (a->sa_family) {
case NGTCP2_AF_INET: {
- const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
- *bi = (const ngtcp2_sockaddr_in *)(void *)b;
+ const ngtcp2_sockaddr_in *ai = (void *)a, *bi = (void *)b;
return ai->sin_port == bi->sin_port &&
memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
}
case NGTCP2_AF_INET6: {
- const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
- *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
+ const ngtcp2_sockaddr_in6 *ai = (void *)a, *bi = (void *)b;
return ai->sin6_port == bi->sin6_port &&
memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
}
@@ -89,8 +90,7 @@ uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
switch (a->sa_family) {
case NGTCP2_AF_INET: {
- const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
- *bi = (const ngtcp2_sockaddr_in *)(void *)b;
+ const ngtcp2_sockaddr_in *ai = (void *)a, *bi = (void *)b;
if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
flags |= NGTCP2_ADDR_CMP_FLAG_ADDR;
}
@@ -100,8 +100,7 @@ uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
return flags;
}
case NGTCP2_AF_INET6: {
- const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
- *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
+ const ngtcp2_sockaddr_in6 *ai = (void *)a, *bi = (void *)b;
if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
flags |= NGTCP2_ADDR_CMP_FLAG_ADDR;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_addr.h b/third_party/ngtcp2/lib/ngtcp2_addr.h
index c2224f85cd9..6314b1afc81 100644
--- a/third_party/ngtcp2/lib/ngtcp2_addr.h
+++ b/third_party/ngtcp2/lib/ngtcp2_addr.h
@@ -46,14 +46,14 @@ void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src);
int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b);
/* NGTCP2_ADDR_CMP_FLAG_NONE indicates that no flag set. */
-#define NGTCP2_ADDR_CMP_FLAG_NONE 0x0u
+#define NGTCP2_ADDR_CMP_FLAG_NONE 0x0U
/* NGTCP2_ADDR_CMP_FLAG_ADDR indicates IP addresses do not match. */
-#define NGTCP2_ADDR_CMP_FLAG_ADDR 0x1u
+#define NGTCP2_ADDR_CMP_FLAG_ADDR 0x1U
/* NGTCP2_ADDR_CMP_FLAG_PORT indicates ports do not match. */
-#define NGTCP2_ADDR_CMP_FLAG_PORT 0x2u
+#define NGTCP2_ADDR_CMP_FLAG_PORT 0x2U
/* NGTCP2_ADDR_CMP_FLAG_FAMILY indicates address families do not
match. */
-#define NGTCP2_ADDR_CMP_FLAG_FAMILY 0x4u
+#define NGTCP2_ADDR_CMP_FLAG_FAMILY 0x4U
/*
* ngtcp2_addr_cmp compares address and port between |a| and |b|, and
diff --git a/third_party/ngtcp2/lib/ngtcp2_balloc.c b/third_party/ngtcp2/lib/ngtcp2_balloc.c
index 4a6797689fc..fff7a9ef65b 100644
--- a/third_party/ngtcp2/lib/ngtcp2_balloc.c
+++ b/third_party/ngtcp2/lib/ngtcp2_balloc.c
@@ -30,7 +30,7 @@
void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
const ngtcp2_mem *mem) {
- assert((blklen & 0xfu) == 0);
+ assert((blklen & 0xFU) == 0);
balloc->mem = mem;
balloc->blklen = blklen;
@@ -66,25 +66,25 @@ int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
if (ngtcp2_buf_left(&balloc->buf) < n) {
p = ngtcp2_mem_malloc(balloc->mem,
- sizeof(ngtcp2_memblock_hd) + 0x8u + balloc->blklen);
+ sizeof(ngtcp2_memblock_hd) + 0x8U + balloc->blklen);
if (p == NULL) {
return NGTCP2_ERR_NOMEM;
}
- hd = (ngtcp2_memblock_hd *)(void *)p;
+ hd = (void *)p;
hd->next = balloc->head;
balloc->head = hd;
ngtcp2_buf_init(
&balloc->buf,
- (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) &
- ~(uintptr_t)0xfu),
+ (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xFU) &
+ ~(uintptr_t)0xFU),
balloc->blklen);
}
- assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
+ assert(((uintptr_t)balloc->buf.last & 0xFU) == 0);
*pbuf = balloc->buf.last;
- balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
+ balloc->buf.last += (n + 0xFU) & ~(uintptr_t)0xFU;
return 0;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_bbr.c b/third_party/ngtcp2/lib/ngtcp2_bbr.c
index 04612f11be4..3db6a9f2bff 100644
--- a/third_party/ngtcp2/lib/ngtcp2_bbr.c
+++ b/third_party/ngtcp2/lib/ngtcp2_bbr.c
@@ -33,6 +33,7 @@
#include "ngtcp2_rcvry.h"
#include "ngtcp2_rst.h"
#include "ngtcp2_conn_stat.h"
+#include "ngtcp2_pcg.h"
#define NGTCP2_BBR_MAX_BW_FILTERLEN 2
@@ -69,19 +70,21 @@ static void bbr_on_transmit(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr);
-static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr);
+static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr);
static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr);
static void bbr_reset_full_bw(ngtcp2_cc_bbr *bbr);
-static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_init_pacing_rate(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat);
-static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr,
+static void bbr_set_pacing_rate_with_gain(const ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat,
uint64_t pacing_gain_h);
-static void bbr_set_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_set_pacing_rate(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat);
static void bbr_enter_startup(ngtcp2_cc_bbr *bbr);
@@ -99,47 +102,47 @@ static void bbr_update_control_parameters(ngtcp2_cc_bbr *cc,
ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack);
-static void bbr_update_on_loss(ngtcp2_cc_bbr *cc, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
-
static void bbr_update_latest_delivery_signals(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+ const ngtcp2_conn_stat *cstat);
static void bbr_advance_latest_delivery_signals(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+ const ngtcp2_conn_stat *cstat);
static void bbr_update_congestion_signals(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack);
-static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+static void
+bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
-static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
static void bbr_loss_lower_bounds(ngtcp2_cc_bbr *bbr);
static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr);
-static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack);
static void bbr_update_round(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack);
static void bbr_start_round(ngtcp2_cc_bbr *bbr);
-static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr);
+static int bbr_is_in_probe_bw_state(const ngtcp2_cc_bbr *bbr);
-static int bbr_is_probing_bw(ngtcp2_cc_bbr *bbr);
+static int bbr_is_probing_bw(const ngtcp2_cc_bbr *bbr);
static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
static void bbr_enter_drain(ngtcp2_cc_bbr *bbr);
-static void bbr_check_drain_done(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static void bbr_check_drain_done(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
static void bbr_enter_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts);
@@ -150,59 +153,70 @@ static void bbr_start_probe_bw_cruise(ngtcp2_cc_bbr *bbr);
static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr);
-static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
-static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
+static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
-static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
-static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr,
+static int bbr_has_elapsed_in_phase(const ngtcp2_cc_bbr *bbr,
ngtcp2_duration interval, ngtcp2_tstamp ts);
-static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+static uint64_t bbr_inflight_with_headroom(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+ const ngtcp2_conn_stat *cstat);
static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack);
-static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack);
+static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
-static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr);
-static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+static int bbr_is_reno_coexistence_probe_time(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
-static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+static uint64_t bbr_target_inflight(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
-static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr);
+static int bbr_is_inflight_too_high(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_rs *rs);
static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
+ const ngtcp2_conn_stat *cstat,
+ const ngtcp2_rs *rs, ngtcp2_tstamp ts);
static void bbr_note_loss(ngtcp2_cc_bbr *bbr);
-static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static void bbr_save_state_upon_loss(ngtcp2_cc_bbr *bbr);
+
+static void bbr_handle_spurious_loss_detection(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
+
+static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
-static uint64_t
-bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
- const ngtcp2_cc_pkt *pkt);
+static uint64_t bbr_inflight_at_loss(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_cc_pkt *pkt,
+ const ngtcp2_rs *rs);
static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
@@ -219,7 +233,7 @@ static void bbr_check_probe_rtt_done(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+ const ngtcp2_conn_stat *cstat);
static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts);
@@ -230,27 +244,28 @@ static void bbr_handle_restart_from_idle(ngtcp2_cc_bbr *bbr,
static uint64_t bbr_bdp_multiple(ngtcp2_cc_bbr *bbr, uint64_t gain_h);
static uint64_t bbr_quantization_budget(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
uint64_t inflight);
-static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat,
uint64_t gain_h);
static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+ const ngtcp2_conn_stat *cstat);
static void bbr_update_offload_budget(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+ const ngtcp2_conn_stat *cstat);
static uint64_t min_pipe_cwnd(size_t max_udp_payload_size);
static void bbr_advance_max_bw_filter(ngtcp2_cc_bbr *bbr);
-static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat);
-static void bbr_restore_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_restore_cwnd(const ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
-static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat);
static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat);
@@ -258,10 +273,11 @@ static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr,
static void bbr_set_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack);
-static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr,
+static void bbr_bound_cwnd_for_model(const ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat);
-static void bbr_set_send_quantum(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
+static void bbr_set_send_quantum(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat);
static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_time);
@@ -288,7 +304,7 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
bbr->full_bw_reached = 0;
bbr_reset_congestion_signals(bbr);
- bbr_reset_lower_bounds(bbr);
+ bbr_reset_shortterm_model(bbr);
bbr_init_round_counting(bbr);
bbr_reset_full_bw(bbr);
bbr_init_pacing_rate(bbr, cstat);
@@ -333,9 +349,12 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
bbr->max_inflight = 0;
- bbr->congestion_recovery_start_ts = UINT64_MAX;
-
bbr->bdp = 0;
+
+ bbr->undo_state = 0;
+ bbr->undo_bw_shortterm = 0;
+ bbr->undo_inflight_shortterm = 0;
+ bbr->undo_inflight_longterm = 0;
}
static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) {
@@ -344,7 +363,7 @@ static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) {
bbr->inflight_latest = 0;
}
-static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr) {
+static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr) {
bbr->bw_shortterm = UINT64_MAX;
bbr->inflight_shortterm = UINT64_MAX;
}
@@ -362,7 +381,7 @@ static void bbr_reset_full_bw(ngtcp2_cc_bbr *bbr) {
}
static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
if (bbr->full_bw_now || !bbr->round_start || bbr->rst->rs.is_app_limited) {
return;
}
@@ -383,15 +402,16 @@ static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr,
bbr->full_bw_reached = 1;
- ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
- "bbr reached full bandwidth, full_bw=%" PRIu64, bbr->full_bw);
+ ngtcp2_log_infof(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "bbr reached full bandwidth, full_bw=%" PRIu64,
+ bbr->full_bw);
}
static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) {
if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 ||
(bbr->in_loss_recovery &&
bbr->round_count <= bbr->round_count_at_recovery) ||
- !bbr_is_inflight_too_high(bbr)) {
+ !bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
return;
}
@@ -400,15 +420,17 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) {
bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), bbr->inflight_latest);
}
-static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
- cstat->pacing_interval_m =
+static void bbr_init_pacing_rate(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat) {
+ cstat->pacing_interval_m = ngtcp2_max_uint64(
((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS
: cstat->smoothed_rtt)
<< 10) *
- 100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd;
+ 100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd,
+ 1);
}
-static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr,
+static void bbr_set_pacing_rate_with_gain(const ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat,
uint64_t pacing_gain_h) {
uint64_t interval_m;
@@ -426,7 +448,8 @@ static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr,
}
}
-static void bbr_set_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static void bbr_set_pacing_rate(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat) {
bbr_set_pacing_rate_with_gain(bbr, cstat, bbr->pacing_gain_h);
}
@@ -482,13 +505,8 @@ static void bbr_update_control_parameters(ngtcp2_cc_bbr *bbr,
bbr_set_cwnd(bbr, cstat, ack);
}
-static void bbr_update_on_loss(ngtcp2_cc_bbr *cc, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
- bbr_handle_lost_packet(cc, cstat, pkt, ts);
-}
-
static void bbr_update_latest_delivery_signals(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
bbr->loss_round_start = 0;
bbr->bw_latest = ngtcp2_max_uint64(bbr->bw_latest, cstat->delivery_rate_sec);
bbr->inflight_latest =
@@ -501,7 +519,7 @@ static void bbr_update_latest_delivery_signals(ngtcp2_cc_bbr *bbr,
}
static void bbr_advance_latest_delivery_signals(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
if (bbr->loss_round_start) {
bbr->bw_latest = cstat->delivery_rate_sec;
bbr->inflight_latest = bbr->rst->rs.delivered;
@@ -509,7 +527,7 @@ static void bbr_advance_latest_delivery_signals(ngtcp2_cc_bbr *bbr,
}
static void bbr_update_congestion_signals(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack) {
bbr_update_max_bw(bbr, cstat, ack);
@@ -527,8 +545,9 @@ static void bbr_update_congestion_signals(ngtcp2_cc_bbr *bbr,
bbr->loss_in_round = 0;
}
-static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+static void
+bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
if (bbr_is_probing_bw(bbr)) {
return;
}
@@ -539,7 +558,8 @@ static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr,
}
}
-static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
if (bbr->bw_shortterm == UINT64_MAX) {
bbr->bw_shortterm = bbr->max_bw;
}
@@ -562,11 +582,12 @@ static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr) {
bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_shortterm);
}
-static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack) {
bbr_update_round(bbr, ack);
- if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) {
+ if (cstat->delivery_rate_sec && (cstat->delivery_rate_sec >= bbr->max_bw ||
+ !bbr->rst->rs.is_app_limited)) {
ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec,
bbr->cycle_count);
@@ -597,7 +618,7 @@ static void bbr_start_round(ngtcp2_cc_bbr *bbr) {
bbr->next_round_delivered = bbr->rst->delivered;
}
-static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr) {
+static int bbr_is_in_probe_bw_state(const ngtcp2_cc_bbr *bbr) {
switch (bbr->state) {
case NGTCP2_BBR_STATE_PROBE_BW_DOWN:
case NGTCP2_BBR_STATE_PROBE_BW_CRUISE:
@@ -609,7 +630,7 @@ static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr) {
}
}
-static int bbr_is_probing_bw(ngtcp2_cc_bbr *bbr) {
+static int bbr_is_probing_bw(const ngtcp2_cc_bbr *bbr) {
switch (bbr->state) {
case NGTCP2_BBR_STATE_STARTUP:
case NGTCP2_BBR_STATE_PROBE_BW_REFILL:
@@ -621,7 +642,7 @@ static int bbr_is_probing_bw(ngtcp2_cc_bbr *bbr) {
}
static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_duration interval = ts - bbr->extra_acked_interval_start;
@@ -663,7 +684,8 @@ static void bbr_enter_drain(ngtcp2_cc_bbr *bbr) {
bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H;
}
-static void bbr_check_drain_done(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static void bbr_check_drain_done(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
if (bbr->state == NGTCP2_BBR_STATE_DRAIN &&
cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) {
@@ -707,7 +729,7 @@ static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr) {
ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
"bbr start ProbeBW_REFILL");
- bbr_reset_lower_bounds(bbr);
+ bbr_reset_shortterm_model(bbr);
bbr->bw_probe_up_rounds = 0;
bbr->bw_probe_up_acks = 0;
@@ -720,7 +742,8 @@ static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr) {
bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H;
}
-static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr start ProbeBW_UP");
bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING;
@@ -737,14 +760,14 @@ static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
}
static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
if (!bbr->full_bw_reached) {
return;
}
- bbr_adapt_upper_bounds(bbr, cstat, ack);
+ bbr_adapt_longterm_model(bbr, cstat, ack);
if (!bbr_is_in_probe_bw_state(bbr)) {
return;
@@ -756,7 +779,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr,
return;
}
- if (bbr_is_time_to_cruise(bbr, cstat, ts)) {
+ if (bbr_is_time_to_cruise(bbr, cstat)) {
bbr_start_probe_bw_cruise(bbr);
}
@@ -785,40 +808,34 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr,
}
}
-static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts) {
- (void)ts;
-
- if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) {
- return 0;
- }
-
- if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) {
- return 1;
- }
+static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
+ uint64_t inflight = ngtcp2_min_uint64(bbr_inflight_with_headroom(bbr, cstat),
+ bbr_inflight(bbr, cstat, 100));
- return 0;
+ return cstat->bytes_in_flight <= inflight;
}
-static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_longterm) {
bbr_reset_full_bw(bbr);
bbr->full_bw = cstat->delivery_rate_sec;
- } else if (bbr->full_bw_now) {
- return 1;
+
+ return 0;
}
- return 0;
+ return bbr->full_bw_now;
}
-static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr,
+static int bbr_has_elapsed_in_phase(const ngtcp2_cc_bbr *bbr,
ngtcp2_duration interval,
ngtcp2_tstamp ts) {
return ts > bbr->cycle_stamp + interval;
}
-static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+static uint64_t bbr_inflight_with_headroom(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
uint64_t headroom;
uint64_t mpcwnd;
if (bbr->inflight_longterm == UINT64_MAX) {
@@ -839,7 +856,7 @@ static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr,
}
static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
uint64_t growth_this_round = cstat->max_tx_udp_payload_size
<< bbr->bw_probe_up_rounds;
@@ -848,7 +865,7 @@ static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr,
}
static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack) {
uint64_t delta;
@@ -871,8 +888,9 @@ static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr,
}
}
-static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack) {
+static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
if (bbr->ack_phase == NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING &&
bbr->round_start) {
bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_FEEDBACK;
@@ -885,7 +903,7 @@ static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
}
}
- if (!bbr_is_inflight_too_high(bbr)) {
+ if (!bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
if (bbr->inflight_longterm == UINT64_MAX) {
return;
}
@@ -900,7 +918,8 @@ static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
}
}
-static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
if (bbr_has_elapsed_in_phase(bbr, bbr->bw_probe_wait, ts) ||
bbr_is_reno_coexistence_probe_time(bbr, cstat)) {
@@ -913,41 +932,35 @@ static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
}
static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr) {
- uint8_t rand;
-
- bbr->rand(&rand, 1, &bbr->rand_ctx);
-
- bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256);
-
- bbr->rand(&rand, 1, &bbr->rand_ctx);
-
- bbr->bw_probe_wait = 2 * NGTCP2_SECONDS + NGTCP2_SECONDS * rand / 255;
+ bbr->rounds_since_bw_probe = ngtcp2_pcg32_rand_n(bbr->pcg, 2);
+ bbr->bw_probe_wait =
+ 2 * NGTCP2_SECONDS + ngtcp2_pcg32_rand_n(bbr->pcg, NGTCP2_SECONDS + 1);
}
-static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+static int bbr_is_reno_coexistence_probe_time(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
uint64_t reno_rounds =
bbr_target_inflight(bbr, cstat) / cstat->max_tx_udp_payload_size;
return bbr->rounds_since_bw_probe >= ngtcp2_min_uint64(reno_rounds, 63);
}
-static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+static uint64_t bbr_target_inflight(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd);
}
-static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr) {
- const ngtcp2_rs *rs = &bbr->rst->rs;
+static int bbr_is_inflight_too_high(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_rs *rs) {
+ (void)bbr;
return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM >
rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER;
}
static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
+ const ngtcp2_rs *rs,
ngtcp2_tstamp ts) {
- const ngtcp2_rs *rs = &bbr->rst->rs;
-
bbr->bw_probe_samples = 0;
if (!rs->is_app_limited) {
@@ -964,14 +977,51 @@ static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
static void bbr_note_loss(ngtcp2_cc_bbr *bbr) {
if (!bbr->loss_in_round) {
bbr->loss_round_delivered = bbr->rst->delivered;
+ bbr_save_state_upon_loss(bbr);
}
bbr->loss_in_round = 1;
}
-static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static void bbr_save_state_upon_loss(ngtcp2_cc_bbr *bbr) {
+ bbr->undo_state = bbr->state;
+ bbr->undo_bw_shortterm = bbr->bw_shortterm;
+ bbr->undo_inflight_shortterm = bbr->inflight_shortterm;
+ bbr->undo_inflight_longterm = bbr->inflight_longterm;
+}
+
+static void bbr_handle_spurious_loss_detection(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat) {
+ bbr->loss_in_round = 0;
+
+ bbr_reset_full_bw(bbr);
+
+ bbr->bw_shortterm =
+ ngtcp2_max_uint64(bbr->bw_shortterm, bbr->undo_bw_shortterm);
+ bbr->inflight_shortterm =
+ ngtcp2_max_uint64(bbr->inflight_shortterm, bbr->undo_inflight_shortterm);
+ bbr->inflight_longterm =
+ ngtcp2_max_uint64(bbr->inflight_longterm, bbr->undo_inflight_longterm);
+
+ if (bbr->state != NGTCP2_BBR_STATE_PROBE_RTT &&
+ bbr->state != bbr->undo_state) {
+ switch (bbr->undo_state) {
+ case NGTCP2_BBR_STATE_STARTUP:
+ bbr_enter_startup(bbr);
+ break;
+ case NGTCP2_BBR_STATE_PROBE_BW_UP:
+ bbr_start_probe_bw_up(bbr, cstat);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
- ngtcp2_rs *rs = &bbr->rst->rs;
+ ngtcp2_rs rs = {0};
bbr_note_loss(bbr);
@@ -979,23 +1029,21 @@ static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
return;
}
- rs->tx_in_flight = pkt->tx_in_flight;
- /* bbr->rst->lost is not incremented for pkt yet */
- assert(bbr->rst->lost + pkt->pktlen >= pkt->lost);
- rs->lost = bbr->rst->lost + pkt->pktlen - pkt->lost;
- rs->is_app_limited = pkt->is_app_limited;
+ rs.tx_in_flight = pkt->tx_in_flight;
+ assert(bbr->rst->lost >= pkt->lost);
+ rs.lost = bbr->rst->lost - pkt->lost;
+ rs.is_app_limited = pkt->is_app_limited;
- if (bbr_is_inflight_too_high(bbr)) {
- rs->tx_in_flight = bbr_inflight_longterm_from_lost_packet(bbr, pkt);
+ if (bbr_is_inflight_too_high(bbr, &rs)) {
+ rs.tx_in_flight = bbr_inflight_at_loss(bbr, pkt, &rs);
- bbr_handle_inflight_too_high(bbr, cstat, ts);
+ bbr_handle_inflight_too_high(bbr, cstat, &rs, ts);
}
}
-static uint64_t
-bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
- const ngtcp2_cc_pkt *pkt) {
- ngtcp2_rs *rs = &bbr->rst->rs;
+static uint64_t bbr_inflight_at_loss(const ngtcp2_cc_bbr *bbr,
+ const ngtcp2_cc_pkt *pkt,
+ const ngtcp2_rs *rs) {
uint64_t inflight_prev, lost_prev, lost_prefix;
(void)bbr;
@@ -1038,8 +1086,8 @@ static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack,
bbr->min_rtt = bbr->probe_rtt_min_delay;
bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp;
- ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
- "bbr update min_rtt=%" PRIu64, bbr->min_rtt);
+ ngtcp2_log_infof(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "bbr update min_rtt=%" PRIu64, bbr->min_rtt);
}
}
@@ -1110,18 +1158,13 @@ static void bbr_check_probe_rtt_done(ngtcp2_cc_bbr *bbr,
}
static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
- uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight;
-
- if (app_limited) {
- bbr->rst->app_limited = app_limited;
- } else {
- bbr->rst->app_limited = cstat->max_tx_udp_payload_size;
- }
+ const ngtcp2_conn_stat *cstat) {
+ bbr->rst->app_limited =
+ ngtcp2_max_uint64(bbr->rst->delivered + cstat->bytes_in_flight, 1);
}
static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) {
- bbr_reset_lower_bounds(bbr);
+ bbr_reset_shortterm_model(bbr);
if (bbr->full_bw_reached) {
bbr_start_probe_bw_down(bbr, ts);
@@ -1163,7 +1206,7 @@ static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
}
static uint64_t bbr_quantization_budget(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
+ const ngtcp2_conn_stat *cstat,
uint64_t inflight) {
bbr_update_offload_budget(bbr, cstat);
@@ -1178,7 +1221,7 @@ static uint64_t bbr_quantization_budget(ngtcp2_cc_bbr *bbr,
return inflight;
}
-static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
+static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat,
uint64_t gain_h) {
uint64_t inflight = bbr_bdp_multiple(bbr, gain_h);
@@ -1186,7 +1229,7 @@ static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
}
static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
uint64_t inflight;
inflight = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h) + bbr->extra_acked;
@@ -1194,7 +1237,7 @@ static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr,
}
static void bbr_update_offload_budget(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
bbr->offload_budget = 3 * cstat->send_quantum;
}
@@ -1202,7 +1245,7 @@ static void bbr_advance_max_bw_filter(ngtcp2_cc_bbr *bbr) {
++bbr->cycle_count;
}
-static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat) {
if (!bbr->in_loss_recovery && bbr->state != NGTCP2_BBR_STATE_PROBE_RTT) {
bbr->prior_cwnd = cstat->cwnd;
return;
@@ -1211,12 +1254,13 @@ static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
bbr->prior_cwnd = ngtcp2_max_uint64(bbr->prior_cwnd, cstat->cwnd);
}
-static void bbr_restore_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static void bbr_restore_cwnd(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat) {
cstat->cwnd = ngtcp2_max_uint64(cstat->cwnd, bbr->prior_cwnd);
}
static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+ const ngtcp2_conn_stat *cstat) {
uint64_t probe_rtt_cwnd =
bbr_bdp_multiple(bbr, NGTCP2_BBR_PROBE_RTT_CWND_GAIN_H);
uint64_t mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
@@ -1226,12 +1270,9 @@ static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr,
static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat) {
- uint64_t probe_rtt_cwnd;
-
if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) {
- probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat);
-
- cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, probe_rtt_cwnd);
+ cstat->cwnd =
+ ngtcp2_min_uint64(cstat->cwnd, bbr_probe_rtt_cwnd(bbr, cstat));
}
}
@@ -1256,7 +1297,7 @@ static void bbr_set_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
bbr_bound_cwnd_for_model(bbr, cstat);
}
-static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr,
+static void bbr_bound_cwnd_for_model(const ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat) {
uint64_t cap = UINT64_MAX;
uint64_t mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
@@ -1275,7 +1316,8 @@ static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr,
cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, cap);
}
-static void bbr_set_send_quantum(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+static void bbr_set_send_quantum(const ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat) {
size_t send_quantum = 64 * 1024;
(void)bbr;
@@ -1295,28 +1337,15 @@ static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
static void bbr_handle_recovery(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack) {
- if (bbr->in_loss_recovery) {
- if (ack->largest_pkt_sent_ts != UINT64_MAX &&
- !in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
- bbr->in_loss_recovery = 0;
- bbr->round_count_at_recovery = UINT64_MAX;
- bbr_restore_cwnd(bbr, cstat);
- }
-
+ if (!bbr->in_loss_recovery) {
return;
}
- if (bbr->congestion_recovery_start_ts != UINT64_MAX) {
- bbr->in_loss_recovery = 1;
- bbr->round_count_at_recovery =
- bbr->round_start ? bbr->round_count : bbr->round_count + 1;
- bbr_save_cwnd(bbr, cstat);
- cstat->cwnd =
- cstat->bytes_in_flight +
- ngtcp2_max_uint64(ack->bytes_delivered, cstat->max_tx_udp_payload_size);
-
- cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts;
- bbr->congestion_recovery_start_ts = UINT64_MAX;
+ if (ack->largest_pkt_sent_ts != UINT64_MAX &&
+ !in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
+ bbr->in_loss_recovery = 0;
+ bbr->round_count_at_recovery = UINT64_MAX;
+ bbr_restore_cwnd(bbr, cstat);
}
}
@@ -1324,22 +1353,26 @@ static void bbr_cc_on_pkt_lost(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
- bbr_update_on_loss(bbr, cstat, pkt, ts);
+ bbr_handle_lost_packet(bbr, cstat, pkt, ts);
}
static void bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp sent_ts, uint64_t bytes_lost,
+ ngtcp2_tstamp sent_ts,
+ const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
- (void)bytes_lost;
+ (void)ack;
- if (bbr->in_loss_recovery ||
- bbr->congestion_recovery_start_ts != UINT64_MAX ||
- in_congestion_recovery(cstat, sent_ts)) {
+ if (bbr->in_loss_recovery || in_congestion_recovery(cstat, sent_ts)) {
return;
}
- bbr->congestion_recovery_start_ts = ts;
+ bbr->in_loss_recovery = 1;
+ bbr->round_count_at_recovery =
+ bbr->round_start ? bbr->round_count : bbr->round_count + 1;
+ bbr_save_cwnd(bbr, cstat);
+
+ cstat->congestion_recovery_start_ts = ts;
}
static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc,
@@ -1348,14 +1381,13 @@ static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc,
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
(void)ts;
- bbr->congestion_recovery_start_ts = UINT64_MAX;
cstat->congestion_recovery_start_ts = UINT64_MAX;
- if (bbr->in_loss_recovery) {
- bbr->in_loss_recovery = 0;
- bbr->round_count_at_recovery = UINT64_MAX;
- bbr_restore_cwnd(bbr, cstat);
- }
+ bbr->in_loss_recovery = 0;
+ bbr->round_count_at_recovery = UINT64_MAX;
+
+ bbr_restore_cwnd(bbr, cstat);
+ bbr_handle_spurious_loss_detection(bbr, cstat);
}
static void bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
@@ -1365,7 +1397,6 @@ static void bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
(void)ts;
cstat->congestion_recovery_start_ts = UINT64_MAX;
- bbr->congestion_recovery_start_ts = UINT64_MAX;
bbr->in_loss_recovery = 0;
bbr->round_count_at_recovery = UINT64_MAX;
@@ -1380,6 +1411,11 @@ static void bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
bbr_handle_recovery(bbr, cstat, ack);
+
+ if (ack->bytes_delivered == 0) {
+ return;
+ }
+
bbr_update_on_ack(bbr, cstat, ack, ts);
}
@@ -1399,23 +1435,23 @@ static void bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log,
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
- ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
- const ngtcp2_rand_ctx *rand_ctx) {
- memset(bbr, 0, sizeof(*bbr));
-
- bbr->cc.log = log;
- bbr->cc.on_pkt_lost = bbr_cc_on_pkt_lost;
- bbr->cc.congestion_event = bbr_cc_congestion_event;
- bbr->cc.on_spurious_congestion = bbr_cc_on_spurious_congestion;
- bbr->cc.on_persistent_congestion = bbr_cc_on_persistent_congestion;
- bbr->cc.on_ack_recv = bbr_cc_on_ack_recv;
- bbr->cc.on_pkt_sent = bbr_cc_on_pkt_sent;
- bbr->cc.reset = bbr_cc_reset;
-
- bbr->rst = rst;
- bbr->rand = rand;
- bbr->rand_ctx = *rand_ctx;
- bbr->initial_cwnd = cstat->cwnd;
+ ngtcp2_tstamp initial_ts, ngtcp2_pcg32 *pcg) {
+ *bbr = (ngtcp2_cc_bbr){
+ .cc =
+ {
+ .log = log,
+ .on_pkt_lost = bbr_cc_on_pkt_lost,
+ .congestion_event = bbr_cc_congestion_event,
+ .on_spurious_congestion = bbr_cc_on_spurious_congestion,
+ .on_persistent_congestion = bbr_cc_on_persistent_congestion,
+ .on_ack_recv = bbr_cc_on_ack_recv,
+ .on_pkt_sent = bbr_cc_on_pkt_sent,
+ .reset = bbr_cc_reset,
+ },
+ .rst = rst,
+ .pcg = pcg,
+ .initial_cwnd = cstat->cwnd,
+ };
bbr_on_init(bbr, cstat, initial_ts);
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_bbr.h b/third_party/ngtcp2/lib/ngtcp2_bbr.h
index e823711a500..5177944b290 100644
--- a/third_party/ngtcp2/lib/ngtcp2_bbr.h
+++ b/third_party/ngtcp2/lib/ngtcp2_bbr.h
@@ -35,6 +35,7 @@
#include "ngtcp2_window_filter.h"
typedef struct ngtcp2_rst ngtcp2_rst;
+typedef struct ngtcp2_pcg32 ngtcp2_pcg32;
typedef enum ngtcp2_bbr_state {
NGTCP2_BBR_STATE_STARTUP,
@@ -62,8 +63,7 @@ typedef struct ngtcp2_cc_bbr {
uint64_t initial_cwnd;
ngtcp2_rst *rst;
- ngtcp2_rand rand;
- ngtcp2_rand_ctx rand_ctx;
+ ngtcp2_pcg32 *pcg;
/* max_bw_filter for tracking the maximum recent delivery rate
samples for estimating max_bw. */
@@ -75,8 +75,8 @@ typedef struct ngtcp2_cc_bbr {
ngtcp2_tstamp min_rtt_stamp;
ngtcp2_tstamp probe_rtt_done_stamp;
int probe_rtt_round_done;
- uint64_t prior_cwnd;
int idle_restart;
+ uint64_t prior_cwnd;
ngtcp2_tstamp extra_acked_interval_start;
uint64_t extra_acked_delivered;
@@ -91,8 +91,8 @@ typedef struct ngtcp2_cc_bbr {
/* Round counting */
uint64_t next_round_delivered;
- int round_start;
uint64_t round_count;
+ int round_start;
/* Full pipe */
uint64_t full_bw;
@@ -106,7 +106,12 @@ typedef struct ngtcp2_cc_bbr {
ngtcp2_bbr_state state;
uint64_t cwnd_gain_h;
- int loss_round_start;
+ /* Backup for spurious losses */
+ ngtcp2_bbr_state undo_state;
+ uint64_t undo_bw_shortterm;
+ uint64_t undo_inflight_shortterm;
+ uint64_t undo_inflight_longterm;
+
uint64_t loss_round_delivered;
uint64_t rounds_since_bw_probe;
uint64_t max_bw;
@@ -120,23 +125,22 @@ typedef struct ngtcp2_cc_bbr {
ngtcp2_tstamp cycle_stamp;
ngtcp2_bbr_ack_phase ack_phase;
ngtcp2_duration bw_probe_wait;
- int bw_probe_samples;
size_t bw_probe_up_rounds;
uint64_t bw_probe_up_acks;
uint64_t inflight_longterm;
- int probe_rtt_expired;
ngtcp2_duration probe_rtt_min_delay;
ngtcp2_tstamp probe_rtt_min_stamp;
- int in_loss_recovery;
uint64_t round_count_at_recovery;
uint64_t max_inflight;
- ngtcp2_tstamp congestion_recovery_start_ts;
uint64_t bdp;
+ int loss_round_start;
+ int bw_probe_samples;
+ int probe_rtt_expired;
+ int in_loss_recovery;
} ngtcp2_cc_bbr;
void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log,
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
- ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
- const ngtcp2_rand_ctx *rand_ctx);
+ ngtcp2_tstamp initial_ts, ngtcp2_pcg32 *pcg);
#endif /* !defined(NGTCP2_BBR_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_buf.c b/third_party/ngtcp2/lib/ngtcp2_buf.c
index 75326d6b76b..083a766281b 100644
--- a/third_party/ngtcp2/lib/ngtcp2_buf.c
+++ b/third_party/ngtcp2/lib/ngtcp2_buf.c
@@ -26,8 +26,12 @@
#include "ngtcp2_mem.h"
void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
- buf->begin = buf->pos = buf->last = begin;
- buf->end = begin + len;
+ *buf = (ngtcp2_buf){
+ .begin = begin,
+ .end = begin + len,
+ .pos = begin,
+ .last = begin,
+ };
}
void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; }
@@ -36,6 +40,12 @@ size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
return (size_t)(buf->end - buf->begin);
}
+void ngtcp2_buf_trunc(ngtcp2_buf *buf, size_t len) {
+ if (ngtcp2_buf_len(buf) > len) {
+ buf->last = buf->pos + len;
+ }
+}
+
int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
const ngtcp2_mem *mem) {
*pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len);
diff --git a/third_party/ngtcp2/lib/ngtcp2_buf.h b/third_party/ngtcp2/lib/ngtcp2_buf.h
index e87adb11991..b59ac9a54b3 100644
--- a/third_party/ngtcp2/lib/ngtcp2_buf.h
+++ b/third_party/ngtcp2/lib/ngtcp2_buf.h
@@ -62,7 +62,9 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf);
* written to the underlying buffer. In other words, it returns
* buf->end - buf->last.
*/
-#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last)
+static inline size_t ngtcp2_buf_left(const ngtcp2_buf *buf) {
+ return (size_t)(buf->end - buf->last);
+}
/*
* ngtcp2_buf_len returns the number of bytes left to read. In other
@@ -76,6 +78,13 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf);
*/
size_t ngtcp2_buf_cap(const ngtcp2_buf *buf);
+/*
+ * ngtcp2_buf_trunc truncates the number of bytes to read to at most
+ * |len|. In other words, it sets buf->last = buf->pos + len if
+ * ngtcp2_buf_len(buf) > len.
+ */
+void ngtcp2_buf_trunc(ngtcp2_buf *buf, size_t len);
+
/*
* ngtcp2_buf_chain is a linked list of ngtcp2_buf.
*/
diff --git a/third_party/ngtcp2/lib/ngtcp2_callbacks.c b/third_party/ngtcp2/lib/ngtcp2_callbacks.c
new file mode 100644
index 00000000000..1d65d93d566
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_callbacks.c
@@ -0,0 +1,75 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_callbacks.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_unreachable.h"
+
+static void callbacks_copy(ngtcp2_callbacks *dest, const ngtcp2_callbacks *src,
+ int callbacks_version) {
+ assert(callbacks_version != NGTCP2_CALLBACKS_VERSION);
+
+ memcpy(dest, src, ngtcp2_callbackslen_version(callbacks_version));
+}
+
+const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest(
+ ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src) {
+ if (callbacks_version == NGTCP2_CALLBACKS_VERSION) {
+ return src;
+ }
+
+ *dest = (ngtcp2_callbacks){0};
+
+ callbacks_copy(dest, src, callbacks_version);
+
+ return dest;
+}
+
+void ngtcp2_callbacks_convert_to_old(int callbacks_version,
+ ngtcp2_callbacks *dest,
+ const ngtcp2_callbacks *src) {
+ assert(callbacks_version != NGTCP2_CALLBACKS_VERSION);
+
+ callbacks_copy(dest, src, callbacks_version);
+}
+
+size_t ngtcp2_callbackslen_version(int callbacks_version) {
+ ngtcp2_callbacks callbacks;
+
+ switch (callbacks_version) {
+ case NGTCP2_CALLBACKS_VERSION:
+ return sizeof(callbacks);
+ case NGTCP2_CALLBACKS_V2:
+ return offsetof(ngtcp2_callbacks, begin_path_validation) +
+ sizeof(callbacks.begin_path_validation);
+ case NGTCP2_CALLBACKS_V1:
+ return offsetof(ngtcp2_callbacks, tls_early_data_rejected) +
+ sizeof(callbacks.tls_early_data_rejected);
+ default:
+ ngtcp2_unreachable();
+ }
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_callbacks.h b/third_party/ngtcp2/lib/ngtcp2_callbacks.h
new file mode 100644
index 00000000000..751766bb83e
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_callbacks.h
@@ -0,0 +1,73 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CALLBACKS_H
+#define NGTCP2_CALLBACKS_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* defined(HAVE_CONFIG_H) */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_callbacks_convert_to_latest converts |src| of version
+ * |callbacks_version| to the latest version NGTCP2_CALLBACKS_VERSION.
+ *
+ * |dest| must point to the latest version. |src| may be the older
+ * version, and if so, it may have fewer fields. Accessing those
+ * fields causes undefined behavior.
+ *
+ * If |callbacks_version| == NGTCP2_CALLBACKS_VERSION, no conversion
+ * is made, and |src| is returned. Otherwise, first |dest| is
+ * zero-initialized, and then all valid fields in |src| are copied
+ * into |dest|. Finally, |dest| is returned.
+ */
+const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest(
+ ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src);
+
+/*
+ * ngtcp2_callbacks_convert_to_old converts |src| of the latest
+ * version to |dest| of version |callbacks_version|.
+ *
+ * |callbacks_version| must not be the latest version
+ * NGTCP2_CALLBACKS_VERSION.
+ *
+ * |dest| points to the older version, and it may have fewer fields.
+ * Accessing those fields causes undefined behavior.
+ *
+ * This function copies all valid fields in version
+ * |callbacks_version| from |src| to |dest|.
+ */
+void ngtcp2_callbacks_convert_to_old(int callbacks_version,
+ ngtcp2_callbacks *dest,
+ const ngtcp2_callbacks *src);
+
+/*
+ * ngtcp2_callbackslen_version returns the effective length of
+ * ngtcp2_callbacks at the version |callbacks_version|.
+ */
+size_t ngtcp2_callbackslen_version(int callbacks_version);
+
+#endif /* !defined(NGTCP2_CALLBACKS_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_cc.c b/third_party/ngtcp2/lib/ngtcp2_cc.c
index ad3665b6cf6..73dfde6904b 100644
--- a/third_party/ngtcp2/lib/ngtcp2_cc.c
+++ b/third_party/ngtcp2/lib/ngtcp2_cc.c
@@ -41,6 +41,40 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
return ngtcp2_min_uint64(10 * max_udp_payload_size, n);
}
+/* 1.25 is the under-utilization avoidance factor described in
+ https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */
+#define NGTCP2_CC_PACING_GAIN_H 125
+
+static void init_pacing_rate(ngtcp2_conn_stat *cstat) {
+ assert(cstat->cwnd);
+
+ cstat->pacing_interval_m = ngtcp2_max_uint64(
+ (NGTCP2_MILLISECONDS << 10) * 100 / NGTCP2_CC_PACING_GAIN_H / cstat->cwnd,
+ 1);
+ cstat->send_quantum = 10 * cstat->max_tx_udp_payload_size;
+}
+
+static void set_pacing_rate(ngtcp2_conn_stat *cstat) {
+ size_t send_quantum = 64 * 1024;
+
+ assert(cstat->cwnd);
+
+ cstat->pacing_interval_m =
+ ((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS
+ : cstat->smoothed_rtt)
+ << 10) *
+ 100 / NGTCP2_CC_PACING_GAIN_H / cstat->cwnd;
+
+ cstat->pacing_interval_m = ngtcp2_max_uint64(cstat->pacing_interval_m, 1);
+
+ send_quantum =
+ ngtcp2_min_size(send_quantum, (size_t)((NGTCP2_MILLISECONDS << 10) /
+ cstat->pacing_interval_m));
+
+ cstat->send_quantum =
+ ngtcp2_max_size(send_quantum, 10 * cstat->max_tx_udp_payload_size);
+}
+
ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
size_t pktlen, ngtcp2_pktns_id pktns_id,
ngtcp2_tstamp sent_ts, uint64_t lost,
@@ -56,19 +90,26 @@ ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
return pkt;
}
-static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; }
-
-void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
- memset(reno, 0, sizeof(*reno));
+static void reno_cc_reset(ngtcp2_cc_reno *reno, ngtcp2_conn_stat *cstat) {
+ reno->pending_add = 0;
- reno->cc.log = log;
- reno->cc.on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked;
- reno->cc.congestion_event = ngtcp2_cc_reno_cc_congestion_event;
- reno->cc.on_persistent_congestion =
- ngtcp2_cc_reno_cc_on_persistent_congestion;
- reno->cc.reset = ngtcp2_cc_reno_cc_reset;
+ init_pacing_rate(cstat);
+}
- reno_cc_reset(reno);
+void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat) {
+ *reno = (ngtcp2_cc_reno){
+ .cc =
+ {
+ .log = log,
+ .on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked,
+ .congestion_event = ngtcp2_cc_reno_cc_congestion_event,
+ .on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion,
+ .reset = ngtcp2_cc_reno_cc_reset,
+ },
+ };
+
+ reno_cc_reset(reno, cstat);
}
static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
@@ -90,9 +131,12 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
if (cstat->cwnd < cstat->ssthresh) {
cstat->cwnd += pkt->pktlen;
- ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
- "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
- pkt->pkt_num, cstat->cwnd);
+
+ set_pacing_rate(cstat);
+
+ ngtcp2_log_infof(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd);
return;
}
@@ -100,14 +144,17 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
reno->pending_add = m % cstat->cwnd;
cstat->cwnd += m / cstat->cwnd;
+
+ set_pacing_rate(cstat);
}
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
- uint64_t bytes_lost, ngtcp2_tstamp ts) {
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
uint64_t min_cwnd;
- (void)bytes_lost;
+ (void)ack;
if (in_congestion_recovery(cstat, sent_ts)) {
return;
@@ -121,9 +168,11 @@ void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
reno->pending_add = 0;
- ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
- "reduce cwnd because of packet loss cwnd=%" PRIu64,
- cstat->cwnd);
+ set_pacing_rate(cstat);
+
+ ngtcp2_log_infof(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "reduce cwnd because of packet loss cwnd=%" PRIu64,
+ cstat->cwnd);
}
void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
@@ -134,31 +183,32 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ set_pacing_rate(cstat);
}
void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
- (void)cstat;
(void)ts;
- reno_cc_reset(reno);
+ reno_cc_reset(reno, cstat);
}
static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
v->cwnd_prior = 0;
v->w_max = 0;
- v->k = 0;
+ v->k_m = 0;
v->epoch_start = UINT64_MAX;
v->w_est = 0;
v->app_limited_start_ts = UINT64_MAX;
v->app_limited_duration = 0;
- v->pending_bytes_delivered = 0;
- v->pending_est_bytes_delivered = 0;
+ v->pending_bytes_acked = 0;
+ v->pending_est_bytes_acked = 0;
}
-static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
+static void cubic_cc_reset(ngtcp2_cc_cubic *cubic, ngtcp2_conn_stat *cstat) {
cubic_vars_reset(&cubic->current);
cubic_vars_reset(&cubic->undo.v);
cubic->undo.cwnd = 0;
@@ -172,23 +222,26 @@ static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
cubic->hs.css_round = 0;
cubic->next_round_delivered = 0;
+
+ init_pacing_rate(cstat);
}
void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
- ngtcp2_rst *rst) {
- memset(cubic, 0, sizeof(*cubic));
-
- cubic->cc.log = log;
- cubic->cc.on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv;
- cubic->cc.congestion_event = ngtcp2_cc_cubic_cc_congestion_event;
- cubic->cc.on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion;
- cubic->cc.on_persistent_congestion =
- ngtcp2_cc_cubic_cc_on_persistent_congestion;
- cubic->cc.reset = ngtcp2_cc_cubic_cc_reset;
-
- cubic->rst = rst;
-
- cubic_cc_reset(cubic);
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst) {
+ *cubic = (ngtcp2_cc_cubic){
+ .cc =
+ {
+ .log = log,
+ .on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv,
+ .congestion_event = ngtcp2_cc_cubic_cc_congestion_event,
+ .on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion,
+ .on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion,
+ .reset = ngtcp2_cc_cubic_cc_reset,
+ },
+ .rst = rst,
+ };
+
+ cubic_cc_reset(cubic, cstat);
}
uint64_t ngtcp2_cbrt(uint64_t n) {
@@ -208,7 +261,6 @@ uint64_t ngtcp2_cbrt(uint64_t n) {
y <<= 1;
b = 3 * y * (y + 1) + 1;
if (n >= b) {
- n -= b;
y++;
}
@@ -223,30 +275,54 @@ uint64_t ngtcp2_cbrt(uint64_t n) {
#define NGTCP2_HS_CSS_GROWTH_DIVISOR 4
#define NGTCP2_HS_CSS_ROUNDS 5
-static int64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic,
- const ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts) {
+static uint64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic,
+ const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
ngtcp2_duration t = ts - cubic->current.epoch_start;
- int64_t tx = (int64_t)((t << 10) / NGTCP2_SECONDS);
- int64_t time_delta = ngtcp2_min_int64(tx - cubic->current.k, 3600 << 10);
- int64_t delta = ((((time_delta * time_delta) >> 10) * time_delta) >> 20) *
- (int64_t)cstat->max_tx_udp_payload_size * 4 / 10;
+ uint64_t tx_m = (t << 10) / NGTCP2_SECONDS;
+ int neg = tx_m < cubic->current.k_m;
+ uint64_t time_delta_m;
+ uint64_t delta;
+
+ /* Avoid signed bit-shift */
+ if (neg) {
+ time_delta_m = cubic->current.k_m - tx_m;
+ } else {
+ time_delta_m = tx_m - cubic->current.k_m;
+ }
+
+ time_delta_m = ngtcp2_min_uint64(time_delta_m, 3600 << 10);
+
+ delta = ((((time_delta_m * time_delta_m) >> 10) * time_delta_m) >> 10) *
+ cstat->max_tx_udp_payload_size * 4 / 10;
+ delta >>= 10;
+
+ if (neg) {
+ if (cubic->current.w_max < delta) {
+ /* Negative w_cubic is not interesting. */
+ return 0;
+ }
- return (int64_t)cubic->current.w_max + delta;
+ return cubic->current.w_max - delta;
+ }
+
+ return cubic->current.w_max + delta;
}
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
- int64_t w_cubic, w_cubic_next;
+ uint64_t w_cubic, w_cubic_next;
uint64_t target, m;
+ uint64_t bytes_acked;
ngtcp2_duration rtt_thresh;
int round_start;
int is_app_limited =
cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited;
- if (in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
+ if (ack->bytes_delivered == 0 ||
+ in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
return;
}
@@ -266,9 +342,11 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cstat->cwnd += ack->bytes_delivered;
}
- ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
- "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64,
- ack->bytes_delivered, cstat->cwnd);
+ set_pacing_rate(cstat);
+
+ ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64,
+ ack->bytes_delivered, cstat->cwnd);
}
if (round_start) {
@@ -346,44 +424,54 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cubic, cstat,
ts - cubic->current.app_limited_duration + cstat->smoothed_rtt);
- if (w_cubic_next < (int64_t)cstat->cwnd) {
+ if (w_cubic_next < cstat->cwnd) {
target = cstat->cwnd;
- } else if (2 * w_cubic_next > 3 * (int64_t)cstat->cwnd) {
+ } else if (2 * w_cubic_next > 3 * cstat->cwnd) {
target = cstat->cwnd * 3 / 2;
} else {
- assert(w_cubic_next >= 0);
- target = (uint64_t)w_cubic_next;
+ target = w_cubic_next;
}
- m = ack->bytes_delivered * cstat->max_tx_udp_payload_size +
- cubic->current.pending_est_bytes_delivered;
- cubic->current.pending_est_bytes_delivered = m % cstat->cwnd;
+ bytes_acked = ack->bytes_delivered * cstat->max_tx_udp_payload_size;
+ m = (bytes_acked + cubic->current.pending_est_bytes_acked) / cstat->cwnd;
+
+ cubic->current.pending_est_bytes_acked += bytes_acked;
+ cubic->current.pending_est_bytes_acked -= m * cstat->cwnd;
+
+ assert(cubic->current.pending_est_bytes_acked < cstat->cwnd);
if (cubic->current.w_est < cubic->current.cwnd_prior) {
- cubic->current.w_est += m * 9 / 17 / cstat->cwnd;
+ cubic->current.w_est += m * 9 / 17;
} else {
- cubic->current.w_est += m / cstat->cwnd;
+ cubic->current.w_est += m;
}
- if ((int64_t)cubic->current.w_est > w_cubic) {
+ if (cubic->current.w_est > w_cubic) {
cstat->cwnd = cubic->current.w_est;
} else {
- m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size +
- cubic->current.pending_bytes_delivered;
- cstat->cwnd += m / cstat->cwnd;
- cubic->current.pending_bytes_delivered = m % cstat->cwnd;
+ bytes_acked = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size;
+ m = (bytes_acked + cubic->current.pending_bytes_acked) / cstat->cwnd;
+
+ cubic->current.pending_bytes_acked += bytes_acked;
+ cubic->current.pending_bytes_acked -= m * cstat->cwnd;
+
+ assert(cubic->current.pending_bytes_acked < cstat->cwnd);
+
+ cstat->cwnd += m;
}
- ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
- "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
- " k=%" PRIi64 " target=%" PRIu64 " w_est=%" PRIu64,
- ack->bytes_delivered, cstat->cwnd, cubic->current.k, target,
- cubic->current.w_est);
+ set_pacing_rate(cstat);
+
+ ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
+ " k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64,
+ ack->bytes_delivered, cstat->cwnd, cubic->current.k_m,
+ target, cubic->current.w_est);
}
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
- uint64_t bytes_lost,
+ const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
uint64_t flight_size;
@@ -404,8 +492,8 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cubic->current.epoch_start = ts;
cubic->current.app_limited_start_ts = UINT64_MAX;
cubic->current.app_limited_duration = 0;
- cubic->current.pending_bytes_delivered = 0;
- cubic->current.pending_est_bytes_delivered = 0;
+ cubic->current.pending_bytes_acked = 0;
+ cubic->current.pending_est_bytes_acked = 0;
if (cstat->cwnd < cubic->current.w_max) {
cubic->current.w_max = cstat->cwnd * 17 / 20;
@@ -413,10 +501,13 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cubic->current.w_max = cstat->cwnd;
}
+ cubic->current.w_max =
+ ngtcp2_max_uint64(cubic->current.w_max, 2 * cstat->max_tx_udp_payload_size);
+
cstat->ssthresh = cstat->cwnd * 7 / 10;
if (cubic->rst->rs.delivered * 2 < cstat->cwnd) {
- flight_size = cstat->bytes_in_flight + bytes_lost;
+ flight_size = cstat->bytes_in_flight + ack->bytes_lost;
cstat->ssthresh = ngtcp2_min_uint64(
cstat->ssthresh,
ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size));
@@ -430,21 +521,18 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
cubic->current.w_est = cstat->cwnd;
- if (cstat->cwnd < cubic->current.w_max) {
- cwnd_delta = cubic->current.w_max - cstat->cwnd;
- } else {
- cwnd_delta = cstat->cwnd - cubic->current.w_max;
- }
+ assert(cubic->current.w_max >= cstat->cwnd);
- cubic->current.k = (int64_t)ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 /
- cstat->max_tx_udp_payload_size);
- if (cstat->cwnd >= cubic->current.w_max) {
- cubic->current.k = -cubic->current.k;
- }
+ cwnd_delta = cubic->current.w_max - cstat->cwnd;
+
+ cubic->current.k_m =
+ ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 / cstat->max_tx_udp_payload_size);
- ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
- "reduce cwnd because of packet loss cwnd=%" PRIu64,
- cstat->cwnd);
+ set_pacing_rate(cstat);
+
+ ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "reduce cwnd because of packet loss cwnd=%" PRIu64,
+ cstat->cwnd);
}
void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc,
@@ -460,10 +548,12 @@ void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc,
cstat->cwnd = cubic->undo.cwnd;
cstat->ssthresh = cubic->undo.ssthresh;
- ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
- "spurious congestion is detected and congestion state is "
- "restored cwnd=%" PRIu64,
- cstat->cwnd);
+ set_pacing_rate(cstat);
+
+ ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
+ "spurious congestion is detected and congestion state is "
+ "restored cwnd=%" PRIu64,
+ cstat->cwnd);
}
cubic_vars_reset(&cubic->undo.v);
@@ -477,17 +567,18 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
(void)ts;
- cubic_cc_reset(cubic);
+ cubic_cc_reset(cubic, cstat);
cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ set_pacing_rate(cstat);
}
void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
- (void)cstat;
(void)ts;
- cubic_cc_reset(cubic);
+ cubic_cc_reset(cubic, cstat);
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_cc.h b/third_party/ngtcp2/lib/ngtcp2_cc.h
index 296a1a433f1..a19e90b1d0d 100644
--- a/third_party/ngtcp2/lib/ngtcp2_cc.h
+++ b/third_party/ngtcp2/lib/ngtcp2_cc.h
@@ -88,11 +88,6 @@ typedef struct ngtcp2_cc_pkt {
* acknowledged and lost bytes.
*/
typedef struct ngtcp2_cc_ack {
- /**
- * :member:`prior_bytes_in_flight` is the in-flight bytes before
- * processing this ACK.
- */
- uint64_t prior_bytes_in_flight;
/**
* :member:`bytes_delivered` is the number of bytes acknowledged.
*/
@@ -143,13 +138,16 @@ typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
* @functypedef
*
* :type:`ngtcp2_cc_congestion_event` is a callback function which is
- * called when congestion event happens (e.g., when packet is lost).
- * |bytes_lost| is the number of bytes lost in this congestion event.
+ * called when congestion event happens (e.g., when packet is lost or
+ * due to ECN). |ack| contains information after ACK processing.
+ * This callback may be called from non-ACK processing context. In
+ * that case, the information only taken from |ack| processing has
+ * default values, like 0 or UINT64_MAX;
*/
typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
- uint64_t bytes_lost,
+ const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
/**
@@ -186,20 +184,12 @@ typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
* @functypedef
*
* :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is
- * called when an ack-eliciting packet is sent.
+ * called when an ack-eliciting packet is sent. The lost,
+ * tx_in_flight, and is_app_limited fields in |pkt| are set to 0.
*/
typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt);
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is
- * called when new RTT sample is obtained.
- */
-typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
-
/**
* @functypedef
*
@@ -209,28 +199,6 @@ typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
-/**
- * @enum
- *
- * :type:`ngtcp2_cc_event_type` defines congestion control events.
- */
-typedef enum ngtcp2_cc_event_type {
- /**
- * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet
- * is sent and no other ack-eliciting packet is present.
- */
- NGTCP2_CC_EVENT_TYPE_TX_START
-} ngtcp2_cc_event_type;
-
-/**
- * @functypedef
- *
- * :type:`ngtcp2_cc_event` is a callback function which is called when
- * a specific event happens.
- */
-typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
- ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
-
/**
* @struct
*
@@ -254,7 +222,7 @@ typedef struct ngtcp2_cc {
ngtcp2_cc_on_pkt_lost on_pkt_lost;
/**
* :member:`congestion_event` is a callback function which is called
- * when congestion event happens (.e.g, packet is lost).
+ * when congestion event happens (e.g., packet is lost).
*/
ngtcp2_cc_congestion_event congestion_event;
/**
@@ -277,21 +245,11 @@ typedef struct ngtcp2_cc {
* ack-eliciting packet is sent.
*/
ngtcp2_cc_on_pkt_sent on_pkt_sent;
- /**
- * :member:`new_rtt_sample` is a callback function which is called
- * when new RTT sample is obtained.
- */
- ngtcp2_cc_new_rtt_sample new_rtt_sample;
/**
* :member:`reset` is a callback function which is called when
* congestion control state must be reset.
*/
ngtcp2_cc_reset reset;
- /**
- * :member:`event` is a callback function which is called when a
- * specific event happens.
- */
- ngtcp2_cc_event event;
} ngtcp2_cc;
/*
@@ -310,14 +268,16 @@ typedef struct ngtcp2_cc_reno {
uint64_t pending_add;
} ngtcp2_cc_reno;
-void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log);
+void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat);
void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
- uint64_t bytes_lost, ngtcp2_tstamp ts);
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
ngtcp2_conn_stat *cstat,
@@ -329,7 +289,8 @@ void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
typedef struct ngtcp2_cubic_vars {
uint64_t cwnd_prior;
uint64_t w_max;
- int64_t k;
+ /* CUBIC K with 10 bits extra precision. */
+ uint64_t k_m;
ngtcp2_tstamp epoch_start;
uint64_t w_est;
@@ -339,8 +300,8 @@ typedef struct ngtcp2_cubic_vars {
/* app_limited_duration is the cumulative duration where a
connection is under app limited when ACK is received. */
ngtcp2_duration app_limited_duration;
- uint64_t pending_bytes_delivered;
- uint64_t pending_est_bytes_delivered;
+ uint64_t pending_bytes_acked;
+ uint64_t pending_est_bytes_acked;
} ngtcp2_cubic_vars;
/* ngtcp2_cc_cubic is CUBIC congestion controller. */
@@ -370,14 +331,15 @@ typedef struct ngtcp2_cc_cubic {
} ngtcp2_cc_cubic;
void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cc, ngtcp2_log *log,
- ngtcp2_rst *rst);
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst);
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp sent_ts,
- uint64_t bytes_lost, ngtcp2_tstamp ts);
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
ngtcp2_conn_stat *cstat,
diff --git a/third_party/ngtcp2/lib/ngtcp2_cid.c b/third_party/ngtcp2/lib/ngtcp2_cid.c
index acbee78aaf4..4eba2aaca5d 100644
--- a/third_party/ngtcp2/lib/ngtcp2_cid.c
+++ b/third_party/ngtcp2/lib/ngtcp2_cid.c
@@ -29,8 +29,9 @@
#include "ngtcp2_path.h"
#include "ngtcp2_str.h"
+#include "ngtcp2_pkt.h"
-void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); }
+void ngtcp2_cid_zero(ngtcp2_cid *cid) { *cid = (ngtcp2_cid){0}; }
void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) {
assert(datalen <= NGTCP2_MAX_CIDLEN);
@@ -58,11 +59,13 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) {
int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; }
void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) {
- scid->pe.index = NGTCP2_PQ_BAD_INDEX;
- scid->seq = seq;
- scid->cid = *cid;
- scid->retired_ts = UINT64_MAX;
- scid->flags = NGTCP2_SCID_FLAG_NONE;
+ *scid = (ngtcp2_scid){
+ .pe.index = NGTCP2_PQ_BAD_INDEX,
+ .seq = seq,
+ .cid = *cid,
+ .retired_ts = UINT64_MAX,
+ .flags = NGTCP2_SCID_FLAG_NONE,
+ };
}
void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) {
@@ -72,12 +75,12 @@ void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) {
}
void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token) {
+ const ngtcp2_stateless_reset_token *token) {
dcid->seq = seq;
dcid->cid = *cid;
if (token) {
- memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dcid->token = *token;
dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT;
} else {
dcid->flags = NGTCP2_DCID_FLAG_NONE;
@@ -91,11 +94,12 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
}
-void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) {
+void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid,
+ const ngtcp2_stateless_reset_token *token) {
assert(token);
dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
- memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dcid->token = *token;
}
void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) {
@@ -104,7 +108,7 @@ void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) {
void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
ngtcp2_dcid_init(dest, src->seq, &src->cid,
- (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token
+ (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? &src->token
: NULL);
ngtcp2_path_copy(&dest->ps.path, &src->ps.path);
dest->retired_ts = src->retired_ts;
@@ -121,18 +125,19 @@ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
- memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dest->token = src->token;
} else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT;
}
}
int ngtcp2_dcid_verify_uniqueness(const ngtcp2_dcid *dcid, uint64_t seq,
- const ngtcp2_cid *cid, const uint8_t *token) {
+ const ngtcp2_cid *cid,
+ const ngtcp2_stateless_reset_token *token) {
if (dcid->seq == seq) {
return ngtcp2_cid_eq(&dcid->cid, cid) &&
(dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
- memcmp(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN) == 0
+ ngtcp2_stateless_reset_token_eq(&dcid->token, token)
? 0
: NGTCP2_ERR_PROTO;
}
@@ -140,12 +145,12 @@ int ngtcp2_dcid_verify_uniqueness(const ngtcp2_dcid *dcid, uint64_t seq,
return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO;
}
-int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
- const ngtcp2_path *path,
- const uint8_t *token) {
+int ngtcp2_dcid_verify_stateless_reset_token(
+ const ngtcp2_dcid *dcid, const ngtcp2_path *path,
+ const ngtcp2_stateless_reset_token *token) {
return ngtcp2_path_eq(&dcid->ps.path, path) &&
(dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
- ngtcp2_cmemeq(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN)
+ ngtcp2_cmemeq(dcid->token.data, token->data, sizeof(token->data))
? 0
: NGTCP2_ERR_INVALID_ARGUMENT;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_cid.h b/third_party/ngtcp2/lib/ngtcp2_cid.h
index 9321cfb64e6..f4956b0bac0 100644
--- a/third_party/ngtcp2/lib/ngtcp2_cid.h
+++ b/third_party/ngtcp2/lib/ngtcp2_cid.h
@@ -35,13 +35,13 @@
#include "ngtcp2_path.h"
/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_SCID_FLAG_NONE 0x00u
+#define NGTCP2_SCID_FLAG_NONE 0x00U
/* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that
a remote endpoint uses this particular Connection ID. */
-#define NGTCP2_SCID_FLAG_USED 0x01u
+#define NGTCP2_SCID_FLAG_USED 0x01U
/* NGTCP2_SCID_FLAG_RETIRED indicates that this particular Connection
ID is retired. */
-#define NGTCP2_SCID_FLAG_RETIRED 0x02u
+#define NGTCP2_SCID_FLAG_RETIRED 0x02U
typedef struct ngtcp2_scid {
ngtcp2_pq_entry pe;
@@ -57,13 +57,13 @@ typedef struct ngtcp2_scid {
} ngtcp2_scid;
/* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_DCID_FLAG_NONE 0x00u
+#define NGTCP2_DCID_FLAG_NONE 0x00U
/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path
has been validated. */
-#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u
+#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01U
/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset
token is set in token field. */
-#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u
+#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02U
typedef struct ngtcp2_dcid {
/* seq is the sequence number associated to the Connection ID. */
@@ -93,7 +93,7 @@ typedef struct ngtcp2_dcid {
/* token is a stateless reset token received along with this
Connection ID. The stateless reset token is tied to the
connection, not to the particular Connection ID. */
- uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ ngtcp2_stateless_reset_token token;
} ngtcp2_dcid;
/* ngtcp2_cid_zero makes |cid| zero-length. */
@@ -123,17 +123,18 @@ void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src);
/*
* ngtcp2_dcid_init initializes |dcid| with the given parameters. If
- * |token| is NULL, the function fills dcid->token with 0. |token|
- * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ * |token| is not NULL, the function sets
+ * NGTCP2_DCID_FLAG_TOKEN_PRESENT flag.
*/
void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token);
+ const ngtcp2_stateless_reset_token *token);
/*
* ngtcp2_dcid_set_token sets |token| to |dcid|. |token| must not be
- * NULL, and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ * NULL.
*/
-void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token);
+void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid,
+ const ngtcp2_stateless_reset_token *token);
/*
* ngtcp2_dcid_set_path sets |path| to |dcid|. It sets
@@ -160,7 +161,8 @@ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
* |token|) tuple against |dcid|.
*/
int ngtcp2_dcid_verify_uniqueness(const ngtcp2_dcid *dcid, uint64_t seq,
- const ngtcp2_cid *cid, const uint8_t *token);
+ const ngtcp2_cid *cid,
+ const ngtcp2_stateless_reset_token *token);
/*
* ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset
@@ -172,9 +174,9 @@ int ngtcp2_dcid_verify_uniqueness(const ngtcp2_dcid *dcid, uint64_t seq,
* NGTCP2_ERR_INVALID_ARGUMENT
* Tokens do not match; or |dcid| does not contain a token.
*/
-int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
- const ngtcp2_path *path,
- const uint8_t *token);
+int ngtcp2_dcid_verify_stateless_reset_token(
+ const ngtcp2_dcid *dcid, const ngtcp2_path *path,
+ const ngtcp2_stateless_reset_token *token);
/* TODO It might be performance win if we store congestion state in
this entry, and restore it when migrate back to this path. */
diff --git a/third_party/ngtcp2/lib/ngtcp2_conn.c b/third_party/ngtcp2/lib/ngtcp2_conn.c
index 6abebee33a8..c470eb05b06 100644
--- a/third_party/ngtcp2/lib/ngtcp2_conn.c
+++ b/third_party/ngtcp2/lib/ngtcp2_conn.c
@@ -39,8 +39,10 @@
#include "ngtcp2_net.h"
#include "ngtcp2_transport_params.h"
#include "ngtcp2_settings.h"
+#include "ngtcp2_callbacks.h"
#include "ngtcp2_tstamp.h"
#include "ngtcp2_frame_chain.h"
+#include "ngtcp2_conn_info.h"
/* NGTCP2_FLOW_WINDOW_RTT_FACTOR is the factor of RTT when flow
control window auto-tuning is triggered. */
@@ -253,13 +255,20 @@ static int conn_call_extend_max_local_streams_uni(ngtcp2_conn *conn,
}
static int conn_call_get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid,
- uint8_t *token, size_t cidlen) {
+ ngtcp2_stateless_reset_token *token,
+ size_t cidlen) {
int rv;
- assert(conn->callbacks.get_new_connection_id);
+ if (conn->callbacks.get_new_connection_id2) {
+ rv = conn->callbacks.get_new_connection_id2(conn, cid, token, cidlen,
+ conn->user_data);
+ } else {
+ assert(conn->callbacks.get_new_connection_id);
+
+ rv = conn->callbacks.get_new_connection_id(conn, cid, token->data, cidlen,
+ conn->user_data);
+ }
- rv = conn->callbacks.get_new_connection_id(conn, cid, token, cidlen,
- conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -283,11 +292,38 @@ static int conn_call_remove_connection_id(ngtcp2_conn *conn,
return 0;
}
+static int conn_call_begin_path_validation(ngtcp2_conn *conn,
+ const ngtcp2_pv *pv) {
+ int rv;
+ uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE;
+ const ngtcp2_path *fallback_path = NULL;
+
+ if (!pv || !conn->callbacks.begin_path_validation) {
+ return 0;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) {
+ flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) {
+ fallback_path = &pv->fallback_dcid.ps.path;
+ }
+
+ rv = conn->callbacks.begin_path_validation(conn, flags, &pv->dcid.ps.path,
+ fallback_path, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv,
ngtcp2_path_validation_result res) {
int rv;
uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE;
- const ngtcp2_path *old_path = NULL;
+ const ngtcp2_path *fallback_path = NULL;
if (!conn->callbacks.path_validation) {
return 0;
@@ -298,17 +334,17 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv,
}
if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) {
- old_path = &pv->fallback_dcid.ps.path;
+ fallback_path = &pv->fallback_dcid.ps.path;
}
- if (conn->server && old_path &&
- (ngtcp2_addr_cmp(&pv->dcid.ps.path.remote, &old_path->remote) &
+ if (conn->server && fallback_path &&
+ (ngtcp2_addr_cmp(&pv->dcid.ps.path.remote, &fallback_path->remote) &
(NGTCP2_ADDR_CMP_FLAG_ADDR | NGTCP2_ADDR_CMP_FLAG_FAMILY))) {
flags |= NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN;
}
- rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, old_path,
- res, conn->user_data);
+ rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path,
+ fallback_path, res, conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -395,14 +431,20 @@ static int conn_call_dcid_status(ngtcp2_conn *conn,
const ngtcp2_dcid *dcid) {
int rv;
- if (!conn->callbacks.dcid_status) {
+ if (conn->callbacks.dcid_status2) {
+ rv = conn->callbacks.dcid_status2(
+ conn, type, dcid->seq, &dcid->cid,
+ (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? &dcid->token : NULL,
+ conn->user_data);
+ } else if (conn->callbacks.dcid_status) {
+ rv = conn->callbacks.dcid_status(
+ conn, type, dcid->seq, &dcid->cid,
+ (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token.data : NULL,
+ conn->user_data);
+ } else {
return 0;
}
- rv = conn->callbacks.dcid_status(
- conn, type, dcid->seq, &dcid->cid,
- (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token : NULL,
- conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -475,12 +517,19 @@ static int conn_call_client_initial(ngtcp2_conn *conn) {
return 0;
}
-static int conn_call_get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data) {
+static int conn_call_get_path_challenge_data(ngtcp2_conn *conn,
+ ngtcp2_path_challenge_data *data) {
int rv;
- assert(conn->callbacks.get_path_challenge_data);
+ if (conn->callbacks.get_path_challenge_data2) {
+ rv = conn->callbacks.get_path_challenge_data2(conn, data, conn->user_data);
+ } else {
+ assert(conn->callbacks.get_path_challenge_data);
+
+ rv = conn->callbacks.get_path_challenge_data(conn, data->data,
+ conn->user_data);
+ }
- rv = conn->callbacks.get_path_challenge_data(conn, data, conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -521,14 +570,24 @@ static int conn_call_recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) {
static int
conn_call_recv_stateless_reset(ngtcp2_conn *conn,
- const ngtcp2_pkt_stateless_reset *sr) {
+ const ngtcp2_pkt_stateless_reset2 *sr) {
int rv;
+ ngtcp2_pkt_stateless_reset legacy_sr;
- if (!conn->callbacks.recv_stateless_reset) {
+ if (conn->callbacks.recv_stateless_reset2) {
+ rv = conn->callbacks.recv_stateless_reset2(conn, sr, conn->user_data);
+ } else if (conn->callbacks.recv_stateless_reset) {
+ memcpy(legacy_sr.stateless_reset_token, sr->token.data,
+ sizeof(legacy_sr.stateless_reset_token));
+ legacy_sr.rand = sr->rand;
+ legacy_sr.randlen = sr->randlen;
+
+ rv =
+ conn->callbacks.recv_stateless_reset(conn, &legacy_sr, conn->user_data);
+ } else {
return 0;
}
- rv = conn->callbacks.recv_stateless_reset(conn, sr, conn->user_data);
if (rv != 0) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -668,13 +727,15 @@ static int conn_call_recv_tx_key(ngtcp2_conn *conn,
return 0;
}
+/*
+ * pktns_init initializes |pktns|. It assumes that the object pointed
+ * by |pktns| is zero-cleared.
+ */
static void pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
ngtcp2_rst *rst, ngtcp2_cc *cc, int64_t initial_pkt_num,
ngtcp2_log *log, ngtcp2_qlog *qlog,
ngtcp2_objalloc *rtb_entry_objalloc,
ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
- memset(pktns, 0, sizeof(*pktns));
-
ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
pktns->tx.last_pkt_num = initial_pkt_num - 1;
@@ -696,7 +757,7 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id,
ngtcp2_log *log, ngtcp2_qlog *qlog,
ngtcp2_objalloc *rtb_entry_objalloc,
ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
- *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns));
+ *ppktns = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_pktns));
if (*ppktns == NULL) {
return NGTCP2_ERR_NOMEM;
}
@@ -809,8 +870,8 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn,
*/
static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) {
/* Initializes them with UINT64_MAX. */
- memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time));
- memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts));
+ memset(cstat->loss_time, 0xFF, sizeof(cstat->loss_time));
+ memset(cstat->last_tx_pkt_ts, 0xFF, sizeof(cstat->last_tx_pkt_ts));
}
/*
@@ -1033,16 +1094,31 @@ conn_set_local_transport_params(ngtcp2_conn *conn,
}
static void conn_update_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns) {
- uint8_t gap;
+ const int64_t min_gap = 3;
+ uint8_t r;
+ int64_t gap;
+
+ assert(INT64_MAX != pktns->tx.skip_pkt.next_pkt_num);
+
+ conn->callbacks.rand(&r, 1, &conn->local.settings.rand_ctx);
+
+ if (1LL << pktns->tx.skip_pkt.exponent >
+ (NGTCP2_MAX_PKT_NUM - min_gap) / ((int64_t)r + 1)) {
+ pktns->tx.skip_pkt.next_pkt_num = INT64_MAX;
+ return;
+ }
+
+ gap = ((int64_t)r + 1) * (1LL << pktns->tx.skip_pkt.exponent++) + min_gap;
- conn->callbacks.rand(&gap, 1, &conn->local.settings.rand_ctx);
+ if (pktns->tx.last_pkt_num > NGTCP2_MAX_PKT_NUM - gap) {
+ pktns->tx.skip_pkt.next_pkt_num = INT64_MAX;
+ return;
+ }
- pktns->tx.skip_pkt.next_pkt_num =
- pktns->tx.last_pkt_num + 3 +
- (int64_t)gap * (1ll << pktns->tx.skip_pkt.exponent++);
+ pktns->tx.skip_pkt.next_pkt_num = pktns->tx.last_pkt_num + gap;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "next skip pkn=%" PRId64,
- pktns->tx.skip_pkt.next_pkt_num);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON, "next skip pkn=%" PRId64,
+ pktns->tx.skip_pkt.next_pkt_num);
}
static int conn_handle_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
@@ -1109,18 +1185,21 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
uint32_t *preferred_versions;
ngtcp2_settings settingsbuf;
ngtcp2_transport_params paramsbuf;
- (void)callbacks_version;
+ ngtcp2_callbacks callbacksbuf;
+ uint64_t seed;
(void)settings_version;
settings =
ngtcp2_settings_convert_to_latest(&settingsbuf, settings_version, settings);
params = ngtcp2_transport_params_convert_to_latest(
&paramsbuf, transport_params_version, params);
+ callbacks = ngtcp2_callbacks_convert_to_latest(&callbacksbuf,
+ callbacks_version, callbacks);
assert(settings->max_window <= NGTCP2_MAX_VARINT);
assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
- assert(settings->max_tx_udp_payload_size);
- assert(settings->max_tx_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE);
+ assert(settings->max_tx_udp_payload_size >= NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+ assert(settings->max_tx_udp_payload_size <= NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE);
assert(settings->initial_pkt_num <= INT32_MAX);
assert(settings->initial_rtt);
assert(params->active_connection_id_limit >=
@@ -1147,15 +1226,17 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
assert(callbacks->hp_mask);
assert(server || callbacks->recv_retry);
assert(callbacks->rand);
- assert(callbacks->get_new_connection_id);
+ assert(callbacks->get_new_connection_id2 || callbacks->get_new_connection_id);
assert(callbacks->update_key);
assert(callbacks->delete_crypto_aead_ctx);
assert(callbacks->delete_crypto_cipher_ctx);
- assert(callbacks->get_path_challenge_data);
+ assert(callbacks->get_path_challenge_data2 ||
+ callbacks->get_path_challenge_data);
assert(!server || !ngtcp2_is_reserved_version(client_chosen_version));
for (i = 0; i < settings->pmtud_probeslen; ++i) {
assert(settings->pmtud_probes[i] > NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+ assert(settings->pmtud_probes[i] <= NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE);
}
if (mem == NULL) {
@@ -1217,7 +1298,11 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem);
- ngtcp2_map_init(&(*pconn)->strms, mem);
+ callbacks->rand((uint8_t *)&seed, sizeof(seed), &settings->rand_ctx);
+ ngtcp2_map_init(&(*pconn)->strms, seed, mem);
+
+ callbacks->rand((uint8_t *)&seed, sizeof(seed), &settings->rand_ctx);
+ ngtcp2_pcg32_init(&(*pconn)->pcg, seed);
ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem);
@@ -1284,17 +1369,17 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
switch (settings->cc_algo) {
case NGTCP2_CC_ALGO_RENO:
- ngtcp2_cc_reno_init(&(*pconn)->reno, &(*pconn)->log);
+ ngtcp2_cc_reno_init(&(*pconn)->reno, &(*pconn)->log, &(*pconn)->cstat);
break;
case NGTCP2_CC_ALGO_CUBIC:
- ngtcp2_cc_cubic_init(&(*pconn)->cubic, &(*pconn)->log, &(*pconn)->rst);
+ ngtcp2_cc_cubic_init(&(*pconn)->cubic, &(*pconn)->log, &(*pconn)->cstat,
+ &(*pconn)->rst);
break;
case NGTCP2_CC_ALGO_BBR:
ngtcp2_cc_bbr_init(&(*pconn)->bbr, &(*pconn)->log, &(*pconn)->cstat,
- &(*pconn)->rst, settings->initial_ts, callbacks->rand,
- &settings->rand_ctx);
+ &(*pconn)->rst, settings->initial_ts, &(*pconn)->pcg);
break;
default:
@@ -1303,6 +1388,9 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
ngtcp2_static_ringbuf_path_history_init(&(*pconn)->path_history);
+ ngtcp2_ratelim_init(&(*pconn)->glitch_rlim, settings->glitch_ratelim_burst,
+ settings->glitch_ratelim_rate, settings->initial_ts);
+
(*pconn)->callbacks = *callbacks;
rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst,
@@ -1470,6 +1558,8 @@ fail_seqgap_push:
fail_token:
ngtcp2_mem_free(mem, *pconn);
+ *pconn = NULL;
+
return rv;
}
@@ -1492,10 +1582,13 @@ int ngtcp2_conn_client_new_versioned(
(*pconn)->state = NGTCP2_CS_CLIENT_INITIAL;
(*pconn)->local.bidi.next_stream_id = 0;
(*pconn)->local.uni.next_stream_id = 2;
+ (*pconn)->flags |= NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO;
rv = ngtcp2_conn_commit_local_transport_params(*pconn);
if (rv != 0) {
ngtcp2_conn_del(*pconn);
+ *pconn = NULL;
+
return rv;
}
@@ -1753,8 +1846,18 @@ static int conn_ppe_write_frame(ngtcp2_conn *conn, ngtcp2_ppe *ppe,
static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_rtb_entry *ent) {
ngtcp2_rtb *rtb = &pktns->rtb;
+ ngtcp2_cc_pkt cc_pkt;
int rv;
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ conn->cc.on_pkt_sent) {
+ conn->cc.on_pkt_sent(
+ &conn->cc, &conn->cstat,
+ ngtcp2_cc_pkt_init(&cc_pkt, ent->hd.pkt_num, ent->pktlen, pktns->id,
+ ent->ts, /* lost = */ 0,
+ /* tx_in_flight = */ 0, /* is_app_limited = */ 0));
+ }
+
/* This function implements OnPacketSent, but it handles only
non-ACK-only packet. */
rv = ngtcp2_rtb_add(rtb, ent, &conn->cstat);
@@ -1788,13 +1891,13 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
n = n * 2 - 1;
- if (n > 0xffffff) {
+ if (n > 0xFFFFFF) {
return 4;
}
- if (n > 0xffff) {
+ if (n > 0xFFFF) {
return 3;
}
- if (n > 0xff) {
+ if (n > 0xFF) {
return 2;
}
return 1;
@@ -1809,9 +1912,9 @@ static int conn_cwnd_is_zero(ngtcp2_conn *conn) {
uint64_t cwnd = conn->cstat.cwnd;
if (bytes_in_flight >= cwnd) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC,
- "cwnd limited bytes_in_flight=%lu cwnd=%lu",
- bytes_in_flight, cwnd);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_LDC,
+ "cwnd limited bytes_in_flight=%lu cwnd=%lu",
+ bytes_in_flight, cwnd);
}
return bytes_in_flight >= cwnd;
@@ -1902,6 +2005,16 @@ static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used,
return 0;
}
+static int conn_can_send_next_pkt(ngtcp2_conn *conn, size_t left,
+ uint64_t min_payloadlen) {
+ /* TODO the next packet type should be taken into account */
+ return left >=
+ /* TODO Assuming that pkt_num is encoded in 1 byte. */
+ NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen +
+ conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen +
+ NGTCP2_MAX_AEAD_OVERHEAD;
+}
+
/*
* 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
@@ -1948,26 +2061,30 @@ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left,
return 1;
}
}
- } else {
- assert(type == NGTCP2_PKT_HANDSHAKE);
- if (!require_padding) {
- return 0;
- }
+ return !conn_can_send_next_pkt(conn, left, min_payloadlen);
+ }
- if (!conn->pktns.crypto.tx.ckm) {
- return 1;
- }
+ assert(type == NGTCP2_PKT_HANDSHAKE);
- min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN;
+ if (!require_padding) {
+ /* If we have 1RTT key, pad this Handshake packet so that the next
+ 1RTT packet can be squeezed into the same GSO buffer. */
+ return conn->pktns.crypto.tx.ckm &&
+ !conn_can_send_next_pkt(conn, left, NGTCP2_MIN_COALESCED_PAYLOADLEN);
}
- /* TODO the next packet type should be taken into account */
- return left <
- /* TODO Assuming that pkt_num is encoded in 1 byte. */
- NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen +
- conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen +
- NGTCP2_MAX_AEAD_OVERHEAD;
+ if (!conn->pktns.crypto.tx.ckm) {
+ return 1;
+ }
+
+ /* We might send Handshake packet even if exceeding CWND. In that
+ case, we do not write non-probe 1RTT packet. */
+ if (conn_cwnd_is_zero(conn) && conn->pktns.rtb.probe_pkt_left == 0) {
+ return 1;
+ }
+
+ return !conn_can_send_next_pkt(conn, left, NGTCP2_MIN_COALESCED_PAYLOADLEN);
}
static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -2095,6 +2212,215 @@ static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) {
: NGTCP2_PKT_FLAG_NONE));
}
+/*
+ * conn_cut_crypto_frame splits frc->fr.stream by removing
+ * |removed_data| from frc->fr.stream.data[0]. frc->fr.stream.data[0]
+ * must contain |removed_data|, and frc->fr.stream.datacnt >= 1. New
+ * ngtcp2_frame_chain object that contains |removed_data| is created,
+ * and pushed to |crypto_strm| via ngtcp2_strm_streamfrq_push. If
+ * there are data following the removed part of data, new
+ * ngtcp2_frame_chain object is created for it, and frc->next points
+ * to the object.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_cut_crypto_frame(ngtcp2_conn *conn, ngtcp2_frame_chain *frc,
+ ngtcp2_strm *crypto_strm,
+ const ngtcp2_vec *removed_data) {
+ ngtcp2_vec *data = frc->fr.stream.data;
+ size_t datacnt = frc->fr.stream.datacnt;
+ size_t ndatacnt;
+ ngtcp2_frame_chain *right_frc = NULL, *removed_frc;
+ size_t offset;
+ int rv;
+
+ assert(datacnt);
+ assert(data[0].base < removed_data->base);
+ assert(ngtcp2_vec_end(removed_data) <= ngtcp2_vec_end(&data[0]));
+
+ offset = (size_t)(removed_data->base - data->base);
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &removed_frc, 1, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* ngtcp2_frame_chain for the removed data */
+ removed_frc->fr.stream.type = NGTCP2_FRAME_CRYPTO;
+ removed_frc->fr.stream.flags = 0;
+ removed_frc->fr.stream.fin = 0;
+ removed_frc->fr.stream.stream_id = 0;
+ removed_frc->fr.stream.offset = frc->fr.stream.offset + offset;
+ removed_frc->fr.stream.datacnt = 1;
+ removed_frc->fr.stream.data[0] = (ngtcp2_vec){
+ .base = data->base + offset,
+ .len = removed_data->len,
+ };
+
+ rv = ngtcp2_strm_streamfrq_push(crypto_strm, removed_frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(removed_frc, &conn->frc_objalloc,
+ conn->mem);
+ return rv;
+ }
+
+ if (data[0].len == offset + removed_data->len) {
+ ndatacnt = datacnt - 1;
+ } else {
+ ndatacnt = datacnt;
+ }
+
+ if (ndatacnt) {
+ /* ngtcp2_frame_chain after the removed data */
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &right_frc, ndatacnt, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ right_frc->fr.stream.type = NGTCP2_FRAME_CRYPTO;
+ right_frc->fr.stream.offset =
+ removed_frc->fr.stream.offset + removed_frc->fr.stream.data->len;
+ right_frc->fr.stream.datacnt = 0;
+ ngtcp2_vec_split(right_frc->fr.stream.data, &right_frc->fr.stream.datacnt,
+ data, &datacnt, offset + removed_data->len, ndatacnt);
+
+ assert(ndatacnt == right_frc->fr.stream.datacnt);
+ assert(1 == datacnt);
+ }
+
+ frc->fr.stream.datacnt = 1;
+ frc->fr.stream.data[0].len = offset;
+ frc->next = right_frc;
+
+ return 0;
+}
+
+/*
+ * conn_crumble_initial_crypto splits CRYPTO frame (*pfrc)->fr.stream
+ * into pieces and adds PADDING and PING frames, and reorder those
+ * frames. Those frames are encoded in the buffer pointed by |data|
+ * and |offsets|. |data| is the pointer to the array of ngtcp2_vec of
+ * at least NGTCP2_MAX_STREAM_DATACNT. |offsets| contains the CRYPTO
+ * offset of the corresponding ngtcp2_vec in |data|, and it also
+ * should have the capacity at least NGTCP2_MAX_STREAM_DATACNT
+ * uint64_t. |left| is the number of bytes available for the current
+ * packet. |crypto_offset| is the next smallest CRYPTO offset.
+ * |crypto_strm| is the CRYPTO stream.
+ *
+ * This function returns the number of objects written to |data| and
+ * |offsets|, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static ngtcp2_ssize
+conn_crumble_initial_crypto(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
+ ngtcp2_vec *data, uint64_t *offsets,
+ ngtcp2_strm *crypto_strm, size_t left,
+ uint64_t crypto_offset) {
+ ngtcp2_vec server_name;
+ ngtcp2_vec removed_data;
+ size_t max_add_frames = 10;
+ size_t single_crypto_overhead =
+ 1 + ngtcp2_put_uvarintlen(crypto_offset + left - 1) +
+ ngtcp2_put_uvarintlen(left);
+ size_t total_crypto_overhead = single_crypto_overhead * max_add_frames;
+ size_t datacnt;
+ size_t i;
+ int rv;
+
+ if (left <= total_crypto_overhead) {
+ return 0;
+ }
+
+ left -= total_crypto_overhead;
+
+ left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
+ if (left == (size_t)-1) {
+ return 0;
+ }
+
+ rv = ngtcp2_strm_streamfrq_pop(crypto_strm, pfrc, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (*pfrc == NULL) {
+ return 0;
+ }
+
+ assert(crypto_offset == (*pfrc)->fr.stream.offset);
+
+ ngtcp2_vec_copy(data, (*pfrc)->fr.stream.data, (*pfrc)->fr.stream.datacnt);
+ datacnt = (*pfrc)->fr.stream.datacnt;
+
+ offsets[0] = (*pfrc)->fr.stream.offset;
+
+ for (i = 1; i < datacnt; ++i) {
+ offsets[i] = offsets[i - 1] + data[i - 1].len;
+ }
+
+ if (datacnt < NGTCP2_MAX_STREAM_DATACNT &&
+ ngtcp2_pkt_find_server_name(&server_name, data) && server_name.len > 1) {
+ if (ngtcp2_strm_streamfrq_empty(crypto_strm) ||
+ ngtcp2_strm_streamfrq_unacked_offset(crypto_strm) == (uint64_t)-1) {
+ datacnt = ngtcp2_pkt_split_vec_at(
+ data, datacnt, offsets,
+ (size_t)(server_name.base - data[0].base) + server_name.len / 2);
+ } else {
+ /* If we have another data to send (most likely in the another
+ packet), remove the part of SNI from this packet. */
+ datacnt = ngtcp2_pkt_remove_vec_partial(
+ &removed_data, data, datacnt, offsets, &conn->pcg, &server_name);
+
+ rv = conn_cut_crypto_frame(conn, *pfrc, crypto_strm, &removed_data);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(*pfrc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ /* Add the length of removed data to total_crypto_overhead so
+ that we can use them for inter CRYPTO frames padding. */
+ total_crypto_overhead += removed_data.len;
+ }
+ }
+
+ if (datacnt < max_add_frames + 1) {
+ max_add_frames -= datacnt - 1;
+
+ datacnt = ngtcp2_pkt_split_vec_rand(data, datacnt, offsets, &conn->pcg,
+ max_add_frames);
+ }
+
+ for (i = 1; i < datacnt; ++i) {
+ total_crypto_overhead -= 1 + ngtcp2_put_uvarintlen(offsets[i]) +
+ ngtcp2_put_uvarintlen(data[i].len);
+ }
+
+ datacnt = ngtcp2_pkt_append_ping_and_padding(data, datacnt, &conn->pcg,
+ total_crypto_overhead);
+
+ ngtcp2_pkt_permutate_vec(data, datacnt, offsets, &conn->pcg);
+
+ return (ngtcp2_ssize)datacnt;
+}
+
+static size_t conn_dgram_padding(ngtcp2_conn *conn, ngtcp2_ppe *ppe) {
+ if (conn->local.settings.no_tx_udp_payload_size_shaping) {
+ return ngtcp2_ppe_dgram_padding_size(
+ ppe, conn->local.settings.max_tx_udp_payload_size);
+ }
+
+ return ngtcp2_ppe_dgram_padding(ppe);
+}
+
static size_t conn_min_pktlen(ngtcp2_conn *conn);
/*
@@ -2125,8 +2451,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
ngtcp2_pkt_hd hd;
ngtcp2_frame_chain *frq = NULL, **pfrc = &frq;
ngtcp2_frame_chain *nfrc;
- ngtcp2_max_frame mfr;
- ngtcp2_frame *ackfr = NULL, lfr;
+ ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES];
+ ngtcp2_frame lfr;
ngtcp2_ssize spktlen;
ngtcp2_crypto_cc cc;
ngtcp2_rtb_entry *rtbent;
@@ -2201,16 +2527,16 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
return 0;
}
- ackfr = ngtcp2_acktr_create_ack_frame(&pktns->acktr, &mfr.fr, type, ts,
- /* ack_delay = */ 0,
- NGTCP2_DEFAULT_ACK_DELAY_EXPONENT);
- if (ackfr) {
- rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr);
+ lfr.ack.ranges = ack_ranges;
+ if (ngtcp2_acktr_create_ack_frame(&pktns->acktr, &lfr.ack, type, ts,
+ /* ack_delay = */ 0,
+ NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) == 0) {
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
if (rv != 0) {
assert(NGTCP2_ERR_NOBUF == rv);
} else {
ngtcp2_acktr_commit_ack(&pktns->acktr);
- ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, lfr.ack.largest_ack);
pkt_empty = 0;
}
}
@@ -2221,38 +2547,94 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
build_pkt:
for (; !ngtcp2_strm_streamfrq_empty(&pktns->crypto.strm);) {
- left = ngtcp2_ppe_left(&ppe);
-
crypto_offset = ngtcp2_strm_streamfrq_unacked_offset(&pktns->crypto.strm);
if (crypto_offset == (uint64_t)-1) {
ngtcp2_strm_streamfrq_clear(&pktns->crypto.strm);
break;
}
- left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
- if (left == (size_t)-1) {
+ left = ngtcp2_ppe_left(&ppe);
+ if (left == 0) {
break;
}
- rv = ngtcp2_strm_streamfrq_pop(&pktns->crypto.strm, &nfrc, left);
- if (rv != 0) {
- assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
- conn->mem);
- return rv;
- }
+ if (type == NGTCP2_PKT_INITIAL &&
+ (conn->flags & NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO)) {
+ ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
+ uint64_t offsets[NGTCP2_MAX_STREAM_DATACNT];
+ ngtcp2_ssize datacnt;
+ size_t i;
- if (nfrc == NULL) {
- break;
- }
+ datacnt = conn_crumble_initial_crypto(
+ conn, &nfrc, data, offsets, &pktns->crypto.strm, left, crypto_offset);
+ if (datacnt < 0) {
+ assert(ngtcp2_err_is_fatal((int)datacnt));
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+ conn->mem);
- rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr);
- if (rv != 0) {
- ngtcp2_unreachable();
+ return datacnt;
+ }
+
+ if (datacnt == 0) {
+ break;
+ }
+
+ for (i = 0; i < (size_t)datacnt; ++i) {
+ if (data[i].base == NULL) {
+ if (data[i].len == 0) {
+ lfr.ping.type = NGTCP2_FRAME_PING;
+ } else {
+ lfr.padding = (ngtcp2_padding){
+ .type = NGTCP2_FRAME_PADDING,
+ .len = data[i].len,
+ };
+ }
+ } else {
+ lfr.stream = (ngtcp2_stream){
+ .type = NGTCP2_FRAME_CRYPTO,
+ .offset = offsets[i],
+ .datacnt = 1,
+ .data = &data[i],
+ };
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
+ if (rv != 0) {
+ ngtcp2_unreachable();
+ }
+ }
+ } else {
+ left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ rv = ngtcp2_strm_streamfrq_pop(&pktns->crypto.strm, &nfrc, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+ conn->mem);
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ break;
+ }
+
+ rv =
+ conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr);
+ if (rv != 0) {
+ ngtcp2_unreachable();
+ }
}
*pfrc = nfrc;
- pfrc = &(*pfrc)->next;
+
+ for (; nfrc->next;) {
+ nfrc = nfrc->next;
+ }
+
+ pfrc = &nfrc->next;
pkt_empty = 0;
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
@@ -2262,7 +2644,9 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
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, 1);
+ num_reclaimed = ngtcp2_rtb_reclaim_on_pto(
+ &pktns->rtb, conn, pktns,
+ !conn->server && type == NGTCP2_PKT_INITIAL ? 2 : 1);
if (num_reclaimed < 0) {
ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
conn->mem);
@@ -2289,7 +2673,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) &&
pktns->rtb.probe_pkt_left) {
- lfr.type = NGTCP2_FRAME_PING;
+ lfr.ping.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
if (rv != 0) {
@@ -2305,7 +2689,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)) {
if (ngtcp2_tstamp_elapsed(pktns->tx.non_ack_pkt_start_ts,
conn->cstat.smoothed_rtt, ts)) {
- lfr.type = NGTCP2_FRAME_PING;
+ lfr.ping.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
if (rv != 0) {
@@ -2334,12 +2718,12 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
conn, type, ngtcp2_ppe_left(&ppe), write_datalen,
(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0,
require_padding)) {
- lfr.type = NGTCP2_FRAME_PADDING;
- lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe);
+ lfr.padding.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.len = conn_dgram_padding(conn, &ppe);
} else if (pkt_empty) {
return 0;
} else {
- lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.type = NGTCP2_FRAME_PADDING;
lfr.padding.len = ngtcp2_ppe_padding_size(&ppe, conn_min_pktlen(conn));
min_padded = 1;
}
@@ -2402,6 +2786,9 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
conn->tx.pacing.pktlen += (size_t)spktlen;
+ ++conn->cstat.pkt_sent;
+ conn->cstat.bytes_sent += (uint64_t)spktlen;
+
ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
++pktns->tx.last_pkt_num;
@@ -2425,12 +2812,12 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
uint8_t type, ngtcp2_tstamp ts) {
- ngtcp2_frame *ackfr;
ngtcp2_pktns *pktns;
ngtcp2_duration ack_delay;
uint64_t ack_delay_exponent;
ngtcp2_ssize spktlen;
- ngtcp2_max_frame mfr;
+ ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES];
+ ngtcp2_frame fr;
assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
@@ -2459,15 +2846,15 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return 0;
}
- ackfr = ngtcp2_acktr_create_ack_frame(&pktns->acktr, &mfr.fr, type, ts,
- ack_delay, ack_delay_exponent);
- if (!ackfr) {
+ fr.ack.ranges = ack_ranges;
+ if (ngtcp2_acktr_create_ack_frame(&pktns->acktr, &fr.ack, type, ts, ack_delay,
+ ack_delay_exponent) != 0) {
return 0;
}
spktlen = ngtcp2_conn_write_single_frame_pkt(
conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE,
- &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
if (spktlen <= 0) {
return spktlen;
@@ -2540,7 +2927,7 @@ static void conn_discard_early_key(ngtcp2_conn *conn) {
conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx);
conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx);
- memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx));
+ conn->early.hp_ctx = (ngtcp2_crypto_cipher_ctx){0};
ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
conn->early.ckm = NULL;
@@ -2730,21 +3117,16 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
dest += nwrite;
destlen -= (size_t)nwrite;
- /* If initial packet size is at least
- NGTCP2_MAX_UDP_PAYLOAD_SIZE, no extra padding is needed in a
- subsequent packet. */
- if (nwrite < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
- if (conn->server) {
- it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
- if (!ngtcp2_ksl_it_end(&it)) {
- rtbent = ngtcp2_ksl_it_get(&it);
- if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
- wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
- }
+ if (conn->server) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
}
- } else {
- wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
}
+ } else {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
}
}
}
@@ -2862,58 +3244,59 @@ static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) {
static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
size_t i, need = conn_required_num_new_connection_id(conn);
size_t cidlen = conn->oscid.datalen;
- ngtcp2_cid cid;
- uint64_t seq;
int rv;
- uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
ngtcp2_frame_chain *nfrc;
ngtcp2_pktns *pktns = &conn->pktns;
ngtcp2_scid *scid;
ngtcp2_ksl_it it;
for (i = 0; i < need; ++i) {
- rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen);
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
if (rv != 0) {
return rv;
}
- if (cid.datalen != cidlen) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ nfrc->fr.new_connection_id.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ nfrc->fr.new_connection_id.seq = ++conn->scid.last_seq;
+ nfrc->fr.new_connection_id.retire_prior_to = 0;
+
+ rv = conn_call_get_new_connection_id(conn, &nfrc->fr.new_connection_id.cid,
+ &nfrc->fr.new_connection_id.token,
+ cidlen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ if (nfrc->fr.new_connection_id.cid.datalen != cidlen) {
+ rv = NGTCP2_ERR_CALLBACK_FAILURE;
+ goto fail;
}
/* Assert uniqueness */
- it = ngtcp2_ksl_lower_bound(&conn->scid.set, &cid);
+ it =
+ ngtcp2_ksl_lower_bound(&conn->scid.set, &nfrc->fr.new_connection_id.cid);
if (!ngtcp2_ksl_it_end(&it) &&
- ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it), &cid)) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it),
+ &nfrc->fr.new_connection_id.cid)) {
+ rv = NGTCP2_ERR_CALLBACK_FAILURE;
+ goto fail;
}
- seq = ++conn->scid.last_seq;
-
scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid));
if (scid == NULL) {
- return NGTCP2_ERR_NOMEM;
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail;
}
- ngtcp2_scid_init(scid, seq, &cid);
+ ngtcp2_scid_init(scid, nfrc->fr.new_connection_id.seq,
+ &nfrc->fr.new_connection_id.cid);
rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid);
if (rv != 0) {
ngtcp2_mem_free(conn->mem, scid);
- return rv;
- }
-
- rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
- if (rv != 0) {
- return rv;
+ goto fail;
}
- nfrc->fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
- nfrc->fr.new_connection_id.seq = seq;
- nfrc->fr.new_connection_id.retire_prior_to = 0;
- nfrc->fr.new_connection_id.cid = cid;
- memcpy(nfrc->fr.new_connection_id.stateless_reset_token, token,
- sizeof(token));
nfrc->next = pktns->tx.frq;
pktns->tx.frq = nfrc;
@@ -2923,6 +3306,11 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
}
return 0;
+
+fail:
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+
+ return rv;
}
static int dcidtr_on_deactivate(const ngtcp2_dcid *dcid, void *user_data) {
@@ -3082,14 +3470,15 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
ngtcp2_crypto_cc *cc = &conn->pkt.cc;
ngtcp2_ppe *ppe = &conn->pkt.ppe;
ngtcp2_pkt_hd *hd = &conn->pkt.hd;
- ngtcp2_max_frame mfr;
- ngtcp2_frame *ackfr = NULL, lfr;
+ ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES];
+ ngtcp2_frame lfr;
ngtcp2_ssize nwrite;
ngtcp2_frame_chain **pfrc, *nfrc, *frc;
ngtcp2_rtb_entry *ent;
ngtcp2_strm *strm;
int pkt_empty = 1;
uint64_t ndatalen = 0;
+ uint64_t wdatalen;
int send_stream = 0;
int stream_blocked = 0;
int send_datagram = 0;
@@ -3108,11 +3497,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
size_t min_pktlen = conn_min_pktlen(conn);
int min_padded = 0;
int padded = 0;
- ngtcp2_cc_pkt cc_pkt;
uint64_t crypto_offset;
uint64_t stream_offset;
ngtcp2_ssize num_reclaimed;
- int fin;
uint64_t target_max_data;
ngtcp2_conn_stat *cstat = &conn->cstat;
uint64_t delta;
@@ -3225,8 +3612,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->tx.last_max_data_ts = ts;
- nfrc->fr.type = NGTCP2_FRAME_MAX_DATA;
- nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset + delta;
+ nfrc->fr.max_data = (ngtcp2_max_data){
+ .type = NGTCP2_FRAME_MAX_DATA,
+ .max_data = conn->rx.unsent_max_offset + delta,
+ };
nfrc->next = pktns->tx.frq;
pktns->tx.frq = nfrc;
@@ -3261,9 +3650,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
/* PATH_RESPONSE is bound to the path that the corresponding
PATH_CHALLENGE is received. */
if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
- lfr.type = NGTCP2_FRAME_PATH_RESPONSE;
- memcpy(lfr.path_response.data, pcent->data,
- sizeof(lfr.path_response.data));
+ lfr.path_response = (ngtcp2_path_response){
+ .type = NGTCP2_FRAME_PATH_RESPONSE,
+ .data = pcent->data,
+ };
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
if (rv != 0) {
@@ -3282,7 +3672,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
An endpoint that receives a PATH_CHALLENGE on an active
path SHOULD send a non-probing packet in response. */
- lfr.type = NGTCP2_FRAME_PING;
+ lfr.ping.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
if (rv != 0) {
assert(NGTCP2_ERR_NOBUF == rv);
@@ -3291,20 +3681,19 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
}
- ackfr = ngtcp2_acktr_create_ack_frame(
- &pktns->acktr, &mfr.fr, type, ts, conn_compute_ack_delay(conn),
- conn->local.transport_params.ack_delay_exponent);
- if (ackfr) {
- rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr);
+ lfr.ack.ranges = ack_ranges;
+ if (ngtcp2_acktr_create_ack_frame(
+ &pktns->acktr, &lfr.ack, type, ts, conn_compute_ack_delay(conn),
+ conn->local.transport_params.ack_delay_exponent) == 0) {
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
if (rv != 0) {
assert(NGTCP2_ERR_NOBUF == rv);
} else {
ngtcp2_acktr_commit_ack(&pktns->acktr);
- ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num,
- ackfr->ack.largest_ack);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, lfr.ack.largest_ack);
assert(NGTCP2_PKT_1RTT == type);
- conn_handle_unconfirmed_key_update_from_remote(
- conn, ackfr->ack.largest_ack, ts);
+ conn_handle_unconfirmed_key_update_from_remote(conn,
+ lfr.ack.largest_ack, ts);
pkt_empty = 0;
}
}
@@ -3319,7 +3708,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
continue;
}
- switch ((*pfrc)->fr.type) {
+ switch ((*pfrc)->fr.hd.type) {
case NGTCP2_FRAME_RESET_STREAM:
strm =
ngtcp2_conn_find_stream(conn, (*pfrc)->fr.reset_stream.stream_id);
@@ -3469,11 +3858,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_RESET_STREAM;
- nfrc->fr.reset_stream.stream_id = strm->stream_id;
- nfrc->fr.reset_stream.app_error_code =
- strm->tx.reset_stream_app_error_code;
- nfrc->fr.reset_stream.final_size = strm->tx.offset;
+ nfrc->fr.reset_stream = (ngtcp2_reset_stream){
+ .type = NGTCP2_FRAME_RESET_STREAM,
+ .stream_id = strm->stream_id,
+ .app_error_code = strm->tx.reset_stream_app_error_code,
+ .final_size = strm->tx.offset,
+ };
*pfrc = nfrc;
strm->flags &= ~NGTCP2_STRM_FLAG_SEND_RESET_STREAM;
@@ -3512,10 +3902,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_STOP_SENDING;
- nfrc->fr.stop_sending.stream_id = strm->stream_id;
- nfrc->fr.stop_sending.app_error_code =
- strm->tx.stop_sending_app_error_code;
+ nfrc->fr.stop_sending = (ngtcp2_stop_sending){
+ .type = NGTCP2_FRAME_STOP_SENDING,
+ .stream_id = strm->stream_id,
+ .app_error_code = strm->tx.stop_sending_app_error_code,
+ };
*pfrc = nfrc;
strm->flags &= ~NGTCP2_STRM_FLAG_SEND_STOP_SENDING;
@@ -3543,9 +3934,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
- nfrc->fr.stream_data_blocked.stream_id = strm->stream_id;
- nfrc->fr.stream_data_blocked.offset = strm->tx.max_offset;
+ nfrc->fr.stream_data_blocked = (ngtcp2_stream_data_blocked){
+ .type = NGTCP2_FRAME_STREAM_DATA_BLOCKED,
+ .stream_id = strm->stream_id,
+ .offset = strm->tx.max_offset,
+ };
*pfrc = nfrc;
strm->tx.last_blocked_offset = strm->tx.max_offset;
@@ -3597,10 +3990,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
strm->tx.last_max_stream_data_ts = ts;
- nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
- nfrc->fr.max_stream_data.stream_id = strm->stream_id;
- nfrc->fr.max_stream_data.max_stream_data =
- strm->rx.unsent_max_offset + delta;
+ nfrc->fr.max_stream_data = (ngtcp2_max_stream_data){
+ .type = NGTCP2_FRAME_MAX_STREAM_DATA,
+ .stream_id = strm->stream_id,
+ .max_stream_data = strm->rx.unsent_max_offset + delta,
+ };
*pfrc = nfrc;
strm->rx.max_offset = strm->rx.unsent_max_offset =
@@ -3696,8 +4090,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
assert(ngtcp2_err_is_fatal(rv));
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI;
- nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams;
+ nfrc->fr.max_streams = (ngtcp2_max_streams){
+ .type = NGTCP2_FRAME_MAX_STREAMS_BIDI,
+ .max_streams = conn->remote.bidi.unsent_max_streams,
+ };
*pfrc = nfrc;
conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams;
@@ -3728,8 +4124,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
assert(ngtcp2_err_is_fatal(rv));
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI;
- nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams;
+ nfrc->fr.max_streams = (ngtcp2_max_streams){
+ .type = NGTCP2_FRAME_MAX_STREAMS_UNI,
+ .max_streams = conn->remote.uni.unsent_max_streams,
+ };
*pfrc = nfrc;
conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams;
@@ -3780,10 +4178,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
left = ngtcp2_ppe_left(ppe);
if (*pfrc == NULL && send_stream && ngtcp2_pq_empty(&conn->tx.strmq) &&
- (ndatalen = ngtcp2_pkt_stream_max_datalen(
+ (wdatalen = ngtcp2_pkt_stream_max_datalen(
vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen,
left)) != (size_t)-1 &&
- (ndatalen || datalen == 0)) {
+ (wdatalen == ndatalen || wdatalen >= NGTCP2_MIN_STREAM_DATALEN) &&
+ (wdatalen || datalen == 0)) {
+ ndatalen = wdatalen;
datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT,
vmsg->stream.data, vmsg->stream.datacnt,
(size_t)ndatalen);
@@ -3800,14 +4200,14 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
nfrc->fr.stream.type = NGTCP2_FRAME_STREAM;
nfrc->fr.stream.flags = 0;
+ nfrc->fr.stream.fin = 0;
nfrc->fr.stream.stream_id = vmsg->stream.strm->stream_id;
nfrc->fr.stream.offset = vmsg->stream.strm->tx.offset;
nfrc->fr.stream.datacnt = datacnt;
ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt);
- fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
- ndatalen == datalen;
- nfrc->fr.stream.fin = (uint8_t)fin;
+ nfrc->fr.stream.fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
+ ndatalen == datalen;
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
if (rv != 0) {
@@ -3826,7 +4226,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->tx.offset += ndatalen;
vmsg->stream.strm->flags |= NGTCP2_STRM_FLAG_ANY_SENT;
- if (fin) {
+ if (nfrc->fr.stream.fin) {
ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR);
}
@@ -3849,8 +4249,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_DATA_BLOCKED;
- nfrc->fr.data_blocked.offset = conn->tx.offset;
+ nfrc->fr.data_blocked = (ngtcp2_data_blocked){
+ .type = NGTCP2_FRAME_DATA_BLOCKED,
+ .offset = conn->tx.offset,
+ };
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
if (rv != 0) {
@@ -3881,9 +4283,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
- nfrc->fr.stream_data_blocked.stream_id = strm->stream_id;
- nfrc->fr.stream_data_blocked.offset = strm->tx.max_offset;
+ nfrc->fr.stream_data_blocked = (ngtcp2_stream_data_blocked){
+ .type = NGTCP2_FRAME_STREAM_DATA_BLOCKED,
+ .stream_id = strm->stream_id,
+ .offset = strm->tx.max_offset,
+ };
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
if (rv != 0) {
@@ -4026,7 +4430,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
if (ngtcp2_tstamp_elapsed(pktns->tx.non_ack_pkt_start_ts,
cstat->smoothed_rtt, ts) ||
keep_alive_expired || conn->pktns.rtb.probe_pkt_left) {
- lfr.type = NGTCP2_FRAME_PING;
+ lfr.ping.type = NGTCP2_FRAME_PING;
rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
if (rv != 0) {
@@ -4041,7 +4445,6 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING;
}
pktns->tx.non_ack_pkt_start_ts = UINT64_MAX;
- pkt_empty = 0;
}
} else if (pktns->tx.non_ack_pkt_start_ts == UINT64_MAX) {
pktns->tx.non_ack_pkt_start_ts = ts;
@@ -4056,7 +4459,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
lfr.padding.len = ngtcp2_ppe_padding_size(ppe, destlen);
} else if (require_padding) {
- lfr.padding.len = ngtcp2_ppe_dgram_padding(ppe);
+ lfr.padding.len = conn_dgram_padding(conn, ppe);
} else {
lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen);
min_padded = 1;
@@ -4067,7 +4470,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
padded = 1;
}
- lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.type = NGTCP2_FRAME_PADDING;
ngtcp2_log_tx_fr(&conn->log, hd, &lfr);
ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
}
@@ -4103,12 +4506,6 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
*pfrc = NULL;
}
- if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
- pktns->rtb.num_ack_eliciting == 0 && conn->cc.event) {
- conn->cc.event(&conn->cc, &conn->cstat, NGTCP2_CC_EVENT_TYPE_TX_START,
- ts);
- }
-
rv = conn_on_pkt_sent(conn, pktns, ent);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
@@ -4117,18 +4514,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
return rv;
}
- if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
- if (conn->cc.on_pkt_sent) {
- conn->cc.on_pkt_sent(
- &conn->cc, &conn->cstat,
- ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite,
- NGTCP2_PKTNS_ID_APPLICATION, ts, ent->rst.lost,
- ent->rst.tx_in_flight, ent->rst.is_app_limited));
- }
-
- if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
- conn_restart_timer_on_write(conn, ts);
- }
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) {
+ conn_restart_timer_on_write(conn, ts);
}
} else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts);
@@ -4140,8 +4528,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
--pktns->rtb.probe_pkt_left;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
- nwrite);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
+ nwrite);
}
conn_update_keep_alive_last_ts(conn, ts);
@@ -4150,6 +4538,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
conn->tx.pacing.pktlen += (size_t)nwrite;
+ ++conn->cstat.pkt_sent;
+ conn->cstat.bytes_sent += (uint64_t)nwrite;
+
ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
++pktns->tx.last_pkt_num;
@@ -4248,17 +4639,17 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
return 0;
}
- lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.type = NGTCP2_FRAME_PADDING;
if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL) {
lfr.padding.len = ngtcp2_ppe_dgram_padding_size(&ppe, destlen);
} else if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) {
- lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe);
+ lfr.padding.len = conn_dgram_padding(conn, &ppe);
} else {
- switch (fr->type) {
+ switch (fr->hd.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_dgram_padding(&ppe);
+ lfr.padding.len = conn_dgram_padding(conn, &ppe);
} else {
lfr.padding.len = 0;
}
@@ -4285,7 +4676,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite);
/* Do this when we are sure that there is no error. */
- switch (fr->type) {
+ switch (fr->hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
ngtcp2_acktr_commit_ack(&pktns->acktr);
@@ -4300,6 +4691,13 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
padded = 0;
}
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ /* Clear padded so that we never store the terminal packet in
+ ngtcp2_rtb. */
+ padded = 0;
+
break;
}
@@ -4333,8 +4731,8 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
--pktns->rtb.probe_pkt_left;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
- nwrite);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
+ nwrite);
}
}
} else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
@@ -4346,7 +4744,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
}
if (!padded) {
- switch (fr->type) {
+ switch (fr->hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
break;
@@ -4357,6 +4755,9 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
conn->tx.pacing.pktlen += (size_t)nwrite;
}
+ ++conn->cstat.pkt_sent;
+ conn->cstat.bytes_sent += (uint64_t)nwrite;
+
ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
++pktns->tx.last_pkt_num;
@@ -4423,8 +4824,10 @@ static int conn_enqueue_retire_connection_id(ngtcp2_conn *conn, uint64_t seq) {
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
- nfrc->fr.retire_connection_id.seq = seq;
+ nfrc->fr.retire_connection_id = (ngtcp2_retire_connection_id){
+ .type = NGTCP2_FRAME_RETIRE_CONNECTION_ID,
+ .seq = seq,
+ };
nfrc->next = pktns->tx.frq;
pktns->tx.frq = nfrc;
@@ -4563,10 +4966,10 @@ static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn,
return 0;
}
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "sending PMTUD probe packet len=%zu", probelen);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "sending PMTUD probe packet len=%zu", probelen);
- lfr.type = NGTCP2_FRAME_PING;
+ lfr.ping.type = NGTCP2_FRAME_PING;
nwrite = ngtcp2_conn_write_single_frame_pkt(
conn, pi, dest, probelen, NGTCP2_PKT_1RTT,
@@ -4764,12 +5167,12 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
return 0;
}
- rv = conn_call_get_path_challenge_data(conn, lfr.path_challenge.data);
+ rv = conn_call_get_path_challenge_data(conn, &lfr.path_challenge.data);
if (rv != 0) {
return rv;
}
- lfr.type = NGTCP2_FRAME_PATH_CHALLENGE;
+ lfr.path_challenge.type = NGTCP2_FRAME_PATH_CHALLENGE;
initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
timeout = conn_compute_pto(conn, &conn->pktns);
@@ -4796,7 +5199,7 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
flags = NGTCP2_PV_ENTRY_FLAG_NONE;
}
- ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts);
+ 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_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
@@ -4902,8 +5305,10 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
}
}
- lfr.type = NGTCP2_FRAME_PATH_RESPONSE;
- memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data));
+ lfr.path_response = (ngtcp2_path_response){
+ .type = NGTCP2_FRAME_PATH_RESPONSE,
+ .data = pcent->data,
+ };
nwrite = ngtcp2_conn_write_single_frame_pkt(
conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
@@ -5060,11 +5465,11 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
ngtcp2_pktns *in_pktns = conn->in_pktns;
ngtcp2_rtb *rtb = &conn->pktns.rtb;
ngtcp2_rtb *in_rtb;
- uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1];
+ char cidbuf[sizeof(retry.odcid.data) * 2 + 1];
uint8_t *token;
- if (!in_pktns || conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) {
- return 0;
+ if (!in_pktns || (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) {
+ return NGTCP2_ERR_DISCARD_PKT;
}
in_rtb = &in_pktns->rtb;
@@ -5085,9 +5490,9 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
return rv;
}
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s",
- (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data,
- retry.odcid.datalen));
+ ngtcp2_log_infof(
+ &conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s",
+ ngtcp2_encode_hex_cstr(cidbuf, retry.odcid.data, retry.odcid.datalen));
if (retry.tokenlen == 0) {
return NGTCP2_ERR_PROTO;
@@ -5181,14 +5586,10 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
num_acked =
ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns, pkt_ts, ts);
- if (num_acked < 0) {
+ if (num_acked <= 0) {
return (int)num_acked;
}
- if (num_acked == 0) {
- return 0;
- }
-
pktns->rtb.probe_pkt_left = 0;
if (cstat->pto_count &&
@@ -5235,9 +5636,12 @@ static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr,
* Stream ID exceeds allowed limit.
* NGTCP2_ERR_NOMEM
* Out of memory.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*/
static int conn_recv_max_stream_data(ngtcp2_conn *conn,
- const ngtcp2_max_stream_data *fr) {
+ const ngtcp2_max_stream_data *fr,
+ ngtcp2_tstamp ts) {
ngtcp2_strm *strm;
ngtcp2_idtr *idtr;
int local_stream = conn_local_stream(conn, fr->stream_id);
@@ -5267,6 +5671,10 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn,
if (strm == NULL) {
if (local_stream) {
/* Stream has been closed. */
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -5277,6 +5685,10 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn,
}
assert(rv == NGTCP2_ERR_STREAM_IN_USE);
/* Stream has been closed. */
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -5296,19 +5708,29 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn,
}
}
- if (strm->tx.max_offset < fr->max_stream_data) {
- strm->tx.max_offset = fr->max_stream_data;
-
- /* Don't call callback if stream is half-closed local */
- if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
- return 0;
+ if (strm->tx.max_offset >= fr->max_stream_data) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
}
- rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id,
- fr->max_stream_data);
- if (rv != 0) {
- return rv;
+ return 0;
+ }
+
+ strm->tx.max_offset = fr->max_stream_data;
+
+ /* Don't call callback if stream is half-closed local */
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
}
+
+ return 0;
+ }
+
+ rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id,
+ fr->max_stream_data);
+ if (rv != 0) {
+ return rv;
}
return 0;
@@ -5503,9 +5925,9 @@ decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp,
}
if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
- dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f));
+ dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0F));
} else {
- dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f));
+ dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1F));
if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) {
hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE;
}
@@ -5620,7 +6042,7 @@ static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path,
}
ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb);
- ngtcp2_path_challenge_entry_init(ent, path, fr->data);
+ ngtcp2_path_challenge_entry_init(ent, path, &fr->data);
}
/*
@@ -5690,7 +6112,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
return 0;
}
- rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data);
+ rv = ngtcp2_pv_validate(pv, &ent_flags, &fr->data);
if (rv != 0) {
assert(!ngtcp2_err_is_fatal(rv));
@@ -5786,7 +6208,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
conn->pv = npv;
- return 0;
+ return conn_call_begin_path_validation(conn, conn->pv);
}
/*
@@ -5941,7 +6363,8 @@ static int conn_verify_fixed_bit(ngtcp2_conn *conn, ngtcp2_pkt_hd *hd) {
static int conn_recv_crypto(ngtcp2_conn *conn,
ngtcp2_encryption_level encryption_level,
- ngtcp2_strm *strm, const ngtcp2_stream *fr);
+ ngtcp2_strm *strm, const ngtcp2_stream *fr,
+ ngtcp2_tstamp ts);
static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
const ngtcp2_pkt_info *pi, const uint8_t *pkt,
@@ -5980,6 +6403,8 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
* TLS stack reported error.
* NGTCP2_ERR_PROTO
* Generic QUIC protocol error.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*
* In addition to the above error codes, error codes returned from
* conn_recv_pkt are also returned.
@@ -5991,8 +6416,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_tstamp ts) {
ngtcp2_ssize nread;
ngtcp2_pkt_hd hd;
- ngtcp2_max_frame mfr;
- ngtcp2_frame *fr = &mfr.fr;
+ ngtcp2_frame_decoder frd;
+ ngtcp2_frame fr;
int rv;
int require_ack = 0;
size_t hdpktlen;
@@ -6023,8 +6448,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return 0;
}
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "buffering 1RTT packet len=%zu", pktlen);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 1RTT packet len=%zu", pktlen);
rv =
conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen, ts);
@@ -6171,8 +6596,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
/* Buffer re-ordered 0-RTT packet. */
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "buffering 0-RTT packet len=%zu", pktlen);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 0-RTT packet len=%zu", pktlen);
rv = conn_buffer_pkt(conn, conn->in_pktns, path, pi, pkt, pktlen, dgramlen,
ts);
@@ -6194,10 +6619,10 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
if (conn->server) {
if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
- ngtcp2_log_info(
+ ngtcp2_log_infof(
&conn->log, NGTCP2_LOG_EVENT_PKT,
"Initial packet was ignored because it is included in UDP datagram "
- "less than %zu bytes: %zu bytes",
+ "less than %d bytes: %zu bytes",
NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen);
return NGTCP2_ERR_DISCARD_PKT;
}
@@ -6277,8 +6702,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
"Handshake packet at this point is unexpected and discarded");
return (ngtcp2_ssize)pktlen;
}
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "buffering Handshake packet len=%zu", pktlen);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering Handshake packet len=%zu", pktlen);
rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pi, pkt, pktlen,
dgramlen, ts);
@@ -6332,8 +6757,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->acktr.max_pkt_num, hd.pkt_num,
hd.pkt_numlen);
if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
return NGTCP2_ERR_DISCARD_PKT;
}
@@ -6380,9 +6805,9 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
!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);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
}
payload = conn->crypto.decrypt_buf.base;
@@ -6438,7 +6863,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_qlog_pkt_received_start(&conn->qlog);
for (; payloadlen;) {
- nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ nread = ngtcp2_frame_decoder_decode(&frd, &fr, payload, payloadlen);
if (nread < 0) {
return nread;
}
@@ -6446,14 +6871,14 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
payload += nread;
payloadlen -= (size_t)nread;
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
- fr->ack.ack_delay = 0;
- fr->ack.ack_delay_unscaled = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.ack_delay_unscaled = 0;
rv =
- ngtcp2_pkt_validate_ack(&fr->ack, conn->local.settings.initial_pkt_num);
+ ngtcp2_pkt_validate_ack(&fr.ack, conn->local.settings.initial_pkt_num);
if (rv != 0) {
return rv;
}
@@ -6461,9 +6886,9 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
}
- ngtcp2_log_rx_fr(&conn->log, &hd, fr);
+ ngtcp2_log_rx_fr(&conn->log, &hd, &fr);
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
if (num_ack_processed >= NGTCP2_MAX_ACK_PER_PKT) {
@@ -6472,7 +6897,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) {
conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
}
- rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ rv = conn_recv_ack(conn, pktns, &fr.ack, pkt_ts, ts);
if (rv != 0) {
return rv;
}
@@ -6482,34 +6907,35 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
case NGTCP2_FRAME_CRYPTO:
if (!conn->server && !conn->negotiated_version &&
- ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt)) {
+ ngtcp2_vec_len(fr.stream.data, fr.stream.datacnt)) {
conn->negotiated_version = hd.version;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "the negotiated version is 0x%08x",
- conn->negotiated_version);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
}
- rv = conn_recv_crypto(conn, encryption_level, crypto, &fr->stream);
+ rv = conn_recv_crypto(conn, encryption_level, crypto, &fr.stream, ts);
if (rv != 0) {
return rv;
}
require_ack = 1;
break;
case NGTCP2_FRAME_CONNECTION_CLOSE:
- rv = conn_recv_connection_close(conn, &fr->connection_close);
+ rv = conn_recv_connection_close(conn, &fr.connection_close);
if (rv != 0) {
return rv;
}
break;
case NGTCP2_FRAME_PING:
+ ++conn->cstat.ping_recv;
require_ack = 1;
break;
default:
return NGTCP2_ERR_PROTO;
}
- ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &fr);
}
if (hd.type == NGTCP2_PKT_HANDSHAKE) {
@@ -6543,6 +6969,7 @@ static int is_unrecoverable_error(int liberr) {
case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
case NGTCP2_ERR_TRANSPORT_PARAM:
case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ case NGTCP2_ERR_INTERNAL:
return 1;
}
@@ -6568,6 +6995,10 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
const uint8_t *origpkt = pkt;
uint32_t version;
+ if (pktlen == 0) {
+ return 0;
+ }
+
if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
conn->dcid.current.bytes_recv += dgramlen;
}
@@ -6595,6 +7026,8 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
return nread;
}
+ ++conn->cstat.pkt_discarded;
+
/* 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
@@ -6612,11 +7045,15 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
unrecoverable, therefore drop connection. */
return nread;
}
+
+ ++conn->cstat.pkt_discarded;
+
return (ngtcp2_ssize)dgramlen;
}
}
if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ ++conn->cstat.pkt_discarded;
return (ngtcp2_ssize)dgramlen;
}
@@ -6632,8 +7069,11 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
pkt += nread;
pktlen -= (size_t)nread;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "read packet %td left %zu", nread, pktlen);
+ ++conn->cstat.pkt_recv;
+ conn->cstat.bytes_recv += (uint64_t)nread;
+
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "read packet %td left %zu", nread, pktlen);
}
return (ngtcp2_ssize)dgramlen;
@@ -6778,15 +7218,24 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
* The end offset exceeds the maximum value.
* NGTCP2_ERR_CALLBACK_FAILURE
* User-defined callback function failed.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*/
static int conn_recv_crypto(ngtcp2_conn *conn,
ngtcp2_encryption_level encryption_level,
- ngtcp2_strm *crypto, const ngtcp2_stream *fr) {
+ ngtcp2_strm *crypto, const ngtcp2_stream *fr,
+ ngtcp2_tstamp ts) {
uint64_t fr_end_offset;
uint64_t rx_offset;
int rv;
+ ngtcp2_ssize nwrite;
if (fr->datacnt == 0) {
+ if (encryption_level != NGTCP2_ENCRYPTION_LEVEL_INITIAL &&
+ ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -6799,6 +7248,11 @@ static int conn_recv_crypto(ngtcp2_conn *conn,
rx_offset = ngtcp2_strm_rx_offset(crypto);
if (fr_end_offset <= rx_offset) {
+ if (encryption_level != NGTCP2_ENCRYPTION_LEVEL_INITIAL &&
+ ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
if (conn->server &&
!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT) &&
encryption_level == NGTCP2_ENCRYPTION_LEVEL_INITIAL) {
@@ -6856,8 +7310,18 @@ static int conn_recv_crypto(ngtcp2_conn *conn,
return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED;
}
- return ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len,
- fr->offset);
+ nwrite = ngtcp2_strm_recv_reordering(crypto, fr->data[0].base,
+ fr->data[0].len, fr->offset);
+ if (nwrite < 0) {
+ return (int)nwrite;
+ }
+
+ if (encryption_level != NGTCP2_ENCRYPTION_LEVEL_INITIAL && nwrite == 0 &&
+ ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
+ return 0;
}
/*
@@ -6891,8 +7355,11 @@ static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) {
* NGTCP2_ERR_FINAL_SIZE
* STREAM frame has strictly larger end offset than it is
* permitted.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*/
-static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
+static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr,
+ ngtcp2_tstamp ts) {
int rv;
ngtcp2_strm *strm;
ngtcp2_idtr *idtr;
@@ -6901,6 +7368,8 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
int bidi;
uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE;
+ ngtcp2_ssize nwrite;
+ int new_strm = 0;
local_stream = conn_local_stream(conn, fr->stream_id);
bidi = bidi_stream(fr->stream_id);
@@ -6934,8 +7403,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
if (strm == NULL) {
if (local_stream) {
- /* TODO The stream has been closed. This should be responded
- with RESET_STREAM, or simply ignored. */
+ /* The stream has been closed. */
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -6945,8 +7417,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
return rv;
}
assert(rv == NGTCP2_ERR_STREAM_IN_USE);
- /* TODO The stream has been closed. This should be responded
- with RESET_STREAM, or simply ignored. */
+ /* The stream has been closed. */
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -6961,6 +7436,8 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
return rv;
}
+ new_strm = 1;
+
if (!bidi) {
ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR);
strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED;
@@ -7001,10 +7478,18 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
}
if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
if (rx_offset == fr_end_offset) {
+ if (!new_strm && ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
} else if (strm->rx.last_offset > fr_end_offset) {
@@ -7024,10 +7509,18 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
ngtcp2_max_uint64(strm->rx.last_offset, fr_end_offset);
if (fr_end_offset <= rx_offset) {
+ if (!new_strm && ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
}
@@ -7056,30 +7549,34 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
rx_offset == strm->rx.last_offset;
- if (fin || datalen) {
- if (fin) {
- sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
- }
- if (!conn_is_tls_handshake_completed(conn)) {
- sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT;
- }
- rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
- (size_t)datalen);
- if (rv != 0) {
- return rv;
- }
+ assert(fin || datalen);
- rv = conn_emit_pending_stream_data(conn, strm, rx_offset);
- if (rv != 0) {
- return rv;
- }
+ if (fin) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
}
- } else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) {
- rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len,
- fr->offset);
+ if (!conn_is_tls_handshake_completed(conn)) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT;
+ }
+ rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
+ (size_t)datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_emit_pending_stream_data(conn, strm, rx_offset);
if (rv != 0) {
return rv;
}
+ } else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) {
+ nwrite = ngtcp2_strm_recv_reordering(strm, fr->data[0].base,
+ fr->data[0].len, fr->offset);
+ if (nwrite < 0) {
+ return (int)nwrite;
+ }
+
+ if (nwrite == 0 && ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
}
return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
}
@@ -7175,9 +7672,12 @@ handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams,
* NGTCP2_MAX_VARINT.
* NGTCP2_ERR_FINAL_SIZE
* The final offset is strictly larger than it is permitted.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*/
static int conn_recv_reset_stream(ngtcp2_conn *conn,
- const ngtcp2_reset_stream *fr) {
+ const ngtcp2_reset_stream *fr,
+ ngtcp2_tstamp ts) {
ngtcp2_strm *strm;
int local_stream = conn_local_stream(conn, fr->stream_id);
int bidi = bidi_stream(fr->stream_id);
@@ -7215,6 +7715,10 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn,
strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
if (strm == NULL) {
if (local_stream) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -7224,6 +7728,11 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn,
return rv;
}
assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -7258,6 +7767,10 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn,
}
if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -7313,9 +7826,12 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn,
* Out of memory.
* NGTCP2_ERR_CALLBACK_FAILURE
* User-defined callback function failed.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*/
static int conn_recv_stop_sending(ngtcp2_conn *conn,
- const ngtcp2_stop_sending *fr) {
+ const ngtcp2_stop_sending *fr,
+ ngtcp2_tstamp ts) {
int rv;
ngtcp2_strm *strm;
ngtcp2_idtr *idtr;
@@ -7344,6 +7860,10 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
if (strm == NULL) {
if (local_stream) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
rv = ngtcp2_idtr_open(idtr, fr->stream_id);
@@ -7352,6 +7872,11 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
return rv;
}
assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -7374,6 +7899,10 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
}
if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING_RECVED) {
+ if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
return 0;
}
@@ -7405,9 +7934,8 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
*/
static int check_stateless_reset(const ngtcp2_dcid *dcid,
const ngtcp2_path *path,
- const ngtcp2_pkt_stateless_reset *sr) {
- return ngtcp2_dcid_verify_stateless_reset_token(
- dcid, path, sr->stateless_reset_token) == 0;
+ const ngtcp2_pkt_stateless_reset2 *sr) {
+ return ngtcp2_dcid_verify_stateless_reset_token(dcid, path, &sr->token) == 0;
}
/*
@@ -7431,7 +7959,7 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
const uint8_t *payload, size_t payloadlen) {
int rv;
ngtcp2_pv *pv = conn->pv;
- ngtcp2_pkt_stateless_reset sr;
+ ngtcp2_pkt_stateless_reset2 sr;
rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen);
if (rv != 0) {
@@ -7442,8 +7970,7 @@ 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_PRESENT) ||
!check_stateless_reset(&pv->fallback_dcid, path, &sr))))) {
- rv = ngtcp2_dcidtr_verify_stateless_reset(&conn->dcid.dtr, path,
- sr.stateless_reset_token);
+ rv = ngtcp2_dcidtr_verify_stateless_reset(&conn->dcid.dtr, path, &sr.token);
if (rv != 0) {
return rv;
}
@@ -7523,7 +8050,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
}
rv = ngtcp2_dcid_verify_uniqueness(&conn->dcid.current, fr->seq, &fr->cid,
- fr->stateless_reset_token);
+ &fr->token);
if (rv != 0) {
return rv;
}
@@ -7532,8 +8059,8 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
}
if (pv) {
- rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid,
- fr->stateless_reset_token);
+ rv =
+ ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid, &fr->token);
if (rv != 0) {
return rv;
}
@@ -7542,8 +8069,8 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
}
}
- rv = ngtcp2_dcidtr_verify_token_uniqueness(
- &conn->dcid.dtr, &found, fr->seq, &fr->cid, fr->stateless_reset_token);
+ rv = ngtcp2_dcidtr_verify_token_uniqueness(&conn->dcid.dtr, &found, fr->seq,
+ &fr->cid, &fr->token);
if (rv != 0) {
return rv;
}
@@ -7621,8 +8148,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
return 0;
}
- ngtcp2_dcidtr_push_unused(&conn->dcid.dtr, fr->seq, &fr->cid,
- fr->stateless_reset_token);
+ ngtcp2_dcidtr_push_unused(&conn->dcid.dtr, fr->seq, &fr->cid, &fr->token);
return 0;
}
@@ -8030,7 +8556,12 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) {
conn->pv = pv;
- return conn_call_activate_dcid(conn, &pv->dcid);
+ rv = conn_call_activate_dcid(conn, &pv->dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_begin_path_validation(conn, conn->pv);
}
/*
@@ -8258,7 +8789,6 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
ngtcp2_pv *pv = NULL;
int rv;
ngtcp2_duration pto;
- int require_new_cid;
int local_addr_eq;
int pref_addr_migration;
uint32_t remote_addr_cmp;
@@ -8316,9 +8846,6 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
* continue to use the current connection ID with the new remote
* address while still sending from the same local address.
*/
- require_new_cid = conn->dcid.current.cid.datalen &&
- ((new_cid_used && remote_addr_cmp) || !local_addr_eq);
-
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
"non-probing packet was received from new remote address");
@@ -8326,8 +8853,6 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
"Found DCID which has already been bound to the new path");
- require_new_cid = 0;
-
if (dcid.cid.datalen) {
rv = conn_call_activate_dcid(conn, &dcid);
if (rv != 0) {
@@ -8335,7 +8860,9 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
}
}
} else {
- if (require_new_cid) {
+ if (conn->dcid.current.cid.datalen &&
+ ((new_cid_used && remote_addr_cmp) || !local_addr_eq)) {
+ /* New DCID is required. */
if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) {
return NGTCP2_ERR_CONN_ID_BLOCKED;
}
@@ -8421,7 +8948,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
conn->pv = pv;
- return 0;
+ return conn_call_begin_path_validation(conn, conn->pv);
}
/*
@@ -8500,8 +9027,8 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
const uint8_t *payload, size_t payloadlen,
ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
ngtcp2_ssize nread;
- ngtcp2_max_frame mfr;
- ngtcp2_frame *fr = &mfr.fr;
+ ngtcp2_frame_decoder frd;
+ ngtcp2_frame fr;
int rv;
int require_ack = 0;
ngtcp2_pktns *pktns;
@@ -8519,7 +9046,7 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
ngtcp2_qlog_pkt_received_start(&conn->qlog);
for (; payloadlen;) {
- nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ nread = ngtcp2_frame_decoder_decode(&frd, &fr, payload, payloadlen);
if (nread < 0) {
return (int)nread;
}
@@ -8527,14 +9054,14 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
payload += nread;
payloadlen -= (size_t)nread;
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
- fr->ack.ack_delay = 0;
- fr->ack.ack_delay_unscaled = 0;
+ fr.ack.ack_delay = 0;
+ fr.ack.ack_delay_unscaled = 0;
rv =
- ngtcp2_pkt_validate_ack(&fr->ack, conn->local.settings.initial_pkt_num);
+ ngtcp2_pkt_validate_ack(&fr.ack, conn->local.settings.initial_pkt_num);
if (rv != 0) {
return rv;
}
@@ -8542,9 +9069,9 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
break;
}
- ngtcp2_log_rx_fr(&conn->log, hd, fr);
+ ngtcp2_log_rx_fr(&conn->log, hd, &fr);
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
if (num_ack_processed >= NGTCP2_MAX_ACK_PER_PKT) {
@@ -8553,7 +9080,7 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
if (!conn->server) {
conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
}
- rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ rv = conn_recv_ack(conn, pktns, &fr.ack, pkt_ts, ts);
if (rv != 0) {
return rv;
}
@@ -8562,20 +9089,22 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
case NGTCP2_FRAME_PADDING:
break;
case NGTCP2_FRAME_CONNECTION_CLOSE:
- rv = conn_recv_connection_close(conn, &fr->connection_close);
+ rv = conn_recv_connection_close(conn, &fr.connection_close);
if (rv != 0) {
return rv;
}
break;
- case NGTCP2_FRAME_CRYPTO:
case NGTCP2_FRAME_PING:
+ ++conn->cstat.ping_recv;
+ /* fall through */
+ case NGTCP2_FRAME_CRYPTO:
require_ack = 1;
break;
default:
return NGTCP2_ERR_PROTO;
}
- ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &fr);
}
ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen);
@@ -8630,6 +9159,8 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
* Flow control limit is violated.
* NGTCP2_ERR_FINAL_SIZE
* Frame has strictly larger end offset than it is permitted.
+ * NGTCP2_ERR_INTERNAL
+ * Suspicious remote endpoint activity exceeded threshold.
*/
static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
const ngtcp2_pkt_info *pi, const uint8_t *pkt,
@@ -8641,8 +9172,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
const uint8_t *payload;
size_t payloadlen;
ngtcp2_ssize nread, nwrite;
- ngtcp2_max_frame mfr;
- ngtcp2_frame *fr = &mfr.fr;
+ ngtcp2_frame_decoder frd;
+ ngtcp2_frame fr;
int require_ack = 0;
ngtcp2_crypto_aead *aead;
ngtcp2_crypto_cipher *hp;
@@ -8736,8 +9267,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
default:
ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "packet type 0x%02x was ignored", hd.type);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet type 0x%02x was ignored", hd.type);
return (ngtcp2_ssize)pktlen;
}
} else {
@@ -8784,8 +9315,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->acktr.max_pkt_num, hd.pkt_num,
hd.pkt_numlen);
if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
return NGTCP2_ERR_DISCARD_PKT;
}
@@ -8796,7 +9327,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
conn->rx.preferred_addr.pkt_num < hd.pkt_num &&
ngtcp2_sockaddr_eq((const ngtcp2_sockaddr *)&conn->hs_local_addr,
path->local.addr)) {
- ngtcp2_log_info(
+ ngtcp2_log_infof(
&conn->log, NGTCP2_LOG_EVENT_PKT,
"pkt=%" PRId64
" is discarded because it was received on handshake local "
@@ -8948,7 +9479,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_qlog_pkt_received_start(&conn->qlog);
for (; payloadlen;) {
- nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ nread = ngtcp2_frame_decoder_decode(&frd, &fr, payload, payloadlen);
if (nread < 0) {
return nread;
}
@@ -8956,7 +9487,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
payload += nread;
payloadlen -= (size_t)nread;
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) &&
@@ -8965,10 +9496,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
assert(conn->remote.transport_params);
assign_recved_ack_delay_unscaled(
- &fr->ack, conn->remote.transport_params->ack_delay_exponent);
+ &fr.ack, conn->remote.transport_params->ack_delay_exponent);
rv =
- ngtcp2_pkt_validate_ack(&fr->ack, conn->local.settings.initial_pkt_num);
+ ngtcp2_pkt_validate_ack(&fr.ack, conn->local.settings.initial_pkt_num);
if (rv != 0) {
return rv;
}
@@ -8976,10 +9507,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
}
- ngtcp2_log_rx_fr(&conn->log, &hd, fr);
+ ngtcp2_log_rx_fr(&conn->log, &hd, &fr);
if (hd.type == NGTCP2_PKT_0RTT) {
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_PADDING:
case NGTCP2_FRAME_PING:
case NGTCP2_FRAME_RESET_STREAM:
@@ -9005,7 +9536,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
case NGTCP2_FRAME_PADDING:
@@ -9016,7 +9547,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
require_ack = 1;
}
- switch (fr->type) {
+ switch (fr.hd.type) {
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
if (num_ack_processed >= NGTCP2_MAX_ACK_PER_PKT) {
@@ -9025,7 +9556,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
if (!conn->server) {
conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
}
- rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ rv = conn_recv_ack(conn, pktns, &fr.ack, pkt_ts, ts);
if (rv != 0) {
return rv;
}
@@ -9033,7 +9564,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
++num_ack_processed;
break;
case NGTCP2_FRAME_STREAM:
- rv = conn_recv_stream(conn, &fr->stream);
+ rv = conn_recv_stream(conn, &fr.stream, ts);
if (rv != 0) {
return rv;
}
@@ -9041,40 +9572,40 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
case NGTCP2_FRAME_CRYPTO:
rv = conn_recv_crypto(conn, NGTCP2_ENCRYPTION_LEVEL_1RTT,
- &pktns->crypto.strm, &fr->stream);
+ &pktns->crypto.strm, &fr.stream, ts);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_RESET_STREAM:
- rv = conn_recv_reset_stream(conn, &fr->reset_stream);
+ rv = conn_recv_reset_stream(conn, &fr.reset_stream, ts);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_STOP_SENDING:
- rv = conn_recv_stop_sending(conn, &fr->stop_sending);
+ rv = conn_recv_stop_sending(conn, &fr.stop_sending, ts);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_MAX_STREAM_DATA:
- rv = conn_recv_max_stream_data(conn, &fr->max_stream_data);
+ rv = conn_recv_max_stream_data(conn, &fr.max_stream_data, ts);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_MAX_DATA:
- conn_recv_max_data(conn, &fr->max_data);
+ conn_recv_max_data(conn, &fr.max_data);
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_MAX_STREAMS_BIDI:
case NGTCP2_FRAME_MAX_STREAMS_UNI:
- rv = conn_recv_max_streams(conn, &fr->max_streams);
+ rv = conn_recv_max_streams(conn, &fr.max_streams);
if (rv != 0) {
return rv;
}
@@ -9082,41 +9613,42 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
case NGTCP2_FRAME_CONNECTION_CLOSE:
case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
- rv = conn_recv_connection_close(conn, &fr->connection_close);
+ rv = conn_recv_connection_close(conn, &fr.connection_close);
if (rv != 0) {
return rv;
}
break;
case NGTCP2_FRAME_PING:
+ ++conn->cstat.ping_recv;
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_PATH_CHALLENGE:
- conn_recv_path_challenge(conn, path, &fr->path_challenge);
+ conn_recv_path_challenge(conn, path, &fr.path_challenge);
path_challenge_recved = 1;
break;
case NGTCP2_FRAME_PATH_RESPONSE:
- rv = conn_recv_path_response(conn, &hd, &fr->path_response, ts);
+ rv = conn_recv_path_response(conn, &hd, &fr.path_response, ts);
if (rv != 0) {
return rv;
}
break;
case NGTCP2_FRAME_NEW_CONNECTION_ID:
- rv = conn_recv_new_connection_id(conn, &fr->new_connection_id);
+ rv = conn_recv_new_connection_id(conn, &fr.new_connection_id);
if (rv != 0) {
return rv;
}
recv_ncid = 1;
break;
case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
- rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id,
- ts);
+ rv =
+ conn_recv_retire_connection_id(conn, &hd, &fr.retire_connection_id, ts);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_NEW_TOKEN:
- rv = conn_recv_new_token(conn, &fr->new_token);
+ rv = conn_recv_new_token(conn, &fr.new_token);
if (rv != 0) {
return rv;
}
@@ -9130,28 +9662,28 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
- rv = conn_recv_streams_blocked_bidi(conn, &fr->streams_blocked);
+ rv = conn_recv_streams_blocked_bidi(conn, &fr.streams_blocked);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
- rv = conn_recv_streams_blocked_uni(conn, &fr->streams_blocked);
+ rv = conn_recv_streams_blocked_uni(conn, &fr.streams_blocked);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
- rv = conn_recv_stream_data_blocked(conn, &fr->stream_data_blocked);
+ rv = conn_recv_stream_data_blocked(conn, &fr.stream_data_blocked);
if (rv != 0) {
return rv;
}
non_probing_pkt = 1;
break;
case NGTCP2_FRAME_DATA_BLOCKED:
- rv = conn_recv_data_blocked(conn, &fr->data_blocked);
+ rv = conn_recv_data_blocked(conn, &fr.data_blocked);
if (rv != 0) {
return rv;
}
@@ -9163,7 +9695,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
conn->local.transport_params.max_datagram_frame_size) {
return NGTCP2_ERR_PROTO;
}
- rv = conn_recv_datagram(conn, &fr->datagram);
+ rv = conn_recv_datagram(conn, &fr.datagram);
if (rv != 0) {
return rv;
}
@@ -9171,7 +9703,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
break;
}
- ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &fr);
}
ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen);
@@ -9286,6 +9818,7 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
*ppc = next;
if (nread < 0) {
if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ ++conn->cstat.pkt_discarded;
continue;
}
return (int)nread;
@@ -9320,6 +9853,7 @@ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn,
*ppc = next;
if (nread < 0) {
if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ ++conn->cstat.pkt_discarded;
continue;
}
return (int)nread;
@@ -9465,6 +9999,7 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ ++conn->cstat.pkt_discarded;
return 0;
}
return (int)nread;
@@ -9474,8 +10009,11 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
pkt += nread;
pktlen -= (size_t)nread;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
- "read packet %td left %zu", nread, pktlen);
+ ++conn->cstat.pkt_recv;
+ conn->cstat.bytes_recv += (uint64_t)nread;
+
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "read packet %td left %zu", nread, pktlen);
}
return 0;
@@ -9497,7 +10035,7 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
return rv;
}
- nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE;
+ nfrc->fr.handshake_done.type = NGTCP2_FRAME_HANDSHAKE_DONE;
nfrc->next = pktns->tx.frq;
pktns->tx.frq = nfrc;
@@ -9647,9 +10185,9 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
if ((size_t)nread < pktlen) {
/* 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 1RTT packet len=%zu",
- pktlen - (size_t)nread);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 1RTT packet len=%zu",
+ pktlen - (size_t)nread);
rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread,
pktlen - (size_t)nread, pktlen, ts);
@@ -9728,8 +10266,8 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
conn_update_timestamp(conn, ts);
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu",
- pktlen);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu",
+ pktlen);
if (pktlen == 0) {
return 0;
@@ -9741,6 +10279,8 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
!ngtcp2_dcidtr_check_path_retired(&conn->dcid.dtr, path)) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
"ignore packet from unknown path");
+ ++conn->cstat.pkt_discarded;
+
return 0;
}
@@ -9814,6 +10354,49 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts);
}
+int ngtcp2_conn_continue_handshake(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_encryption_level encryption_level;
+ uint64_t offset;
+
+ conn_update_timestamp(conn, ts);
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ case NGTCP2_CS_SERVER_INITIAL:
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ /* Most of the handshake interruption happens in Initial
+ encryption level, but this might not be the case depending on
+ the TLS stack and its functionality and where interruption
+ occurs. After all, we do not need to support all kinds of
+ interruptions. */
+ if (conn->in_pktns) {
+ encryption_level = NGTCP2_ENCRYPTION_LEVEL_INITIAL;
+ offset = ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm);
+ } else if (conn->hs_pktns) {
+ encryption_level = NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE;
+ offset = ngtcp2_strm_rx_offset(&conn->hs_pktns->crypto.strm);
+ } else {
+ return 0;
+ }
+
+ rv = conn_call_recv_crypto_data(conn, encryption_level, offset, NULL, 0);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return (int)conn_read_handshake(conn, /* path = */ NULL, /* pi = */ NULL,
+ /* pkt = */ NULL, 0, ts);
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return 0;
+ }
+}
+
/*
* conn_check_pkt_num_exhausted returns nonzero if packet number is
* exhausted in at least one of packet number space.
@@ -9915,6 +10498,7 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
size_t origlen = destlen;
uint64_t pending_early_datalen;
ngtcp2_preferred_addr *paddr;
+ ngtcp2_stateless_reset_token token;
switch (conn->state) {
case NGTCP2_CS_CLIENT_INITIAL:
@@ -10042,8 +10626,8 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
assert(!ngtcp2_dcidtr_unused_full(&conn->dcid.dtr));
paddr = &conn->remote.transport_params->preferred_addr;
- ngtcp2_dcidtr_push_unused(&conn->dcid.dtr, 1, &paddr->cid,
- paddr->stateless_reset_token);
+ memcpy(token.data, paddr->stateless_reset_token, sizeof(token.data));
+ ngtcp2_dcidtr_push_unused(&conn->dcid.dtr, 1, &paddr->cid, &token);
rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1);
if (rv != 0) {
@@ -10051,12 +10635,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
}
- if (conn->remote.transport_params->stateless_reset_token_present) {
- assert(conn->dcid.current.seq == 0);
+ if (conn->remote.transport_params->stateless_reset_token_present &&
+ conn->dcid.current.seq == 0) {
assert(!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT));
- ngtcp2_dcid_set_token(
- &conn->dcid.current,
- conn->remote.transport_params->stateless_reset_token);
+ memcpy(token.data, conn->remote.transport_params->stateless_reset_token,
+ sizeof(token.data));
+ ngtcp2_dcid_set_token(&conn->dcid.current, &token);
}
rv = conn_call_activate_dcid(conn, &conn->dcid.current);
@@ -10095,8 +10679,6 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
res += nwrite;
- dest += nwrite;
- destlen -= (size_t)nwrite;
}
if (res == 0) {
@@ -10106,8 +10688,6 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
}
res += nwrite;
- dest += nwrite;
- origlen -= (size_t)nwrite;
}
return res;
@@ -10343,6 +10923,9 @@ int ngtcp2_conn_install_initial_key(
rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, NULL, tx_iv, ivlen,
conn->mem);
if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+
return rv;
}
@@ -10393,6 +10976,9 @@ int ngtcp2_conn_install_vneg_initial_key(
rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen,
conn->mem);
if (rv != 0) {
+ ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+ conn->vneg.rx.ckm = NULL;
+
return rv;
}
@@ -10430,8 +11016,7 @@ int ngtcp2_conn_install_rx_handshake_key(
if (rv != 0) {
ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
pktns->crypto.rx.ckm = NULL;
-
- memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx));
+ pktns->crypto.rx.hp_ctx = (ngtcp2_crypto_cipher_ctx){0};
return rv;
}
@@ -10461,21 +11046,26 @@ int ngtcp2_conn_install_tx_handshake_key(
if (conn->server) {
rv = ngtcp2_conn_commit_local_transport_params(conn);
if (rv != 0) {
- return rv;
+ goto fail;
}
}
rv = conn_call_recv_tx_key(conn, NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE);
if (rv != 0) {
- ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
- pktns->crypto.tx.ckm = NULL;
-
- memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx));
-
- return rv;
+ goto fail;
}
return 0;
+
+fail:
+ /* If this function fails, aead_ctx and hp_ctx are still owned by
+ the caller. Delete the install key to remove the any reference
+ to them. */
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+ pktns->crypto.tx.hp_ctx = (ngtcp2_crypto_cipher_ctx){0};
+
+ return rv;
}
int ngtcp2_conn_install_0rtt_key(ngtcp2_conn *conn,
@@ -10506,8 +11096,7 @@ int ngtcp2_conn_install_0rtt_key(ngtcp2_conn *conn,
if (rv != 0) {
ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
conn->early.ckm = NULL;
-
- memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx));
+ conn->early.hp_ctx = (ngtcp2_crypto_cipher_ctx){0};
return rv;
}
@@ -10554,8 +11143,7 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret,
if (rv != 0) {
ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
pktns->crypto.rx.ckm = NULL;
-
- memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx));
+ pktns->crypto.rx.hp_ctx = (ngtcp2_crypto_cipher_ctx){0};
return rv;
}
@@ -10600,8 +11188,7 @@ int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret,
if (rv != 0) {
ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
pktns->crypto.tx.ckm = NULL;
-
- memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx));
+ pktns->crypto.tx.hp_ctx = (ngtcp2_crypto_cipher_ctx){0};
return rv;
}
@@ -11059,9 +11646,9 @@ int ngtcp2_conn_set_remote_transport_params(
conn->local.transport_params.version_info.chosen_version =
conn->negotiated_version;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "the negotiated version is 0x%08x",
- conn->negotiated_version);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
} else {
rv = conn_client_validate_transport_params(conn, params);
if (rv != 0) {
@@ -11213,21 +11800,18 @@ int ngtcp2_conn_set_0rtt_remote_transport_params(
/* These parameters are treated specially. If server accepts early
data, it must not set values for these parameters that are
smaller than these remembered values. */
- conn->early.transport_params.initial_max_streams_bidi =
- params->initial_max_streams_bidi;
- conn->early.transport_params.initial_max_streams_uni =
- params->initial_max_streams_uni;
- conn->early.transport_params.initial_max_stream_data_bidi_local =
- params->initial_max_stream_data_bidi_local;
- conn->early.transport_params.initial_max_stream_data_bidi_remote =
- params->initial_max_stream_data_bidi_remote;
- conn->early.transport_params.initial_max_stream_data_uni =
- params->initial_max_stream_data_uni;
- conn->early.transport_params.initial_max_data = params->initial_max_data;
- conn->early.transport_params.active_connection_id_limit =
- params->active_connection_id_limit;
- conn->early.transport_params.max_datagram_frame_size =
- params->max_datagram_frame_size;
+ conn->early.transport_params = (ngtcp2_early_transport_params){
+ .initial_max_streams_bidi = params->initial_max_streams_bidi,
+ .initial_max_streams_uni = params->initial_max_streams_uni,
+ .initial_max_stream_data_bidi_local =
+ params->initial_max_stream_data_bidi_local,
+ .initial_max_stream_data_bidi_remote =
+ params->initial_max_stream_data_bidi_remote,
+ .initial_max_stream_data_uni = params->initial_max_stream_data_uni,
+ .initial_max_data = params->initial_max_data,
+ .active_connection_id_limit = params->active_connection_id_limit,
+ .max_datagram_frame_size = params->max_datagram_frame_size,
+ };
conn_sync_stream_id_limit(conn);
@@ -11417,17 +12001,20 @@ conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path,
return nwrite;
}
+ assert((size_t)nwrite <= destlen);
+
if (cstat->bytes_in_flight >= cstat->cwnd) {
conn->rst.is_cwnd_limited = 1;
} else if ((cstat->cwnd >= cstat->ssthresh ||
cstat->bytes_in_flight * 2 < cstat->cwnd) &&
nwrite == 0 && conn_pacing_pkt_tx_allowed(conn, ts) &&
- (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
- conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight;
-
- if (conn->rst.app_limited == 0) {
- conn->rst.app_limited = cstat->max_tx_udp_payload_size;
- }
+ (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+ /* Because NGTCP2_CONN_FLAG_AGGREGATE_PKTS is set after a
+ packet is produced, if it is set, we are sure that we
+ are not app-limited. */
+ !(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS)) {
+ conn->rst.app_limited =
+ ngtcp2_max_uint64(conn->rst.delivered + cstat->bytes_in_flight, 1);
}
return nwrite;
@@ -11471,12 +12058,17 @@ ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- vmsg.type = NGTCP2_VMSG_TYPE_STREAM;
- vmsg.stream.strm = strm;
- vmsg.stream.flags = flags;
- vmsg.stream.data = datav;
- vmsg.stream.datacnt = datavcnt;
- vmsg.stream.pdatalen = pdatalen;
+ vmsg = (ngtcp2_vmsg){
+ .type = NGTCP2_VMSG_TYPE_STREAM,
+ .stream =
+ {
+ .strm = strm,
+ .data = datav,
+ .datacnt = datavcnt,
+ .pdatalen = pdatalen,
+ .flags = flags,
+ },
+ };
pvmsg = &vmsg;
}
@@ -11549,12 +12141,17 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM;
- vmsg.datagram.dgram_id = dgram_id;
- vmsg.datagram.flags = flags;
- vmsg.datagram.data = datav;
- vmsg.datagram.datacnt = datavcnt;
- vmsg.datagram.paccepted = paccepted;
+ vmsg = (ngtcp2_vmsg){
+ .type = NGTCP2_VMSG_TYPE_DATAGRAM,
+ .datagram =
+ {
+ .data = datav,
+ .datacnt = datavcnt,
+ .dgram_id = dgram_id,
+ .paccepted = paccepted,
+ .flags = flags,
+ },
+ };
if (flags & NGTCP2_WRITE_DATAGRAM_FLAG_PADDING) {
wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY;
@@ -11610,27 +12207,29 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
conn_client_write_handshake(conn, pi, dest, destlen, wflags, vmsg, ts);
/* 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) {
+ shrinks the window drastically. Then continue if we are in
+ post-handshake. There, we might be able to write packets
+ exceeding CWND to avoid deadlock. */
+ if (nwrite < 0) {
return nwrite;
}
if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
return nwrite;
}
- assert(nwrite);
- assert(dest[0] & NGTCP2_HEADER_FORM_BIT);
- assert(conn->negotiated_version);
+ if (nwrite) {
+ assert(dest[0] & NGTCP2_HEADER_FORM_BIT);
+ assert(conn->negotiated_version);
- if (nwrite < NGTCP2_MAX_UDP_PAYLOAD_SIZE &&
- ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) ==
+ if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) ==
NGTCP2_PKT_INITIAL) {
- wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
- }
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
- res = nwrite;
- dest += nwrite;
- destlen -= (size_t)nwrite;
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
/* Break here so that we can coalesces 1RTT packet. */
break;
case NGTCP2_CS_SERVER_INITIAL:
@@ -11685,7 +12284,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
dest += nwrite;
destlen -= (size_t)nwrite;
- if (res < NGTCP2_MAX_UDP_PAYLOAD_SIZE && conn->in_pktns && nwrite > 0) {
+ if (conn->in_pktns && nwrite > 0) {
it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
if (!ngtcp2_ksl_it_end(&it)) {
rtbent = ngtcp2_ksl_it_get(&it);
@@ -11770,7 +12369,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) {
destlen = 0;
} else {
- if (res == 0) {
+ if (res == 0 && !(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS)) {
nwrite =
conn_write_path_response(conn, path, pi, dest, origdestlen, ts);
if (nwrite) {
@@ -11832,9 +12431,22 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
return nwrite;
}
if (nwrite > 0) {
+ /* This makes 1RTT packet padded. If 1RTT packet is not going
+ to be sent, packet is already padded. */
+ if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) ==
+ NGTCP2_PKT_INITIAL) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+
res = nwrite;
dest += nwrite;
destlen -= (size_t)nwrite;
+
+ /* We only exceed CWND to avoid deadlock. Do no write 1RTT
+ packet if CWND is depleted. */
+ if (conn_cwnd_is_zero(conn) && conn->pktns.rtb.probe_pkt_left == 0) {
+ return res;
+ }
} else if (destlen == 0) {
res = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
if (res) {
@@ -11845,9 +12457,9 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
}
if (conn->pktns.rtb.probe_pkt_left) {
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
- "transmit probe pkt left=%zu",
- conn->pktns.rtb.probe_pkt_left);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "transmit probe pkt left=%zu",
+ conn->pktns.rtb.probe_pkt_left);
nwrite = conn_write_pkt(conn, pi, dest, destlen, (size_t)res, vmsg,
NGTCP2_PKT_1RTT, wflags, ts);
@@ -11901,11 +12513,12 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
ngtcp2_frame fr;
uint8_t flags = NGTCP2_WRITE_PKT_FLAG_NONE;
- fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
- fr.connection_close.error_code = error_code;
- fr.connection_close.frame_type = 0;
- fr.connection_close.reasonlen = reasonlen;
- fr.connection_close.reason = (uint8_t *)reason;
+ fr.connection_close = (ngtcp2_connection_close){
+ .type = NGTCP2_FRAME_CONNECTION_CLOSE,
+ .error_code = error_code,
+ .reasonlen = reasonlen,
+ .reason = (uint8_t *)reason,
+ };
if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) &&
pkt_type != NGTCP2_PKT_INITIAL) {
@@ -12080,11 +12693,12 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
assert(conn->pktns.crypto.tx.ckm);
- fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP;
- fr.connection_close.error_code = app_error_code;
- fr.connection_close.frame_type = 0;
- fr.connection_close.reasonlen = reasonlen;
- fr.connection_close.reason = (uint8_t *)reason;
+ fr.connection_close = (ngtcp2_connection_close){
+ .type = NGTCP2_FRAME_CONNECTION_CLOSE_APP,
+ .error_code = app_error_code,
+ .reasonlen = reasonlen,
+ .reason = (uint8_t *)reason,
+ };
nwrite = ngtcp2_conn_write_single_frame_pkt(
conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
@@ -12108,11 +12722,12 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
static void ccerr_init(ngtcp2_ccerr *ccerr, ngtcp2_ccerr_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;
+ *ccerr = (ngtcp2_ccerr){
+ .type = type,
+ .error_code = error_code,
+ .reason = (uint8_t *)reason,
+ .reasonlen = reasonlen,
+ };
}
void ngtcp2_ccerr_default(ngtcp2_ccerr *ccerr) {
@@ -12147,7 +12762,7 @@ void ngtcp2_ccerr_set_liberr(ngtcp2_ccerr *ccerr, int liberr,
reasonlen);
return;
- };
+ }
ngtcp2_ccerr_set_transport_error(
ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason,
@@ -12528,8 +13143,8 @@ int ngtcp2_conn_get_tls_early_data_rejected(ngtcp2_conn *conn) {
return (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) != 0;
}
-int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
- ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
+void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+ ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
ngtcp2_conn_stat *cstat = &conn->cstat;
assert(rtt > 0);
@@ -12550,13 +13165,13 @@ int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration 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(
+ ngtcp2_log_infof(
&conn->log, NGTCP2_LOG_EVENT_LDC,
"ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64
" min_rtt=%" PRIu64 " ack_delay=%" PRIu64,
rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS,
ack_delay / NGTCP2_MILLISECONDS);
- return NGTCP2_ERR_INVALID_ARGUMENT;
+ return;
}
cstat->latest_rtt = rtt;
@@ -12573,7 +13188,7 @@ int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8;
}
- ngtcp2_log_info(
+ ngtcp2_log_infof(
&conn->log, NGTCP2_LOG_EVENT_LDC,
"latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64
" rttvar=%" PRIu64 " ack_delay=%" PRIu64,
@@ -12581,23 +13196,12 @@ int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
cstat->min_rtt / NGTCP2_MILLISECONDS,
cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS);
-
- return 0;
}
void ngtcp2_conn_get_conn_info_versioned(ngtcp2_conn *conn,
int conn_info_version,
ngtcp2_conn_info *cinfo) {
- const ngtcp2_conn_stat *cstat = &conn->cstat;
- (void)conn_info_version;
-
- cinfo->latest_rtt = cstat->latest_rtt;
- cinfo->min_rtt = cstat->min_rtt;
- cinfo->smoothed_rtt = cstat->smoothed_rtt;
- cinfo->rttvar = cstat->rttvar;
- cinfo->cwnd = cstat->cwnd;
- cinfo->ssthresh = cstat->ssthresh;
- cinfo->bytes_in_flight = cstat->bytes_in_flight;
+ ngtcp2_conn_info_init_versioned(conn_info_version, cinfo, &conn->cstat);
}
static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn,
@@ -12679,9 +13283,10 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
if (earliest_loss_time != UINT64_MAX) {
cstat->loss_detection_timer = earliest_loss_time;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC,
- "loss_detection_timer=%" PRIu64 " nonzero crypto loss time",
- cstat->loss_detection_timer);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_LDC,
+ "loss_detection_timer=%" PRIu64
+ " nonzero crypto loss time",
+ cstat->loss_detection_timer);
return;
}
@@ -12705,9 +13310,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
timeout =
cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC,
- "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64,
- cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_LDC,
+ "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64,
+ cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS);
}
void ngtcp2_conn_cancel_loss_detection_timer(ngtcp2_conn *conn) {
@@ -12779,8 +13384,8 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
++cstat->pto_count;
- ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, "pto_count=%zu",
- cstat->pto_count);
+ ngtcp2_log_infof(&conn->log, NGTCP2_LOG_EVENT_LDC, "pto_count=%zu",
+ cstat->pto_count);
ngtcp2_conn_set_loss_detection_timer(conn, ts);
@@ -12821,7 +13426,6 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
const uint8_t *data, const size_t datalen) {
ngtcp2_pktns *pktns;
ngtcp2_frame_chain *frc;
- ngtcp2_stream *fr;
int rv;
if (datalen == 0) {
@@ -12849,21 +13453,22 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
return rv;
}
- rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &frc, 1, &conn->frc_objalloc, conn->mem);
if (rv != 0) {
return rv;
}
- fr = &frc->fr.stream;
-
- fr->type = NGTCP2_FRAME_CRYPTO;
- fr->flags = 0;
- fr->fin = 0;
- fr->stream_id = 0;
- fr->offset = pktns->crypto.tx.offset;
- fr->datacnt = 1;
- fr->data[0].len = datalen;
- fr->data[0].base = (uint8_t *)data;
+ frc->fr.stream.type = NGTCP2_FRAME_CRYPTO;
+ frc->fr.stream.flags = 0;
+ frc->fr.stream.fin = 0;
+ frc->fr.stream.stream_id = 0;
+ frc->fr.stream.offset = pktns->crypto.tx.offset;
+ frc->fr.stream.datacnt = 1;
+ frc->fr.stream.data[0] = (ngtcp2_vec){
+ .base = (uint8_t *)data,
+ .len = datalen,
+ };
rv = ngtcp2_strm_streamfrq_push(&pktns->crypto.strm, frc);
if (rv != 0) {
@@ -12968,20 +13573,46 @@ static size_t conn_get_num_active_dcid(ngtcp2_conn *conn) {
return n;
}
-static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest,
+size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
+ ngtcp2_cid_token2 cid_tokens[/* current */ 1 + /* pv */ 2 +
+ NGTCP2_DCIDTR_MAX_RETIRED_DCID_SIZE];
+ size_t n, i;
+
+ if (!dest) {
+ return ngtcp2_conn_get_active_dcid2(conn, NULL);
+ }
+
+ n = ngtcp2_conn_get_active_dcid2(conn, cid_tokens);
+
+ for (i = 0; i < n; ++i) {
+ dest[i].seq = cid_tokens[i].seq;
+ dest[i].cid = cid_tokens[i].cid;
+ ngtcp2_path_storage_init2(&dest[i].ps, &cid_tokens[i].ps.path);
+ dest[i].token_present = cid_tokens[i].token_present;
+
+ if (dest[i].token_present) {
+ memcpy(dest[i].token, cid_tokens[i].token.data, sizeof(dest[i].token));
+ }
+ }
+
+ return n;
+}
+
+static void copy_dcid_to_cid_token(ngtcp2_cid_token2 *dest,
const ngtcp2_dcid *src) {
dest->seq = src->seq;
dest->cid = src->cid;
ngtcp2_path_storage_init2(&dest->ps, &src->ps.path);
if ((dest->token_present =
(src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) != 0)) {
- memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dest->token = src->token;
}
}
-size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
+size_t ngtcp2_conn_get_active_dcid2(ngtcp2_conn *conn,
+ ngtcp2_cid_token2 *dest) {
ngtcp2_pv *pv = conn->pv;
- ngtcp2_cid_token *orig = dest;
+ ngtcp2_cid_token2 *orig = dest;
ngtcp2_dcid *dcid;
size_t len, i;
@@ -13138,7 +13769,12 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
conn->pv = pv;
}
- return conn_call_activate_dcid(conn, &conn->dcid.current);
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_begin_path_validation(conn, conn->pv);
}
int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
@@ -13178,7 +13814,12 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
conn->pv = pv;
- return conn_call_activate_dcid(conn, &pv->dcid);
+ rv = conn_call_activate_dcid(conn, &pv->dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_begin_path_validation(conn, conn->pv);
}
uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) {
@@ -13354,8 +13995,17 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id,
return 0;
}
+void *ngtcp2_conn_get_stream_user_data(ngtcp2_conn *conn, int64_t stream_id) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return NULL;
+ }
+
+ return strm->stream_user_data;
+}
+
void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
- uint64_t pacing_interval_m;
ngtcp2_duration wait, d;
conn_update_timestamp(conn, ts);
@@ -13364,20 +14014,9 @@ void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
return;
}
- if (conn->cstat.pacing_interval_m) {
- pacing_interval_m = conn->cstat.pacing_interval_m;
- } else {
- /* 1.25 is the under-utilization avoidance factor described in
- https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */
- pacing_interval_m = ((conn->cstat.first_rtt_sample_ts == UINT64_MAX
- ? NGTCP2_MILLISECONDS
- : conn->cstat.smoothed_rtt)
- << 10) *
- 100 / 125 / conn->cstat.cwnd;
- pacing_interval_m = ngtcp2_max_uint64(pacing_interval_m, 1);
- }
-
- wait = (ngtcp2_duration)((conn->tx.pacing.pktlen * pacing_interval_m) >> 10);
+ wait = (ngtcp2_duration)((conn->tx.pacing.pktlen *
+ conn->cstat.pacing_interval_m) >>
+ 10);
d = ngtcp2_min_uint64(wait / 2, conn->tx.pacing.compensation);
wait -= d;
@@ -13411,6 +14050,115 @@ void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
ent->ts = ts;
}
+ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
+ ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts) {
+ ngtcp2_ssize nwrite;
+
+ buflen = ngtcp2_min_size(buflen, ngtcp2_conn_get_send_quantum(conn));
+
+ nwrite = ngtcp2_conn_write_aggregate_pkt2_versioned(
+ conn, path, pkt_info_version, pi, buf, buflen, pgsolen, write_pkt, 0, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ ngtcp2_conn_update_pkt_tx_time(conn, ts);
+
+ return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt2_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
+ ngtcp2_write_pkt write_pkt, size_t num_pkts, ngtcp2_tstamp ts) {
+ size_t max_udp_payloadlen = ngtcp2_conn_get_max_tx_udp_payload_size(conn);
+ size_t path_max_udp_payloadlen =
+ ngtcp2_conn_get_path_max_tx_udp_payload_size(conn);
+ ngtcp2_ssize nwrite;
+ uint8_t *wbuf = buf;
+ size_t wbuflen;
+ ngtcp2_ecn_state ecn_state;
+ int first_pkt;
+ ngtcp2_pkt_info pi_discard;
+ ngtcp2_path_storage path_discard;
+ (void)pkt_info_version;
+
+ assert(buflen >= path_max_udp_payloadlen);
+
+ if (num_pkts == 0) {
+ num_pkts = SIZE_MAX;
+ }
+
+ for (;;) {
+ ecn_state = conn->tx.ecn.state;
+
+ wbuflen = buflen >= max_udp_payloadlen ? max_udp_payloadlen
+ : path_max_udp_payloadlen;
+
+ nwrite = write_pkt(conn, path, pi, wbuf, wbuflen, ts, conn->user_data);
+ if (nwrite < 0) {
+ break;
+ }
+
+ if (nwrite == 0) {
+ nwrite = wbuf - buf;
+ break;
+ }
+
+ first_pkt = buf == wbuf;
+ wbuf += nwrite;
+ buflen -= (size_t)nwrite;
+
+ --num_pkts;
+
+ if (first_pkt) {
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS));
+
+ *pgsolen = (size_t)nwrite;
+
+ if ((size_t)nwrite != path_max_udp_payloadlen ||
+ buflen < path_max_udp_payloadlen || ecn_state != conn->tx.ecn.state ||
+ num_pkts == 0) {
+ nwrite = wbuf - buf;
+ break;
+ }
+
+ /* All aggregated packets should share the same path and pi.
+ Pass the placeholder values to the callback because they
+ might be overwritten by later calls, especially pi is set to
+ empty when no packet is produced. */
+ if (path) {
+ ngtcp2_path_storage_zero(&path_discard);
+ path = &path_discard.path;
+ }
+
+ if (pi) {
+ pi = &pi_discard;
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_AGGREGATE_PKTS;
+
+ continue;
+ }
+
+ if (buflen < path_max_udp_payloadlen || (size_t)nwrite < *pgsolen ||
+ ecn_state != conn->tx.ecn.state || num_pkts == 0) {
+ nwrite = wbuf - buf;
+ break;
+ }
+ }
+
+ conn->flags &= ~NGTCP2_CONN_FLAG_AGGREGATE_PKTS;
+
+ return nwrite;
+}
+
+ngtcp2_tstamp ngtcp2_conn_get_timestamp(const ngtcp2_conn *conn) {
+ return conn->log.last_ts;
+}
+
const ngtcp2_path_history_entry *
ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_tstamp ts) {
@@ -13434,9 +14182,9 @@ ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path,
void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
const ngtcp2_path *path,
- const uint8_t *data) {
+ const ngtcp2_path_challenge_data *data) {
ngtcp2_path_storage_init2(&pcent->ps, path);
- memcpy(pcent->data, data, sizeof(pcent->data));
+ pcent->data = *data;
}
/* The functions prefixed with ngtcp2_pkt_ are usually put inside
@@ -13454,7 +14202,7 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close(
ngtcp2_crypto_km ckm;
ngtcp2_crypto_cc cc;
ngtcp2_ppe ppe;
- ngtcp2_frame fr = {0};
+ ngtcp2_frame fr;
int rv;
ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid,
@@ -13485,11 +14233,12 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close(
return NGTCP2_ERR_NOBUF;
}
- fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
- fr.connection_close.error_code = error_code;
- fr.connection_close.frame_type = 0;
- fr.connection_close.reasonlen = reasonlen;
- fr.connection_close.reason = (uint8_t *)reason;
+ fr.connection_close = (ngtcp2_connection_close){
+ .type = NGTCP2_FRAME_CONNECTION_CLOSE,
+ .error_code = error_code,
+ .reasonlen = reasonlen,
+ .reason = (uint8_t *)reason,
+ };
rv = ngtcp2_ppe_encode_frame(&ppe, &fr);
if (rv != 0) {
diff --git a/third_party/ngtcp2/lib/ngtcp2_conn.h b/third_party/ngtcp2/lib/ngtcp2_conn.h
index 5979d39654b..47175736d42 100644
--- a/third_party/ngtcp2/lib/ngtcp2_conn.h
+++ b/third_party/ngtcp2/lib/ngtcp2_conn.h
@@ -52,6 +52,8 @@
#include "ngtcp2_rst.h"
#include "ngtcp2_conn_stat.h"
#include "ngtcp2_dcidtr.h"
+#include "ngtcp2_pcg.h"
+#include "ngtcp2_ratelim.h"
typedef enum {
/* Client specific handshake states */
@@ -75,10 +77,6 @@ typedef enum {
unreceived data. */
#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536
-/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can
- accept. */
-#define NGTCP2_MAX_RETRIES 3
-
/* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source
connection ID the local endpoint provides to the remote endpoint.
The chosen value was described in old draft. Now a remote endpoint
@@ -96,48 +94,35 @@ typedef enum {
#define NGTCP2_CCERR_MAX_REASONLEN 1024
/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u
+#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00U
/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other
than Initial packet should be padded so that UDP datagram payload
is at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes. Initial packet
might be padded based on QUIC requirement regardless of this
flag. */
-#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u
+#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01U
/* 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 0x02u
+#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02U
/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL is just like
NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, but it requests to add
padding to the full UDP datagram payload size. */
-#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL 0x04u
+#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL 0x04U
/* NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY adds padding to the QUIC
packet as much as possible if the packet is not empty. */
-#define NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY 0x08u
-
-/*
- * ngtcp2_max_frame is defined so that it covers the largest ACK
- * frame.
- */
-typedef union ngtcp2_max_frame {
- ngtcp2_frame fr;
- struct {
- ngtcp2_ack ack;
- /* ack includes 1 ngtcp2_ack_range. */
- ngtcp2_ack_range ranges[NGTCP2_MAX_ACK_RANGES - 1];
- } ackfr;
-} ngtcp2_max_frame;
+#define NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY 0x08U
typedef struct ngtcp2_path_challenge_entry {
ngtcp2_path_storage ps;
- uint8_t data[8];
+ ngtcp2_path_challenge_data data;
} ngtcp2_path_challenge_entry;
void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
const ngtcp2_path *path,
- const uint8_t *data);
+ const ngtcp2_path_challenge_data *data);
/* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_CONN_FLAG_NONE 0x00u
+#define NGTCP2_CONN_FLAG_NONE 0x00U
/* NGTCP2_CONN_FLAG_TLS_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
@@ -145,61 +130,69 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
implementations declare TLS handshake completion as server when
they write off Server Finished and before deriving application rx
secret. */
-#define NGTCP2_CONN_FLAG_TLS_HANDSHAKE_COMPLETED 0x01u
+#define NGTCP2_CONN_FLAG_TLS_HANDSHAKE_COMPLETED 0x01U
/* NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED is set when the first
Initial packet has successfully been processed. */
-#define NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED 0x02u
+#define NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED 0x02U
/* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport
parameters are received. */
-#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u
+#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04U
/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a
local transport parameters are applied. */
-#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u
+#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08U
/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry
packet. */
-#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u
+#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10U
/* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is
rejected by a peer. */
-#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u
+#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20U
/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired
keep-alive timer has been cancelled. */
-#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u
+#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40U
/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint
confirmed completion of handshake. */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u
+#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80U
/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when the library
transitions its state to "post handshake". */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x0100u
+#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x0100U
/* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early
handshake retransmission has done when server receives overlapping
Initial crypto data. */
-#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u
+#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200U
/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint
sends a QUIC packet without Fixed Bit set if a remote endpoint
supports Greasing QUIC Bit extension. */
-#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u
+#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400U
/* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is
not confirmed by the local endpoint. That is, it has not received
ACK frame which acknowledges packet which is encrypted with new
key. */
-#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800U
/* NGTCP2_CONN_FLAG_PPE_PENDING is set when
NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of
ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */
-#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u
+#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000U
/* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer
should be restarted on next write. */
-#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u
+#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000U
/* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer
verified client address. This flag is only used by client. */
-#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u
+#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000U
/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is
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 0x8000u
+#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000U
/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local
endpoint has initiated key update. */
-#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000U
+/* NGTCP2_CONN_FLAG_AGGREGATE_PKTS is set when
+ ngtcp2_conn_writev_stream is called inside the callback invoked by
+ ngtcp2_conn_write_aggregate_pkt. */
+#define NGTCP2_CONN_FLAG_AGGREGATE_PKTS 0x20000U
+/* NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO, if set, crumbles an
+ Initial CRYPTO frame into pieces as a countermeasure against Deep
+ Packet Inspection. */
+#define NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO 0x40000U
typedef struct ngtcp2_pktns {
struct {
@@ -394,14 +387,14 @@ struct ngtcp2_conn {
ngtcp2_tstamp last_max_data_ts;
struct {
- /* state is the state of ECN validation */
- ngtcp2_ecn_state state;
/* validation_start_ts is the timestamp when ECN validation is
started. It is UINT64_MAX if it has not started yet. */
ngtcp2_tstamp validation_start_ts;
/* dgram_sent is the number of UDP datagram sent during ECN
validation period. */
size_t dgram_sent;
+ /* state is the state of ECN validation */
+ ngtcp2_ecn_state state;
} ecn;
struct {
@@ -552,13 +545,13 @@ struct ngtcp2_conn {
/* retry_aead_ctx is AEAD cipher context to verify Retry packet
integrity. It is used by client only. */
ngtcp2_crypto_aead_ctx retry_aead_ctx;
+ /* decryption_failure_count is the number of received packets that
+ fail authentication. */
+ uint64_t decryption_failure_count;
/* tls_error is TLS related error. */
int tls_error;
/* tls_alert is TLS alert generated by the local endpoint. */
uint8_t tls_alert;
- /* decryption_failure_count is the number of received packets that
- fail authentication. */
- uint64_t decryption_failure_count;
} crypto;
/* pkt contains the packet intermediate construction data to support
@@ -568,13 +561,13 @@ struct ngtcp2_conn {
ngtcp2_pkt_hd hd;
ngtcp2_ppe ppe;
ngtcp2_frame_chain **pfrc;
+ ngtcp2_ssize hs_spktlen;
int pkt_empty;
int hd_logged;
+ int require_padding;
/* 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;
struct {
@@ -630,7 +623,6 @@ struct ngtcp2_conn {
ngtcp2_log log;
ngtcp2_qlog qlog;
ngtcp2_rst rst;
- ngtcp2_cc_algo cc_algo;
union {
ngtcp2_cc cc;
ngtcp2_cc_reno reno;
@@ -641,17 +633,23 @@ struct ngtcp2_conn {
successfully. The path is added to this history when a local
endpoint migrates to the another path. */
ngtcp2_static_ringbuf_path_history path_history;
+ /* glitch_rlim is the rate limit of glitches that can be tolerated.
+ If more than those glitches are detected, a connection is
+ closed. */
+ ngtcp2_ratelim glitch_rlim;
const ngtcp2_mem *mem;
/* idle_ts is the time instant when idle timer started. */
ngtcp2_tstamp idle_ts;
/* handshake_confirmed_ts is the time instant when handshake is
confirmed. For server, it is confirmed when completed. */
ngtcp2_tstamp handshake_confirmed_ts;
+ ngtcp2_pcg32 pcg;
void *user_data;
uint32_t client_chosen_version;
uint32_t negotiated_version;
/* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */
uint32_t flags;
+ ngtcp2_cc_algo cc_algo;
int server;
};
@@ -663,9 +661,6 @@ typedef enum ngtcp2_vmsg_type {
typedef struct ngtcp2_vmsg_stream {
/* strm is a stream that data is sent to. */
ngtcp2_strm *strm;
- /* flags is bitwise OR of zero or more of
- NGTCP2_WRITE_STREAM_FLAG_*. */
- uint32_t flags;
/* data is the pointer to ngtcp2_vec array which contains the stream
data to send. */
const ngtcp2_vec *data;
@@ -674,6 +669,9 @@ typedef struct ngtcp2_vmsg_stream {
/* pdatalen is the pointer to the variable which the number of bytes
written is assigned to if pdatalen is not NULL. */
ngtcp2_ssize *pdatalen;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_WRITE_STREAM_FLAG_*. */
+ uint32_t flags;
} ngtcp2_vmsg_stream;
typedef struct ngtcp2_vmsg_datagram {
@@ -684,12 +682,12 @@ typedef struct ngtcp2_vmsg_datagram {
size_t datacnt;
/* dgram_id is an opaque identifier chosen by an application. */
uint64_t dgram_id;
- /* flags is bitwise OR of zero or more of
- NGTCP2_WRITE_DATAGRAM_FLAG_*. */
- uint32_t flags;
/* paccepted is the pointer to the variable which, if it is not
NULL, is assigned nonzero if data is written to a packet. */
int *paccepted;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_WRITE_DATAGRAM_FLAG_*. */
+ uint32_t flags;
} ngtcp2_vmsg_datagram;
typedef struct ngtcp2_vmsg {
@@ -757,15 +755,9 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm);
* ack_delay included in ACK frame. |ack_delay| is actually tainted
* (sent by peer), so don't assume that |ack_delay| is always smaller
* than, or equals to |rtt|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_INVALID_ARGUMENT
- * RTT sample is ignored.
*/
-int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
- ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
+void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+ ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
@@ -951,7 +943,7 @@ ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
* @function
*
* `ngtcp2_conn_write_connection_close_pkt` writes a packet which
- * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed
+ * 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
@@ -993,7 +985,7 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
* @function
*
* `ngtcp2_conn_write_application_close_pkt` writes a packet which
- * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
+ * 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
@@ -1006,7 +998,7 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
* 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
+ * 0x1C) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
* instead.
*
* This function must not be called from inside the callback
diff --git a/third_party/ngtcp2/lib/ngtcp2_conn_info.c b/third_party/ngtcp2/lib/ngtcp2_conn_info.c
new file mode 100644
index 00000000000..84bb8820f50
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_conn_info.c
@@ -0,0 +1,50 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conn_info.h"
+#include "ngtcp2_conn_stat.h"
+
+void ngtcp2_conn_info_init_versioned(int conn_info_version,
+ ngtcp2_conn_info *cinfo,
+ const ngtcp2_conn_stat *cstat) {
+ cinfo->latest_rtt = cstat->latest_rtt;
+ cinfo->min_rtt = cstat->min_rtt;
+ cinfo->smoothed_rtt = cstat->smoothed_rtt;
+ cinfo->rttvar = cstat->rttvar;
+ cinfo->cwnd = cstat->cwnd;
+ cinfo->ssthresh = cstat->ssthresh;
+ cinfo->bytes_in_flight = cstat->bytes_in_flight;
+
+ switch (conn_info_version) {
+ case NGTCP2_CONN_INFO_V2:
+ cinfo->pkt_sent = cstat->pkt_sent;
+ cinfo->bytes_sent = cstat->bytes_sent;
+ cinfo->pkt_recv = cstat->pkt_recv;
+ cinfo->bytes_recv = cstat->bytes_recv;
+ cinfo->pkt_lost = cstat->pkt_lost;
+ cinfo->bytes_lost = cstat->bytes_lost;
+ cinfo->ping_recv = cstat->ping_recv;
+ cinfo->pkt_discarded = cstat->pkt_discarded;
+ }
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_conn_info.h b/third_party/ngtcp2/lib/ngtcp2_conn_info.h
new file mode 100644
index 00000000000..161309df304
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_conn_info.h
@@ -0,0 +1,45 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONN_INFO_H
+#define NGTCP2_CONN_INFO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* defined(HAVE_CONFIG_H) */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_conn_stat ngtcp2_conn_stat;
+
+/*
+ * ngtcp2_conn_info_init_versioned initializes |cinfo| of version
+ * |conn_info_version| from |cstat|. This function only fills the
+ * fields of |cinfo| that are available in the specified version.
+ */
+void ngtcp2_conn_info_init_versioned(int conn_info_version,
+ ngtcp2_conn_info *cinfo,
+ const ngtcp2_conn_stat *cstat);
+
+#endif /* !defined(NGTCP2_CONN_INFO_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_conn_stat.h b/third_party/ngtcp2/lib/ngtcp2_conn_stat.h
index be041b90860..b5fea473910 100644
--- a/third_party/ngtcp2/lib/ngtcp2_conn_stat.h
+++ b/third_party/ngtcp2/lib/ngtcp2_conn_stat.h
@@ -31,6 +31,8 @@
#include <ngtcp2/ngtcp2.h>
+#include "ngtcp2_pktns_id.h"
+
/**
* @struct
*
@@ -128,6 +130,43 @@ typedef struct ngtcp2_conn_stat {
* scheduled and transmitted together.
*/
size_t send_quantum;
+ /*
+ * pkt_sent is the number of QUIC packets sent.
+ */
+ uint64_t pkt_sent;
+ /*
+ * bytes_sent is the number of bytes (the sum of QUIC packet length)
+ * sent.
+ */
+ uint64_t bytes_sent;
+ /*
+ * pkt_recv is the number of QUIC packets received, excluding
+ * discarded ones.
+ */
+ uint64_t pkt_recv;
+ /*
+ * bytes_recv is the number of bytes (the sum of QUIC packet length)
+ * received, excluding discarded ones.
+ */
+ uint64_t bytes_recv;
+ /*
+ * pkt_lost is the number of QUIC packets that are considered lost,
+ * excluding PMTUD packets.
+ */
+ uint64_t pkt_lost;
+ /*
+ * bytes_lost is the number of bytes (the sum of QUIC packet length)
+ * lost, excluding PMTUD packets.
+ */
+ uint64_t bytes_lost;
+ /*
+ * ping_recv is the number of PING frames received.
+ */
+ uint64_t ping_recv;
+ /*
+ * pkt_discarded is the number of QUIC packets discarded.
+ */
+ uint64_t pkt_discarded;
} ngtcp2_conn_stat;
#endif /* !defined(NGTCP2_CONN_STAT_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_conv.c b/third_party/ngtcp2/lib/ngtcp2_conv.c
index 6528011cc0e..a0cef0c8643 100644
--- a/third_party/ngtcp2/lib/ngtcp2_conv.c
+++ b/third_party/ngtcp2/lib/ngtcp2_conv.c
@@ -63,33 +63,33 @@ const uint8_t *ngtcp2_get_uint16(uint16_t *dest, const uint8_t *p) {
}
static const uint8_t *get_uvarint(uint64_t *dest, const uint8_t *p) {
- union {
- uint8_t n8;
- uint16_t n16;
- uint32_t n32;
- uint64_t n64;
- } n;
+ uint16_t n16;
+ uint32_t n32;
+ uint64_t n64;
switch (*p >> 6) {
case 0:
*dest = *p++;
return p;
case 1:
- memcpy(&n, p, 2);
- n.n8 &= 0x3f;
- *dest = ngtcp2_ntohs(n.n16);
+ memcpy(&n16, p, 2);
+ n16 = ngtcp2_ntohs(n16);
+ n16 &= 0x3FFF;
+ *dest = n16;
return p + 2;
case 2:
- memcpy(&n, p, 4);
- n.n8 &= 0x3f;
- *dest = ngtcp2_ntohl(n.n32);
+ memcpy(&n32, p, 4);
+ n32 = ngtcp2_ntohl(n32);
+ n32 &= 0x3FFFFFFF;
+ *dest = n32;
return p + 4;
case 3:
- memcpy(&n, p, 8);
- n.n8 &= 0x3f;
- *dest = ngtcp2_ntohl64(n.n64);
+ memcpy(&n64, p, 8);
+ n64 = ngtcp2_ntohl64(n64);
+ n64 &= 0x3FFFFFFFFFFFFFFF;
+ *dest = n64;
return p + 8;
default:
@@ -168,7 +168,7 @@ uint8_t *ngtcp2_put_uvarint(uint8_t *p, uint64_t n) {
}
assert(n < 4611686018427387904ULL);
rv = ngtcp2_put_uint64be(p, n);
- *p |= 0xc0;
+ *p |= 0xC0;
return rv;
}
@@ -200,7 +200,7 @@ uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len) {
}
size_t ngtcp2_get_uvarintlen(const uint8_t *p) {
- return (size_t)(1u << (*p >> 6));
+ return (size_t)(1U << (*p >> 6));
}
size_t ngtcp2_put_uvarintlen(uint64_t n) {
diff --git a/third_party/ngtcp2/lib/ngtcp2_crypto.c b/third_party/ngtcp2/lib/ngtcp2_crypto.c
index 1f74e8c5839..078568fde3b 100644
--- a/third_party/ngtcp2/lib/ngtcp2_crypto.c
+++ b/third_party/ngtcp2/lib/ngtcp2_crypto.c
@@ -65,15 +65,21 @@ int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen,
}
p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km);
- (*pckm)->secret.base = p;
- (*pckm)->secret.len = secretlen;
- p += secretlen;
- (*pckm)->iv.base = p;
- (*pckm)->iv.len = ivlen;
- (*pckm)->aead_ctx.native_handle = NULL;
- (*pckm)->pkt_num = -1;
- (*pckm)->use_count = 0;
- (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE;
+
+ **pckm = (ngtcp2_crypto_km){
+ .secret =
+ {
+ .base = p,
+ .len = secretlen,
+ },
+ .iv =
+ {
+ .base = p + secretlen,
+ .len = ivlen,
+ },
+ .pkt_num = -1,
+ .flags = NGTCP2_CRYPTO_KM_FLAG_NONE,
+ };
return 0;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_crypto.h b/third_party/ngtcp2/lib/ngtcp2_crypto.h
index ca6d494846f..c1e786b5fc7 100644
--- a/third_party/ngtcp2/lib/ngtcp2_crypto.h
+++ b/third_party/ngtcp2/lib/ngtcp2_crypto.h
@@ -42,10 +42,10 @@
#define NGTCP2_MAX_AEAD_OVERHEAD 16
/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u
+#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00U
/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is
set. */
-#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u
+#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01U
typedef struct ngtcp2_crypto_km {
ngtcp2_vec secret;
diff --git a/third_party/ngtcp2/lib/ngtcp2_dcidtr.c b/third_party/ngtcp2/lib/ngtcp2_dcidtr.c
index 170b9f803cc..a312398bbfa 100644
--- a/third_party/ngtcp2/lib/ngtcp2_dcidtr.c
+++ b/third_party/ngtcp2/lib/ngtcp2_dcidtr.c
@@ -157,9 +157,9 @@ int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest,
return 0;
}
-int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr,
- const ngtcp2_path *path,
- const uint8_t *token) {
+int ngtcp2_dcidtr_verify_stateless_reset(
+ const ngtcp2_dcidtr *dtr, const ngtcp2_path *path,
+ const ngtcp2_stateless_reset_token *token) {
const ngtcp2_dcid *dcid;
const ngtcp2_ringbuf *rb = &dtr->bound.rb;
size_t i, len = ngtcp2_ringbuf_len(rb);
@@ -176,7 +176,7 @@ int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr,
static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound,
uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token) {
+ const ngtcp2_stateless_reset_token *token) {
const ngtcp2_dcid *dcid;
size_t i, len = ngtcp2_ringbuf_len(rb);
int rv;
@@ -196,9 +196,9 @@ static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound,
return 0;
}
-int ngtcp2_dcidtr_verify_token_uniqueness(const ngtcp2_dcidtr *dtr, int *pfound,
- uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token) {
+int ngtcp2_dcidtr_verify_token_uniqueness(
+ const ngtcp2_dcidtr *dtr, int *pfound, uint64_t seq, const ngtcp2_cid *cid,
+ const ngtcp2_stateless_reset_token *token) {
int rv;
rv = verify_token_uniqueness(&dtr->bound.rb, pfound, seq, cid, token);
@@ -406,7 +406,8 @@ ngtcp2_tstamp ngtcp2_dcidtr_earliest_retired_ts(const ngtcp2_dcidtr *dtr) {
}
void ngtcp2_dcidtr_push_unused(ngtcp2_dcidtr *dtr, uint64_t seq,
- const ngtcp2_cid *cid, const uint8_t *token) {
+ const ngtcp2_cid *cid,
+ const ngtcp2_stateless_reset_token *token) {
ngtcp2_dcid *dcid = ngtcp2_ringbuf_push_back(&dtr->unused.rb);
ngtcp2_dcid_init(dcid, seq, cid, token);
diff --git a/third_party/ngtcp2/lib/ngtcp2_dcidtr.h b/third_party/ngtcp2/lib/ngtcp2_dcidtr.h
index 63043427bc0..945ed12bae1 100644
--- a/third_party/ngtcp2/lib/ngtcp2_dcidtr.h
+++ b/third_party/ngtcp2/lib/ngtcp2_dcidtr.h
@@ -154,9 +154,9 @@ int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest,
* There is no Destination Connection ID that matches the given
* |path| and |token|.
*/
-int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr,
- const ngtcp2_path *path,
- const uint8_t *token);
+int ngtcp2_dcidtr_verify_stateless_reset(
+ const ngtcp2_dcidtr *dtr, const ngtcp2_path *path,
+ const ngtcp2_stateless_reset_token *token);
/*
* ngtcp2_dcidtr_verify_token_uniqueness verifies that the uniqueness
@@ -180,9 +180,9 @@ int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr,
* The given combination of values does not satisfy the above
* conditions.
*/
-int ngtcp2_dcidtr_verify_token_uniqueness(const ngtcp2_dcidtr *dtr, int *pfound,
- uint64_t seq, const ngtcp2_cid *cid,
- const uint8_t *token);
+int ngtcp2_dcidtr_verify_token_uniqueness(
+ const ngtcp2_dcidtr *dtr, int *pfound, uint64_t seq, const ngtcp2_cid *cid,
+ const ngtcp2_stateless_reset_token *token);
/*
* ngtcp2_dcidtr_retire_inactive_dcid_prior_to retires inactive
@@ -273,7 +273,8 @@ ngtcp2_tstamp ngtcp2_dcidtr_earliest_retired_ts(const ngtcp2_dcidtr *dtr);
* buffer space is full, the earliest ngtcp2_dcid is removed.
*/
void ngtcp2_dcidtr_push_unused(ngtcp2_dcidtr *dtr, uint64_t seq,
- const ngtcp2_cid *cid, const uint8_t *token);
+ const ngtcp2_cid *cid,
+ const ngtcp2_stateless_reset_token *token);
/*
* ngtcp2_dcidtr_pop_unused_cid_token removes an unused Destination
diff --git a/third_party/ngtcp2/lib/ngtcp2_frame_chain.c b/third_party/ngtcp2/lib/ngtcp2_frame_chain.c
index 0f6b06a788d..ec0839b7a4d 100644
--- a/third_party/ngtcp2/lib/ngtcp2_frame_chain.c
+++ b/third_party/ngtcp2/lib/ngtcp2_frame_chain.c
@@ -36,7 +36,7 @@ int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
return NGTCP2_ERR_NOMEM;
}
- ngtcp2_frame_chain_init(*pfrc);
+ ngtcp2_frame_chain_init(*pfrc, NGTCP2_FRAME_CHAIN_FLAG_NONE);
return 0;
}
@@ -48,7 +48,7 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
return NGTCP2_ERR_NOMEM;
}
- ngtcp2_frame_chain_init(*pfrc);
+ ngtcp2_frame_chain_init(*pfrc, NGTCP2_FRAME_CHAIN_FLAG_MALLOC);
return 0;
}
@@ -57,14 +57,26 @@ int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
size_t datacnt,
ngtcp2_objalloc *objalloc,
const ngtcp2_mem *mem) {
+ int rv;
+
if (datacnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) {
- return ngtcp2_frame_chain_extralen_new(pfrc,
- sizeof(ngtcp2_vec) * (datacnt - 1) -
- NGTCP2_FRAME_CHAIN_STREAM_AVAIL,
- mem);
+ rv = ngtcp2_frame_chain_extralen_new(
+ pfrc,
+ sizeof(ngtcp2_vec) * (datacnt - NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES),
+ mem);
+ } else {
+ rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
+ }
+
+ if (rv != 0) {
+ return rv;
}
- return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
+ (*pfrc)->fr.stream.data =
+ (ngtcp2_vec *)(void *)((uint8_t *)*pfrc +
+ offsetof(ngtcp2_frame_chain, buf));
+
+ return 0;
}
int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
@@ -87,9 +99,9 @@ int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
}
fr = &(*pfrc)->fr;
- fr->type = NGTCP2_FRAME_NEW_TOKEN;
+ fr->new_token.type = NGTCP2_FRAME_NEW_TOKEN;
- p = (uint8_t *)fr + sizeof(ngtcp2_new_token);
+ p = (uint8_t *)*pfrc + offsetof(ngtcp2_frame_chain, buf);
memcpy(p, token, tokenlen);
fr->new_token.token = p;
@@ -98,21 +110,6 @@ int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
return 0;
}
-void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) {
- ngtcp2_frame_chain_binder *binder;
-
- if (frc == NULL) {
- return;
- }
-
- binder = frc->binder;
- if (binder && --binder->refcount == 0) {
- ngtcp2_mem_free(mem, binder);
- }
-
- ngtcp2_mem_free(mem, frc);
-}
-
void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
ngtcp2_objalloc *objalloc,
const ngtcp2_mem *mem) {
@@ -122,39 +119,23 @@ void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
return;
}
- switch (frc->fr.type) {
- case NGTCP2_FRAME_CRYPTO:
- case NGTCP2_FRAME_STREAM:
- if (frc->fr.stream.datacnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) {
- ngtcp2_frame_chain_del(frc, mem);
-
- return;
- }
-
- break;
- case NGTCP2_FRAME_NEW_TOKEN:
- if (frc->fr.new_token.tokenlen > NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES) {
- ngtcp2_frame_chain_del(frc, mem);
-
- return;
- }
-
- break;
- }
-
binder = frc->binder;
if (binder && --binder->refcount == 0) {
ngtcp2_mem_free(mem, binder);
}
- frc->binder = NULL;
+ if (frc->flags & NGTCP2_FRAME_CHAIN_FLAG_MALLOC) {
+ ngtcp2_mem_free(mem, frc);
+ return;
+ }
ngtcp2_objalloc_frame_chain_release(objalloc, frc);
}
-void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) {
+void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc, uint32_t flags) {
frc->next = NULL;
frc->binder = NULL;
+ frc->flags = flags;
}
void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
diff --git a/third_party/ngtcp2/lib/ngtcp2_frame_chain.h b/third_party/ngtcp2/lib/ngtcp2_frame_chain.h
index e7b33632529..c73100dbcba 100644
--- a/third_party/ngtcp2/lib/ngtcp2_frame_chain.h
+++ b/third_party/ngtcp2/lib/ngtcp2_frame_chain.h
@@ -36,10 +36,10 @@
/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is
set. */
-#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00u
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00U
/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information
which a frame carries has been acknowledged. */
-#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01u
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01U
/*
* ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to
@@ -57,6 +57,25 @@ typedef struct ngtcp2_frame_chain_binder {
int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder,
const ngtcp2_mem *mem);
+/* NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES is the number of datacnt
+ that changes allocation method. If datacnt is more than this
+ value, ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
+ Otherwise, it is allocated using ngtcp2_objalloc. */
+#define NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES 4
+
+/* NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES is the length of a token that
+ changes allocation method. If the length is more than this value,
+ ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
+ Otherwise, it is allocated using ngtcp2_objalloc. */
+#define NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES \
+ (NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES * sizeof(ngtcp2_vec))
+
+/* NGTCP2_FRAME_CHAIN_FLAG_NONE indicates no flag is set. */
+#define NGTCP2_FRAME_CHAIN_FLAG_NONE 0x0
+/* NGTCP2_FRAME_CHAIN_FLAG_MALLOC indicates that ngtcp2_frame_chain is
+ allocated by ngtcp2_mem_malloc. */
+#define NGTCP2_FRAME_CHAIN_FLAG_MALLOC 0x1
+
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/*
@@ -67,7 +86,9 @@ struct ngtcp2_frame_chain {
struct {
ngtcp2_frame_chain *next;
ngtcp2_frame_chain_binder *binder;
+ uint32_t flags;
ngtcp2_frame fr;
+ uint8_t buf[sizeof(ngtcp2_vec) * NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES];
};
ngtcp2_opl_entry oplent;
@@ -90,10 +111,6 @@ ngtcp2_objalloc_decl(frame_chain, ngtcp2_frame_chain, oplent)
int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
const ngtcp2_mem *mem);
-/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that
- a ngtcp2_stream can include. */
-#define NGTCP2_MAX_STREAM_DATACNT 256
-
/*
* ngtcp2_frame_chain_objalloc_new allocates ngtcp2_frame_chain using
* |objalloc|.
@@ -108,26 +125,6 @@ int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
const ngtcp2_mem *mem);
-/* NGTCP2_FRAME_CHAIN_STREAM_AVAIL is the number of additional bytes
- available after ngtcp2_stream when it is embedded in
- ngtcp2_frame. */
-#define NGTCP2_FRAME_CHAIN_STREAM_AVAIL \
- (sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream))
-
-/* NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES is the number of datacnt
- that changes allocation method. If datacnt is more than this
- value, ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
- Otherwise, it is allocated using ngtcp2_objalloc. */
-#define NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES \
- (NGTCP2_FRAME_CHAIN_STREAM_AVAIL / sizeof(ngtcp2_vec) + 1)
-
-/* NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES is the length of a token that
- changes allocation method. If the length is more than this value,
- ngtcp2_frame_chain is allocated without ngtcp2_objalloc.
- Otherwise, it is allocated using ngtcp2_objalloc. */
-#define NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES \
- (sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token))
-
/*
* ngtcp2_frame_chain_stream_datacnt_objalloc_new allocates enough
* data to store additional |datacnt| - 1 ngtcp2_vec object after
@@ -135,8 +132,6 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
* words, |datacnt| <= NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES,
* ngtcp2_frame_chain_objalloc_new is called internally. Otherwise,
* ngtcp2_frame_chain_extralen_new is used and objalloc is not used.
- * Therefore, it is important to call ngtcp2_frame_chain_objalloc_del
- * without changing datacnt field.
*/
int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
size_t datacnt,
@@ -157,24 +152,19 @@ int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
const ngtcp2_mem *mem);
/*
- * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the
- * memory pointed by |frc|.
- */
-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|.
+ * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse
+ * if NGTCP2_FRAME_CHAIN_FLAG_MALLOC is not set in |frc|->flags.
+ * Otherwise, it deletes |frc|.
*/
void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
ngtcp2_objalloc *objalloc,
const ngtcp2_mem *mem);
/*
- * ngtcp2_frame_chain_init initializes |frc|.
+ * ngtcp2_frame_chain_init initializes |frc|. |flags| is bitwise-OR
+ * of zero or more of NGTCP2_FRAME_CHAIN_FLAG_*.
*/
-void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc);
+void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc, uint32_t flags);
/*
* ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain
diff --git a/third_party/ngtcp2/lib/ngtcp2_gaptr.c b/third_party/ngtcp2/lib/ngtcp2_gaptr.c
index d04b9634c20..267bd07225d 100644
--- a/third_party/ngtcp2/lib/ngtcp2_gaptr.c
+++ b/third_party/ngtcp2/lib/ngtcp2_gaptr.c
@@ -35,11 +35,11 @@ void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
}
static int gaptr_gap_init(ngtcp2_gaptr *gaptr) {
- ngtcp2_range range = {
- .end = UINT64_MAX,
- };
-
- return ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL);
+ return ngtcp2_ksl_insert(&gaptr->gap, NULL,
+ &(ngtcp2_range){
+ .end = UINT64_MAX,
+ },
+ NULL);
}
void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
@@ -116,10 +116,6 @@ uint64_t ngtcp2_gaptr_first_gap_offset(const ngtcp2_gaptr *gaptr) {
ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr,
uint64_t offset) {
- ngtcp2_range q = {
- .begin = offset,
- .end = offset + 1,
- };
ngtcp2_ksl_it it;
if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
@@ -129,7 +125,11 @@ ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr,
return r;
}
- it = ngtcp2_ksl_lower_bound_search(&gaptr->gap, &q,
+ it = ngtcp2_ksl_lower_bound_search(&gaptr->gap,
+ &(ngtcp2_range){
+ .begin = offset,
+ .end = offset + 1,
+ },
ngtcp2_ksl_range_exclusive_search);
assert(!ngtcp2_ksl_it_end(&it));
diff --git a/third_party/ngtcp2/lib/ngtcp2_ksl.c b/third_party/ngtcp2/lib/ngtcp2_ksl.c
index 22c131a1ac6..80000f42f9b 100644
--- a/third_party/ngtcp2/lib/ngtcp2_ksl.c
+++ b/third_party/ngtcp2/lib/ngtcp2_ksl.c
@@ -31,71 +31,78 @@
#include "ngtcp2_macro.h"
#include "ngtcp2_mem.h"
-#include "ngtcp2_range.h"
static ngtcp2_ksl_blk null_blk;
ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent)
-static size_t ksl_nodelen(size_t keylen) {
- assert(keylen >= sizeof(uint64_t));
-
- return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0x7u) &
- ~(uintptr_t)0x7u;
-}
-
-static size_t ksl_blklen(size_t nodelen) {
- return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK -
- sizeof(uint64_t);
+static size_t ksl_blklen(size_t aligned_keylen) {
+ return sizeof(ngtcp2_ksl_blk) + NGTCP2_KSL_MAX_NBLK * aligned_keylen;
}
/*
- * ksl_node_set_key sets |key| to |node|.
+ * ksl_set_nth_key sets |key| to |n|th node under |blk|.
*/
-static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
- const void *key) {
- memcpy(node->key, key, ksl->keylen);
+static void ksl_set_nth_key(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ size_t n, const ngtcp2_ksl_key *key) {
+ memcpy(blk->keys + n * ksl->aligned_keylen, key, ksl->keylen);
}
void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar,
ngtcp2_ksl_search search, size_t keylen,
const ngtcp2_mem *mem) {
- size_t nodelen = ksl_nodelen(keylen);
+ size_t aligned_keylen;
+
+ assert(keylen >= sizeof(uint64_t));
+
+ aligned_keylen = (keylen + 0x7U) & ~0x7U;
+
+ assert(aligned_keylen <= UINT16_MAX);
ngtcp2_objalloc_init(&ksl->blkalloc,
- (ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu, mem);
+ (ksl_blklen(aligned_keylen) + 0xFU) & ~(uintptr_t)0xFU,
+ mem);
- ksl->head = NULL;
+ ksl->root = NULL;
ksl->front = ksl->back = NULL;
ksl->compar = compar;
ksl->search = search;
ksl->n = 0;
ksl->keylen = keylen;
- ksl->nodelen = nodelen;
+ ksl->aligned_keylen = aligned_keylen;
}
static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) {
- return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc,
- ksl_blklen(ksl->nodelen));
+ ngtcp2_ksl_blk *blk = ngtcp2_objalloc_ksl_blk_len_get(
+ &ksl->blkalloc, ksl_blklen(ksl->aligned_keylen));
+
+ if (!blk) {
+ return NULL;
+ }
+
+ blk->keys = (uint8_t *)blk + sizeof(*blk);
+ blk->aligned_keylen = (uint16_t)ksl->aligned_keylen;
+
+ return blk;
}
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);
+static int ksl_root_init(ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_blk *root = ksl_blk_objalloc_new(ksl);
- if (!head) {
+ if (!root) {
return NGTCP2_ERR_NOMEM;
}
- head->next = head->prev = NULL;
- head->n = 0;
- head->leaf = 1;
+ root->next = root->prev = NULL;
+ root->n = 0;
+ root->leaf = 1;
- ksl->head = head;
- ksl->front = ksl->back = head;
+ ksl->root = root;
+ ksl->front = ksl->back = root;
return 0;
}
@@ -109,7 +116,7 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
if (!blk->leaf) {
for (i = 0; i < blk->n; ++i) {
- ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk);
+ ksl_free_blk(ksl, blk->nodes[i].blk);
}
}
@@ -118,12 +125,12 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
#endif /* defined(NOMEMPOOL) */
void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
- if (!ksl || !ksl->head) {
+ if (!ksl || !ksl->root) {
return;
}
#ifdef NOMEMPOOL
- ksl_free_blk(ksl, ksl->head);
+ ksl_free_blk(ksl, ksl->root);
#endif /* defined(NOMEMPOOL) */
ngtcp2_objalloc_free(&ksl->blkalloc);
@@ -160,8 +167,10 @@ static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
rblk->n = blk->n / 2;
blk->n -= rblk->n;
- memcpy(rblk->nodes, blk->nodes + ksl->nodelen * blk->n,
- ksl->nodelen * rblk->n);
+ memcpy(rblk->nodes, blk->nodes + blk->n, rblk->n * sizeof(ngtcp2_ksl_node));
+
+ memcpy(rblk->keys, blk->keys + blk->n * ksl->aligned_keylen,
+ rblk->n * ksl->aligned_keylen);
assert(blk->n >= NGTCP2_KSL_MIN_NBLK);
assert(rblk->n >= NGTCP2_KSL_MIN_NBLK);
@@ -181,32 +190,31 @@ static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
* Out of memory.
*/
static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
- ngtcp2_ksl_node *node;
- ngtcp2_ksl_blk *lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk, *rblk;
+ ngtcp2_ksl_blk *lblk = blk->nodes[i].blk, *rblk;
rblk = ksl_split_blk(ksl, lblk);
if (rblk == NULL) {
return NGTCP2_ERR_NOMEM;
}
- memmove(blk->nodes + (i + 2) * ksl->nodelen,
- blk->nodes + (i + 1) * ksl->nodelen,
- ksl->nodelen * (blk->n - (i + 1)));
+ memmove(blk->nodes + (i + 2), blk->nodes + (i + 1),
+ (blk->n - (i + 1)) * sizeof(ngtcp2_ksl_node));
+
+ memmove(blk->keys + (i + 1) * ksl->aligned_keylen,
+ blk->keys + i * ksl->aligned_keylen,
+ (blk->n - i) * ksl->aligned_keylen);
- node = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
- node->blk = rblk;
+ blk->nodes[i + 1].blk = rblk;
++blk->n;
- ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
- node = ngtcp2_ksl_nth_node(ksl, blk, i);
- ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ ksl_set_nth_key(ksl, blk, i, ngtcp2_ksl_blk_nth_key(lblk, lblk->n - 1));
return 0;
}
/*
- * ksl_split_head splits a head (root) block. It increases the height
- * of skip list by 1.
+ * ksl_split_root splits a root block. It increases the height of
+ * skip list by 1.
*
* It returns 0 if it succeeds, or one of the following negative error
* codes:
@@ -214,37 +222,34 @@ static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
* NGTCP2_ERR_NOMEM
* Out of memory.
*/
-static int ksl_split_head(ngtcp2_ksl *ksl) {
- ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
- ngtcp2_ksl_node *node;
+static int ksl_split_root(ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_blk *rblk = NULL, *lblk, *nroot = NULL;
- rblk = ksl_split_blk(ksl, ksl->head);
+ rblk = ksl_split_blk(ksl, ksl->root);
if (rblk == NULL) {
return NGTCP2_ERR_NOMEM;
}
- lblk = ksl->head;
+ lblk = ksl->root;
- nhead = ksl_blk_objalloc_new(ksl);
+ nroot = ksl_blk_objalloc_new(ksl);
- if (nhead == NULL) {
+ if (nroot == NULL) {
ksl_blk_objalloc_del(ksl, rblk);
return NGTCP2_ERR_NOMEM;
}
- nhead->next = nhead->prev = NULL;
- nhead->n = 2;
- nhead->leaf = 0;
+ nroot->next = nroot->prev = NULL;
+ nroot->n = 2;
+ nroot->leaf = 0;
- node = ngtcp2_ksl_nth_node(ksl, nhead, 0);
- ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
- node->blk = lblk;
+ ksl_set_nth_key(ksl, nroot, 0, ngtcp2_ksl_blk_nth_key(lblk, lblk->n - 1));
+ nroot->nodes[0].blk = lblk;
- node = ngtcp2_ksl_nth_node(ksl, nhead, 1);
- ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
- node->blk = rblk;
+ ksl_set_nth_key(ksl, nroot, 1, ngtcp2_ksl_blk_nth_key(rblk, rblk->n - 1));
+ nroot->nodes[1].blk = rblk;
- ksl->head = nhead;
+ ksl->root = nroot;
return 0;
}
@@ -257,16 +262,17 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
*/
static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i,
const ngtcp2_ksl_key *key, void *data) {
- ngtcp2_ksl_node *node;
-
assert(blk->n < NGTCP2_KSL_MAX_NBLK);
- memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
- ksl->nodelen * (blk->n - i));
+ memmove(blk->nodes + (i + 1), blk->nodes + i,
+ (blk->n - i) * sizeof(ngtcp2_ksl_node));
+
+ memmove(blk->keys + (i + 1) * ksl->aligned_keylen,
+ blk->keys + i * ksl->aligned_keylen,
+ (blk->n - i) * ksl->aligned_keylen);
- node = ngtcp2_ksl_nth_node(ksl, blk, i);
- ksl_node_set_key(ksl, node, key);
- node->data = data;
+ ksl_set_nth_key(ksl, blk, i, key);
+ blk->nodes[i].data = data;
++blk->n;
}
@@ -278,28 +284,27 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
size_t i;
int rv;
- if (!ksl->head) {
- rv = ksl_head_init(ksl);
+ if (!ksl->root) {
+ rv = ksl_root_init(ksl);
if (rv != 0) {
return rv;
}
}
- if (ksl->head->n == NGTCP2_KSL_MAX_NBLK) {
- rv = ksl_split_head(ksl);
+ if (ksl->root->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_root(ksl);
if (rv != 0) {
return rv;
}
}
- blk = ksl->head;
+ blk = ksl->root;
for (;;) {
i = ksl->search(ksl, blk, key);
if (blk->leaf) {
- if (i < blk->n &&
- !ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) {
+ if (i < blk->n && !ksl->compar(key, ngtcp2_ksl_blk_nth_key(blk, i))) {
if (it) {
*it = ngtcp2_ksl_end(ksl);
}
@@ -311,7 +316,7 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
++ksl->n;
if (it) {
- ngtcp2_ksl_it_init(it, ksl, blk, i);
+ ngtcp2_ksl_it_init(it, blk, i);
}
return 0;
@@ -320,17 +325,17 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
if (i == blk->n) {
/* This insertion extends the largest key in this subtree. */
for (; !blk->leaf;) {
- node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1);
+ node = &blk->nodes[blk->n - 1];
if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
rv = ksl_split_node(ksl, blk, blk->n - 1);
if (rv != 0) {
return rv;
}
- node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1);
+ node = &blk->nodes[blk->n - 1];
}
- ksl_node_set_key(ksl, node, key);
+ ksl_set_nth_key(ksl, blk, blk->n - 1, key);
blk = node->blk;
}
@@ -338,13 +343,13 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
++ksl->n;
if (it) {
- ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1);
+ ngtcp2_ksl_it_init(it, blk, blk->n - 1);
}
return 0;
}
- node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ node = &blk->nodes[i];
if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
rv = ksl_split_node(ksl, blk, i);
@@ -352,12 +357,8 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
return rv;
}
- if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) {
- node = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
-
- if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) {
- ksl_node_set_key(ksl, node, key);
- }
+ if (ksl->compar(ngtcp2_ksl_blk_nth_key(blk, i), key)) {
+ node = &blk->nodes[i + 1];
}
}
@@ -370,8 +371,12 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
* |i|.
*/
static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
- memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
- ksl->nodelen * (blk->n - (i + 1)));
+ memmove(blk->nodes + i, blk->nodes + (i + 1),
+ (blk->n - (i + 1)) * sizeof(ngtcp2_ksl_node));
+
+ memmove(blk->keys + i * ksl->aligned_keylen,
+ blk->keys + (i + 1) * ksl->aligned_keylen,
+ (blk->n - (i + 1)) * ksl->aligned_keylen);
--blk->n;
}
@@ -380,9 +385,9 @@ static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
* ksl_merge_node merges 2 nodes which are the nodes at the index of
* |i| and |i + 1|.
*
- * If |blk| is the head (root) block and it contains just 2 nodes
- * before merging nodes, the merged block becomes head block, which
- * decreases the height of |ksl| by 1.
+ * If |blk| is the root block and it contains just 2 nodes before
+ * merging nodes, the merged block becomes root block, which decreases
+ * the height of |ksl| by 1.
*
* This function returns the pointer to the merged block.
*/
@@ -393,15 +398,17 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
assert(i + 1 < blk->n);
- lnode = ngtcp2_ksl_nth_node(ksl, blk, i);
+ lnode = &blk->nodes[i];
lblk = lnode->blk;
- rblk = ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk;
+ rblk = blk->nodes[i + 1].blk;
- assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK);
+ assert(lblk->n + rblk->n <= NGTCP2_KSL_MAX_NBLK);
- memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
- ksl->nodelen * rblk->n);
+ memcpy(lblk->nodes + lblk->n, rblk->nodes, rblk->n * sizeof(ngtcp2_ksl_node));
+
+ memcpy(lblk->keys + lblk->n * ksl->aligned_keylen, rblk->keys,
+ rblk->n * ksl->aligned_keylen);
lblk->n += rblk->n;
lblk->next = rblk->next;
@@ -414,13 +421,12 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
ksl_blk_objalloc_del(ksl, rblk);
- if (ksl->head == blk && blk->n == 2) {
- ksl_blk_objalloc_del(ksl, ksl->head);
- ksl->head = lblk;
+ if (ksl->root == blk && blk->n == 2) {
+ ksl_blk_objalloc_del(ksl, ksl->root);
+ ksl->root = lblk;
} else {
ksl_remove_node(ksl, blk, i + 1);
- ksl_node_set_key(ksl, lnode,
- ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ ksl_set_nth_key(ksl, blk, i, ngtcp2_ksl_blk_nth_key(lblk, lblk->n - 1));
}
return lblk;
@@ -438,8 +444,8 @@ static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
assert(i > 0);
- lnode = ngtcp2_ksl_nth_node(ksl, blk, i - 1);
- rnode = ngtcp2_ksl_nth_node(ksl, blk, i);
+ lnode = &blk->nodes[i - 1];
+ rnode = &blk->nodes[i];
lblk = lnode->blk;
rblk = rnode->blk;
@@ -453,15 +459,20 @@ static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
assert(lblk->n <= NGTCP2_KSL_MAX_NBLK - n);
assert(rblk->n >= NGTCP2_KSL_MIN_NBLK + n);
- memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, ksl->nodelen * n);
+ memcpy(lblk->nodes + lblk->n, rblk->nodes, n * sizeof(ngtcp2_ksl_node));
+
+ memcpy(lblk->keys + lblk->n * ksl->aligned_keylen, rblk->keys,
+ n * ksl->aligned_keylen);
lblk->n += (uint32_t)n;
rblk->n -= (uint32_t)n;
- ksl_node_set_key(ksl, lnode,
- ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ ksl_set_nth_key(ksl, blk, i - 1, ngtcp2_ksl_blk_nth_key(lblk, lblk->n - 1));
+
+ memmove(rblk->nodes, rblk->nodes + n, rblk->n * sizeof(ngtcp2_ksl_node));
- memmove(rblk->nodes, rblk->nodes + ksl->nodelen * n, ksl->nodelen * rblk->n);
+ memmove(rblk->keys, rblk->keys + n * ksl->aligned_keylen,
+ rblk->n * ksl->aligned_keylen);
}
/*
@@ -476,8 +487,8 @@ static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
assert(i < blk->n - 1);
- lnode = ngtcp2_ksl_nth_node(ksl, blk, i);
- rnode = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+ lnode = &blk->nodes[i];
+ rnode = &blk->nodes[i + 1];
lblk = lnode->blk;
rblk = rnode->blk;
@@ -491,15 +502,20 @@ static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
assert(lblk->n >= NGTCP2_KSL_MIN_NBLK + n);
assert(rblk->n <= NGTCP2_KSL_MAX_NBLK - n);
- memmove(rblk->nodes + ksl->nodelen * n, rblk->nodes, ksl->nodelen * rblk->n);
+ memmove(rblk->nodes + n, rblk->nodes, rblk->n * sizeof(ngtcp2_ksl_node));
+
+ memmove(rblk->keys + n * ksl->aligned_keylen, rblk->keys,
+ rblk->n * ksl->aligned_keylen);
rblk->n += (uint32_t)n;
lblk->n -= (uint32_t)n;
- memcpy(rblk->nodes, lblk->nodes + ksl->nodelen * lblk->n, ksl->nodelen * n);
+ memcpy(rblk->nodes, lblk->nodes + lblk->n, n * sizeof(ngtcp2_ksl_node));
+
+ memcpy(rblk->keys, lblk->keys + lblk->n * ksl->aligned_keylen,
+ n * ksl->aligned_keylen);
- ksl_node_set_key(ksl, lnode,
- ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ ksl_set_nth_key(ksl, blk, i, ngtcp2_ksl_blk_nth_key(lblk, lblk->n - 1));
}
/*
@@ -516,9 +532,9 @@ 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);
+ assert(ksl->root);
- if (blk->n <= NGTCP2_KSL_MIN_NBLK) {
+ if (blk != ksl->root && blk->n == NGTCP2_KSL_MIN_NBLK) {
return ngtcp2_ksl_remove(ksl, it, key);
}
@@ -528,9 +544,9 @@ int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
if (it) {
if (hint->i == blk->n && blk->next) {
- ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
+ ngtcp2_ksl_it_init(it, blk->next, 0);
} else {
- ngtcp2_ksl_it_init(it, ksl, blk, hint->i);
+ ngtcp2_ksl_it_init(it, blk, hint->i);
}
}
@@ -539,7 +555,7 @@ int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
const ngtcp2_ksl_key *key) {
- ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_blk *blk = ksl->root;
ngtcp2_ksl_node *node;
size_t i;
@@ -548,8 +564,8 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
}
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) {
+ blk->nodes[0].blk->n == NGTCP2_KSL_MIN_NBLK &&
+ blk->nodes[1].blk->n == NGTCP2_KSL_MIN_NBLK) {
blk = ksl_merge_node(ksl, blk, 0);
}
@@ -565,7 +581,7 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
}
if (blk->leaf) {
- if (ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) {
+ if (ksl->compar(key, ngtcp2_ksl_blk_nth_key(blk, i))) {
if (it) {
*it = ngtcp2_ksl_end(ksl);
}
@@ -578,16 +594,16 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
if (it) {
if (blk->n == i && blk->next) {
- ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
+ ngtcp2_ksl_it_init(it, blk->next, 0);
} else {
- ngtcp2_ksl_it_init(it, ksl, blk, i);
+ ngtcp2_ksl_it_init(it, blk, i);
}
}
return 0;
}
- node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ node = &blk->nodes[i];
if (node->blk->n > NGTCP2_KSL_MIN_NBLK) {
blk = node->blk;
@@ -596,16 +612,14 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
assert(node->blk->n == NGTCP2_KSL_MIN_NBLK);
- if (i + 1 < blk->n &&
- ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ if (i + 1 < blk->n && blk->nodes[i + 1].blk->n > NGTCP2_KSL_MIN_NBLK) {
ksl_shift_left(ksl, blk, i + 1);
blk = node->blk;
continue;
}
- if (i > 0 &&
- ngtcp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ if (i > 0 && blk->nodes[i - 1].blk->n > NGTCP2_KSL_MIN_NBLK) {
ksl_shift_right(ksl, blk, i - 1);
blk = node->blk;
@@ -631,12 +645,12 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(const ngtcp2_ksl *ksl,
ngtcp2_ksl_it ngtcp2_ksl_lower_bound_search(const ngtcp2_ksl *ksl,
const ngtcp2_ksl_key *key,
ngtcp2_ksl_search search) {
- ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_blk *blk = ksl->root;
ngtcp2_ksl_it it;
size_t i;
if (!blk) {
- ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ ngtcp2_ksl_it_init(&it, &null_blk, 0);
return it;
}
@@ -649,7 +663,7 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_search(const ngtcp2_ksl *ksl,
i = 0;
}
- ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ ngtcp2_ksl_it_init(&it, blk, i);
return it;
}
@@ -657,7 +671,7 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_search(const ngtcp2_ksl *ksl,
if (i == blk->n) {
/* This happens if descendant has smaller key. Fast forward to
find last node in this subtree. */
- for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ for (; !blk->leaf; blk = blk->nodes[blk->n - 1].blk)
;
if (blk->next) {
@@ -667,39 +681,41 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_search(const ngtcp2_ksl *ksl,
i = blk->n;
}
- ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ ngtcp2_ksl_it_init(&it, blk, i);
return it;
}
- blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ blk = blk->nodes[i].blk;
}
}
void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
const ngtcp2_ksl_key *new_key) {
- ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_blk *blk = ksl->root;
ngtcp2_ksl_node *node;
+ const ngtcp2_ksl_key *node_key;
size_t i;
- assert(ksl->head);
+ assert(ksl->root);
for (;;) {
i = ksl->search(ksl, blk, old_key);
assert(i < blk->n);
- node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ node = &blk->nodes[i];
+ node_key = ngtcp2_ksl_blk_nth_key(blk, i);
if (blk->leaf) {
- assert(key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key));
- ksl_node_set_key(ksl, node, new_key);
+ assert(key_equal(ksl->compar, node_key, old_key));
+ ksl_set_nth_key(ksl, blk, i, new_key);
return;
}
- if (key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key) ||
- ksl->compar((ngtcp2_ksl_key *)node->key, new_key)) {
- ksl_node_set_key(ksl, node, new_key);
+ if (key_equal(ksl->compar, node_key, old_key) ||
+ ksl->compar(node_key, new_key)) {
+ ksl_set_nth_key(ksl, blk, i, new_key);
}
blk = node->blk;
@@ -709,15 +725,15 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
size_t ngtcp2_ksl_len(const ngtcp2_ksl *ksl) { return ksl->n; }
void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
- if (!ksl->head) {
+ if (!ksl->root) {
return;
}
#ifdef NOMEMPOOL
- ksl_free_blk(ksl, ksl->head);
+ ksl_free_blk(ksl, ksl->root);
#endif /* defined(NOMEMPOOL) */
- ksl->front = ksl->back = ksl->head = NULL;
+ ksl->front = ksl->back = ksl->root = NULL;
ksl->n = 0;
ngtcp2_objalloc_clear(&ksl->blkalloc);
@@ -727,14 +743,12 @@ void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
static void ksl_print(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
size_t level) {
size_t i;
- ngtcp2_ksl_node *node;
fprintf(stderr, "LV=%zu n=%u\n", level, blk->n);
if (blk->leaf) {
for (i = 0; i < blk->n; ++i) {
- node = ngtcp2_ksl_nth_node(ksl, blk, i);
- fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
+ fprintf(stderr, " %" PRId64, *(int64_t *)ngtcp2_ksl_blk_nth_key(blk, i));
}
fprintf(stderr, "\n");
@@ -743,26 +757,26 @@ static void ksl_print(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
}
for (i = 0; i < blk->n; ++i) {
- ksl_print(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk, level + 1);
+ ksl_print(ksl, blk->nodes[i].blk, level + 1);
}
}
void ngtcp2_ksl_print(const ngtcp2_ksl *ksl) {
- if (!ksl->head) {
+ if (!ksl->root) {
return;
}
- ksl_print(ksl, ksl->head, 0);
+ ksl_print(ksl, ksl->root, 0);
}
#endif /* !defined(WIN32) */
ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it it;
- if (ksl->head) {
- ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
+ if (ksl->root) {
+ ngtcp2_ksl_it_init(&it, ksl->front, 0);
} else {
- ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ ngtcp2_ksl_it_init(&it, &null_blk, 0);
}
return it;
@@ -771,18 +785,16 @@ ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it it;
- if (ksl->head) {
- ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+ if (ksl->root) {
+ ngtcp2_ksl_it_init(&it, ksl->back, ksl->back->n);
} else {
- ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ ngtcp2_ksl_it_init(&it, &null_blk, 0);
}
return it;
}
-void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
- ngtcp2_ksl_blk *blk, size_t i) {
- it->ksl = ksl;
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, ngtcp2_ksl_blk *blk, size_t i) {
it->blk = blk;
it->i = i;
}
@@ -802,12 +814,6 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) {
return it->i == 0 && it->blk->prev == NULL;
}
-int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs) {
- const ngtcp2_range *a = lhs, *b = rhs;
- return a->begin < b->begin;
-}
-
ngtcp2_ksl_search_def(range, ngtcp2_ksl_range_compar)
size_t ngtcp2_ksl_range_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
@@ -815,13 +821,6 @@ size_t ngtcp2_ksl_range_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
return ksl_range_search(ksl, blk, key);
}
-int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs) {
- const ngtcp2_range *a = lhs, *b = rhs;
- return a->begin < b->begin && !(ngtcp2_max_uint64(a->begin, b->begin) <
- ngtcp2_min_uint64(a->end, b->end));
-}
-
ngtcp2_ksl_search_def(range_exclusive, ngtcp2_ksl_range_exclusive_compar)
size_t ngtcp2_ksl_range_exclusive_search(const ngtcp2_ksl *ksl,
@@ -830,11 +829,6 @@ size_t ngtcp2_ksl_range_exclusive_search(const ngtcp2_ksl *ksl,
return ksl_range_exclusive_search(ksl, blk, key);
}
-int ngtcp2_ksl_uint64_less(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs) {
- return *(uint64_t *)lhs < *(uint64_t *)rhs;
-}
-
ngtcp2_ksl_search_def(uint64_less, ngtcp2_ksl_uint64_less)
size_t ngtcp2_ksl_uint64_less_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
@@ -842,11 +836,6 @@ size_t ngtcp2_ksl_uint64_less_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
return ksl_uint64_less_search(ksl, blk, key);
}
-int ngtcp2_ksl_int64_greater(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs) {
- return *(int64_t *)lhs > *(int64_t *)rhs;
-}
-
ngtcp2_ksl_search_def(int64_greater, ngtcp2_ksl_int64_greater)
size_t ngtcp2_ksl_int64_greater_search(const ngtcp2_ksl *ksl,
diff --git a/third_party/ngtcp2/lib/ngtcp2_ksl.h b/third_party/ngtcp2/lib/ngtcp2_ksl.h
index de78bcb8070..8024a360cdb 100644
--- a/third_party/ngtcp2/lib/ngtcp2_ksl.h
+++ b/third_party/ngtcp2/lib/ngtcp2_ksl.h
@@ -34,14 +34,15 @@
#include <ngtcp2/ngtcp2.h>
#include "ngtcp2_objalloc.h"
+#include "ngtcp2_range.h"
#define NGTCP2_KSL_DEGR 16
/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single
block can contain. */
-#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1)
+#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR)
/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single
block other than root must contain. */
-#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1)
+#define NGTCP2_KSL_MIN_NBLK NGTCP2_KSL_DEGR
/*
* ngtcp2_ksl_key represents key in ngtcp2_ksl.
@@ -55,22 +56,13 @@ typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk;
/*
* ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or
* opaque data. If a node is an internal node, it contains
- * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the
- * location starting at key.
+ * ngtcp2_ksl_blk. Otherwise, it has data.
*/
struct ngtcp2_ksl_node {
union {
ngtcp2_ksl_blk *blk;
void *data;
};
- union {
- uint64_t align;
- /* key is a buffer to include key associated to this node.
- Because the length of key is unknown until ngtcp2_ksl_init is
- called, the actual buffer will be allocated after this
- field. */
- uint8_t key[1];
- };
};
/*
@@ -84,19 +76,19 @@ struct ngtcp2_ksl_blk {
/* prev points to the previous block if leaf field is
nonzero. */
ngtcp2_ksl_blk *prev;
+ ngtcp2_ksl_node nodes[NGTCP2_KSL_MAX_NBLK];
+ /* keys is a pointer to the buffer to include
+ NGTCP2_KSL_MAX_NBLK keys. Because the length of key is
+ unknown until ngtcp2_ksl_init is called, the actual buffer
+ will be allocated after this object. */
+ uint8_t *keys;
/* n is the number of nodes this object contains in nodes. */
uint32_t n;
+ /* aligned_keylen is the length of the single key including
+ alignment. */
+ uint16_t aligned_keylen;
/* 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];
- };
+ uint8_t leaf;
};
ngtcp2_opl_entry oplent;
@@ -131,11 +123,10 @@ typedef size_t (*ngtcp2_ksl_search)(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
static size_t ksl_##NAME##_search( \
const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, const ngtcp2_ksl_key *key) { \
size_t i; \
- ngtcp2_ksl_node *node; \
+ uint8_t *node_key; \
\
- for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes; \
- i < blk->n && COMPAR((ngtcp2_ksl_key *)node->key, key); ++i, \
- node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) \
+ for (i = 0, node_key = blk->keys; i < blk->n && COMPAR(node_key, key); \
+ ++i, node_key += ksl->aligned_keylen) \
; \
\
return i; \
@@ -147,7 +138,6 @@ typedef struct ngtcp2_ksl_it ngtcp2_ksl_it;
* ngtcp2_ksl_it is a bidirectional iterator to iterate nodes.
*/
struct ngtcp2_ksl_it {
- const ngtcp2_ksl *ksl;
ngtcp2_ksl_blk *blk;
size_t i;
};
@@ -157,8 +147,8 @@ struct ngtcp2_ksl_it {
*/
struct ngtcp2_ksl {
ngtcp2_objalloc blkalloc;
- /* head points to the root block. */
- ngtcp2_ksl_blk *head;
+ /* root points to the root block. */
+ ngtcp2_ksl_blk *root;
/* front points to the first leaf block. */
ngtcp2_ksl_blk *front;
/* back points to the last leaf block. */
@@ -169,9 +159,7 @@ struct ngtcp2_ksl {
size_t n;
/* keylen is the size of key */
size_t keylen;
- /* nodelen is the actual size of ngtcp2_ksl_node including key
- storage. */
- size_t nodelen;
+ size_t aligned_keylen;
};
/*
@@ -290,10 +278,12 @@ size_t ngtcp2_ksl_len(const ngtcp2_ksl *ksl);
void ngtcp2_ksl_clear(ngtcp2_ksl *ksl);
/*
- * ngtcp2_ksl_nth_node returns the |n|th node under |blk|.
+ * ngtcp2_ksl_blk_nth_key returns the |n|th key under |blk|.
*/
-#define ngtcp2_ksl_nth_node(KSL, BLK, N) \
- ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+static inline const ngtcp2_ksl_key *
+ngtcp2_ksl_blk_nth_key(const ngtcp2_ksl_blk *blk, size_t n) {
+ return blk->keys + n * blk->aligned_keylen;
+}
#ifndef WIN32
/*
@@ -307,26 +297,28 @@ void ngtcp2_ksl_print(const ngtcp2_ksl *ksl);
/*
* ngtcp2_ksl_it_init initializes |it|.
*/
-void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
- ngtcp2_ksl_blk *blk, size_t i);
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, ngtcp2_ksl_blk *blk, size_t i);
/*
* ngtcp2_ksl_it_get returns the data associated to the node which
* |it| points to. It is undefined to call this function when
* ngtcp2_ksl_it_end(it) returns nonzero.
*/
-#define ngtcp2_ksl_it_get(IT) \
- ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
+static inline void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) {
+ return it->blk->nodes[it->i].data;
+}
/*
* ngtcp2_ksl_it_next advances the iterator by one. It is undefined
* if this function is called when ngtcp2_ksl_it_end(it) returns
* nonzero.
*/
-#define ngtcp2_ksl_it_next(IT) \
- (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
- ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
- : 0)
+static inline void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) {
+ if (++it->i == it->blk->n && it->blk->next) {
+ it->blk = it->blk->next;
+ it->i = 0;
+ }
+}
/*
* ngtcp2_ksl_it_prev moves backward the iterator by one. It is
@@ -339,8 +331,9 @@ void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it);
* ngtcp2_ksl_it_end returns nonzero if |it| points to the one beyond
* the last node.
*/
-#define ngtcp2_ksl_it_end(IT) \
- ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+static inline int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it) {
+ return it->blk->n == it->i && it->blk->next == NULL;
+}
/*
* ngtcp2_ksl_it_begin returns nonzero if |it| points to the first
@@ -354,8 +347,9 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it);
* It is undefined to call this function when ngtcp2_ksl_it_end(it)
* returns nonzero.
*/
-#define ngtcp2_ksl_it_key(IT) \
- ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+static inline const ngtcp2_ksl_key *ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) {
+ return ngtcp2_ksl_blk_nth_key(it->blk, it->i);
+}
/*
* ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar.
@@ -363,8 +357,12 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it);
* returns nonzero if ((const ngtcp2_range *)lhs)->begin < ((const
* ngtcp2_range *)rhs)->begin.
*/
-int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs);
+static inline int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = (const ngtcp2_range *)lhs,
+ *b = (const ngtcp2_range *)rhs;
+ return a->begin < b->begin;
+}
/*
* ngtcp2_ksl_range_search is an implementation of ngtcp2_ksl_search
@@ -380,8 +378,13 @@ size_t ngtcp2_ksl_range_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
* *)lhs)->begin < ((const ngtcp2_range *)rhs)->begin, and the 2
* ranges do not intersect.
*/
-int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs);
+static inline int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = (const ngtcp2_range *)lhs,
+ *b = (const ngtcp2_range *)rhs;
+ return a->begin < b->begin && !(ngtcp2_max_uint64(a->begin, b->begin) <
+ ngtcp2_min_uint64(a->end, b->end));
+}
/*
* ngtcp2_ksl_range_exclusive_search is an implementation of
@@ -396,8 +399,10 @@ size_t ngtcp2_ksl_range_exclusive_search(const ngtcp2_ksl *ksl,
* |lhs| and |rhs| must point to uint64_t objects, and the function
* returns nonzero if *(uint64_t *)|lhs| < *(uint64_t *)|rhs|.
*/
-int ngtcp2_ksl_uint64_less(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs);
+static inline int ngtcp2_ksl_uint64_less(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ return *(const uint64_t *)lhs < *(const uint64_t *)rhs;
+}
/*
* ngtcp2_ksl_uint64_less_search is an implementation of
@@ -411,8 +416,10 @@ size_t ngtcp2_ksl_uint64_less_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
* |lhs| and |rhs| must point to int64_t objects, and the function
* returns nonzero if *(int64_t *)|lhs| > *(int64_t *)|rhs|.
*/
-int ngtcp2_ksl_int64_greater(const ngtcp2_ksl_key *lhs,
- const ngtcp2_ksl_key *rhs);
+static inline int ngtcp2_ksl_int64_greater(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ return *(const int64_t *)lhs > *(const int64_t *)rhs;
+}
/*
* ngtcp2_ksl_int64_greater_search is an implementation of
diff --git a/third_party/ngtcp2/lib/ngtcp2_log.c b/third_party/ngtcp2/lib/ngtcp2_log.c
index fc4eb443517..191c64430b5 100644
--- a/third_party/ngtcp2/lib/ngtcp2_log.c
+++ b/third_party/ngtcp2/lib/ngtcp2_log.c
@@ -42,12 +42,12 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
ngtcp2_printf log_printf, ngtcp2_tstamp ts,
void *user_data) {
if (scid) {
- ngtcp2_encode_hex(log->scid, scid->data, scid->datalen);
+ ngtcp2_encode_hex_cstr(log->scid, scid->data, scid->datalen);
} else {
log->scid[0] = '\0';
}
log->log_printf = log_printf;
- log->events = 0xff;
+ log->events = 0xFF;
log->ts = log->last_ts = ts;
log->user_data = user_data;
}
@@ -90,23 +90,10 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
* Frame type in hex string.
*/
-#define NGTCP2_LOG_BUFLEN 4096
+#define NGTCP2_LOG_PKT "%s %" PRId64 " %s"
+#define NGTCP2_LOG_TP "remote transport_parameters"
-/* 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"
-#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)
-
-#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)
-
-#define NGTCP2_LOG_TP_HD_FIELDS \
- timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry"
+#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) (DIR), hd->pkt_num, strpkttype(hd)
static const char *strerrorcode(uint64_t error_code) {
switch (error_code) {
@@ -140,10 +127,14 @@ static const char *strerrorcode(uint64_t error_code) {
return "CRYPTO_BUFFER_EXCEEDED";
case NGTCP2_KEY_UPDATE_ERROR:
return "KEY_UPDATE_ERROR";
+ case NGTCP2_AEAD_LIMIT_REACHED:
+ return "AEAD_LIMIT_REACHED";
+ case NGTCP2_NO_VIABLE_PATH:
+ return "NO_VIABLE_PATH";
case NGTCP2_VERSION_NEGOTIATION_ERROR:
return "VERSION_NEGOTIATION_ERROR";
default:
- if (0x100u <= error_code && error_code <= 0x1ffu) {
+ if (0x100U <= error_code && error_code <= 0x1FFU) {
return "CRYPTO_ERROR";
}
return "(unknown)";
@@ -155,78 +146,41 @@ static const char *strapperrorcode(uint64_t app_error_code) {
return "(unknown)";
}
-static const char *strpkttype_long(uint8_t type) {
- switch (type) {
+static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
+ switch (hd->type) {
case NGTCP2_PKT_INITIAL:
return "Initial";
- case NGTCP2_PKT_RETRY:
- return "Retry";
- case NGTCP2_PKT_HANDSHAKE:
- return "Handshake";
case NGTCP2_PKT_0RTT:
return "0RTT";
- default:
- return "(unknown)";
- }
-}
-
-static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
- if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
- return strpkttype_long(hd->type);
- }
-
- switch (hd->type) {
+ case NGTCP2_PKT_HANDSHAKE:
+ return "Handshake";
+ case NGTCP2_PKT_RETRY:
+ return "Retry";
+ case NGTCP2_PKT_1RTT:
+ return "1RTT";
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) {
- switch (ev) {
- case NGTCP2_LOG_EVENT_CON:
- return "con";
- case NGTCP2_LOG_EVENT_PKT:
- return "pkt";
- case NGTCP2_LOG_EVENT_FRM:
- return "frm";
- case NGTCP2_LOG_EVENT_LDC:
- return "ldc";
- case NGTCP2_LOG_EVENT_CRY:
- return "cry";
- case NGTCP2_LOG_EVENT_PTV:
- return "ptv";
- case NGTCP2_LOG_EVENT_CCA:
- return "cca";
- case NGTCP2_LOG_EVENT_NONE:
- default:
- return "non";
- }
+ return strpkttype(&(ngtcp2_pkt_hd){
+ .type = type,
+ .flags = flags,
+ });
}
-static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; }
-
static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_stream *fr, const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " STREAM(0x%02" PRIx64 ") id=0x%" PRIx64
- " fin=%d offset=%" PRIu64 " len=%" PRIu64 " uni=%d"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id, fr->fin,
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " STREAM(0x%02" PRIx64 ") id=0x%" PRIx64
+ " fin=%d offset=%" PRIu64 " len=%" PRIu64 " uni=%d",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id, fr->fin,
fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt),
(fr->stream_id & 0x2) != 0);
}
@@ -236,58 +190,58 @@ static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
int64_t largest_ack, min_ack;
size_t i;
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") largest_ack=%" PRId64
- " ack_delay=%" PRIu64 "(%" PRIu64 ") ack_range_count=%zu"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack,
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") largest_ack=%" PRId64
+ " ack_delay=%" PRIu64 "(%" PRIu64 ") ack_range_count=%zu",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->largest_ack,
fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay, fr->rangecnt);
largest_ack = fr->largest_ack;
min_ack = fr->largest_ack - (int64_t)fr->first_ack_range;
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") range=[%" PRId64
- "..%" PRId64 "] len=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack,
- fr->first_ack_range);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") range=[%" PRId64
+ "..%" PRId64 "] len=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, largest_ack,
+ min_ack, fr->first_ack_range);
for (i = 0; i < fr->rangecnt; ++i) {
const ngtcp2_ack_range *range = &fr->ranges[i];
largest_ack = min_ack - (int64_t)range->gap - 2;
min_ack = largest_ack - (int64_t)range->len;
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") range=[%" PRId64
- "..%" PRId64 "] gap=%" PRIu64
- " len=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack,
- min_ack, range->gap, range->len);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") range=[%" PRId64
+ "..%" PRId64 "] gap=%" PRIu64
+ " len=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, largest_ack,
+ min_ack, range->gap, range->len);
}
if (fr->type == NGTCP2_FRAME_ACK_ECN) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") ect0=%" PRIu64
- " ect1=%" PRIu64 " ce=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->ecn.ect0,
- fr->ecn.ect1, fr->ecn.ce);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") ect0=%" PRIu64
+ " ect1=%" PRIu64 " ce=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->ecn.ect0,
+ fr->ecn.ect1, fr->ecn.ce);
}
}
static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_padding *fr, const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " PADDING(0x%02" PRIx64 ") len=%zu"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " PADDING(0x%02" PRIx64 ") len=%zu",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->len);
}
static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_reset_stream *fr,
const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " RESET_STREAM(0x%02" PRIx64 ") id=0x%" PRIx64
- " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " RESET_STREAM(0x%02" PRIx64 ") id=0x%" PRIx64
+ " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->stream_id,
strapperrorcode(fr->app_error_code), fr->app_error_code, fr->final_size);
}
@@ -297,189 +251,191 @@ static void log_fr_connection_close(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
char reason[256];
size_t reasonlen = ngtcp2_min_size(sizeof(reason) - 1, fr->reasonlen);
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " CONNECTION_CLOSE(0x%02" PRIx64
- ") error_code=%s(0x%" PRIx64 ") "
- "frame_type=%" PRIx64 " reason_len=%zu reason=[%s]"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " CONNECTION_CLOSE(0x%02" PRIx64 ") error_code=%s(0x%" PRIx64
+ ") "
+ "frame_type=0x%" PRIx64 " reason_len=%zu reason=[%s]",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type,
fr->type == NGTCP2_FRAME_CONNECTION_CLOSE ? strerrorcode(fr->error_code)
: strapperrorcode(fr->error_code),
fr->error_code, fr->frame_type, fr->reasonlen,
- ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen));
+ ngtcp2_encode_printable_ascii_cstr(reason, fr->reason, reasonlen));
}
static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_max_data *fr, const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " MAX_DATA(0x%02" PRIx64 ") max_data=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_data);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " MAX_DATA(0x%02" PRIx64
+ ") max_data=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->max_data);
}
static void log_fr_max_stream_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_max_stream_data *fr,
const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02" PRIx64
- ") id=0x%" PRIx64
- " max_stream_data=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
- fr->max_stream_data);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02" PRIx64
+ ") id=0x%" PRIx64
+ " max_stream_data=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->stream_id,
+ fr->max_stream_data);
}
static void log_fr_max_streams(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_max_streams *fr, const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " MAX_STREAMS(0x%02" PRIx64 ") max_streams=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams);
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " MAX_STREAMS(0x%02" PRIx64 ") max_streams=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->max_streams);
}
static void log_fr_ping(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_ping *fr, const char *dir) {
- log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PING(0x%02" PRIx64 ")"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " PING(0x%02" PRIx64 ")",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type);
}
static void log_fr_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_data_blocked *fr,
const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02" PRIx64 ") offset=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02" PRIx64
+ ") offset=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->offset);
}
static void log_fr_stream_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_stream_data_blocked *fr,
const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02" PRIx64
- ") id=0x%" PRIx64 " offset=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
- fr->offset);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02" PRIx64
+ ") id=0x%" PRIx64 " offset=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->stream_id,
+ fr->offset);
}
static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_streams_blocked *fr,
const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02" PRIx64 ") max_streams=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams);
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02" PRIx64 ") max_streams=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->max_streams);
}
static void log_fr_new_connection_id(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_new_connection_id *fr,
const char *dir) {
- uint8_t buf[sizeof(fr->stateless_reset_token) * 2 + 1];
- uint8_t cid[sizeof(fr->cid.data) * 2 + 1];
-
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02" PRIx64 ") seq=%" PRIu64
- " cid=0x%s retire_prior_to=%" PRIu64
- " stateless_reset_token=0x%s"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq,
- (const char *)ngtcp2_encode_hex(cid, fr->cid.data, fr->cid.datalen),
+ char buf[sizeof(fr->token.data) * 2 + 1];
+ char cid[sizeof(fr->cid.data) * 2 + 1];
+
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02" PRIx64 ") seq=%" PRIu64
+ " cid=0x%s retire_prior_to=%" PRIu64
+ " stateless_reset_token=0x%s",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->seq,
+ ngtcp2_encode_hex_cstr(cid, fr->cid.data, fr->cid.datalen),
fr->retire_prior_to,
- (const char *)ngtcp2_encode_hex(buf, fr->stateless_reset_token,
- sizeof(fr->stateless_reset_token)));
+ ngtcp2_encode_hex_cstr(buf, fr->token.data, sizeof(fr->token.data)));
}
static void log_fr_stop_sending(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_stop_sending *fr,
const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " STOP_SENDING(0x%02" PRIx64 ") id=0x%" PRIx64
- " app_error_code=%s(0x%" PRIx64 ")"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
- strapperrorcode(fr->app_error_code), fr->app_error_code);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " STOP_SENDING(0x%02" PRIx64
+ ") id=0x%" PRIx64
+ " app_error_code=%s(0x%" PRIx64 ")",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->stream_id,
+ strapperrorcode(fr->app_error_code), fr->app_error_code);
}
static void log_fr_path_challenge(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_path_challenge *fr,
const char *dir) {
- uint8_t buf[sizeof(fr->data) * 2 + 1];
+ char buf[sizeof(fr->data.data) * 2 + 1];
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02" PRIx64 ") data=0x%s"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
- (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data)));
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02" PRIx64 ") data=0x%s",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type,
+ ngtcp2_encode_hex_cstr(buf, fr->data.data, sizeof(fr->data.data)));
}
static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_path_response *fr,
const char *dir) {
- uint8_t buf[sizeof(fr->data) * 2 + 1];
+ char buf[sizeof(fr->data.data) * 2 + 1];
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02" PRIx64 ") data=0x%s"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
- (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data)));
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02" PRIx64 ") data=0x%s",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type,
+ ngtcp2_encode_hex_cstr(buf, fr->data.data, sizeof(fr->data.data)));
}
static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_stream *fr, const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " CRYPTO(0x%02" PRIx64 ") offset=%" PRIu64 " len=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset,
- ngtcp2_vec_len(fr->data, fr->datacnt));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " CRYPTO(0x%02" PRIx64 ") offset=%" PRIu64
+ " len=%" PRIu64,
+ NGTCP2_LOG_PKT_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,
const ngtcp2_new_token *fr, const char *dir) {
/* Show at most first 64 bytes of token. If token is longer than 64
bytes, log first 64 bytes and then append "*" */
- uint8_t buf[128 + 1 + 1];
- uint8_t *p;
+ char buf[128 + 1 + 1];
+ char *p;
if (fr->tokenlen > 64) {
- p = ngtcp2_encode_hex(buf, fr->token, 64);
+ p = ngtcp2_encode_hex_cstr(buf, fr->token, 64);
p[128] = '*';
p[129] = '\0';
} else {
- p = ngtcp2_encode_hex(buf, fr->token, fr->tokenlen);
+ p = ngtcp2_encode_hex_cstr(buf, fr->token, fr->tokenlen);
}
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02" PRIx64 ") token=0x%s len=%zu"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->tokenlen);
+
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " NEW_TOKEN(0x%02" PRIx64 ") token=0x%s len=%zu",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, p, fr->tokenlen);
}
static void log_fr_retire_connection_id(ngtcp2_log *log,
const ngtcp2_pkt_hd *hd,
const ngtcp2_retire_connection_id *fr,
const char *dir) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02" PRIx64 ") seq=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02" PRIx64
+ ") seq=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type, fr->seq);
}
static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_handshake_done *fr,
const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02" PRIx64 ")"),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02" PRIx64 ")",
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type);
}
static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_datagram *fr, const char *dir) {
- log->log_printf(log->user_data,
- (NGTCP2_LOG_PKT " DATAGRAM(0x%02" PRIx64 ") len=%" PRIu64),
- NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
- ngtcp2_vec_len(fr->data, fr->datacnt));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_FRM,
+ NGTCP2_LOG_PKT " DATAGRAM(0x%02" PRIx64 ") len=%" PRIu64,
+ NGTCP2_LOG_PKT_HD_FIELDS(dir), fr->type,
+ ngtcp2_vec_len(fr->data, fr->datacnt));
}
static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const ngtcp2_frame *fr, const char *dir) {
- switch (fr->type) {
+ switch (fr->hd.type) {
case NGTCP2_FRAME_STREAM:
log_fr_stream(log, hd, &fr->stream, dir);
break;
@@ -580,13 +536,13 @@ void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
}
for (i = 0; i < nsv; ++i) {
- log->log_printf(log->user_data, (NGTCP2_LOG_PKT " v=0x%08x"),
- NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_PKT, NGTCP2_LOG_PKT " v=0x%08x",
+ NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]);
}
}
-void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
- uint8_t buf[sizeof(sr->stateless_reset_token) * 2 + 1];
+void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset2 *sr) {
+ char buf[sizeof(sr->token.data) * 2 + 1];
ngtcp2_pkt_hd shd;
ngtcp2_pkt_hd *hd = &shd;
@@ -594,23 +550,22 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
return;
}
- memset(&shd, 0, sizeof(shd));
-
- shd.type = NGTCP2_PKT_STATELESS_RESET;
+ shd = (ngtcp2_pkt_hd){
+ .type = NGTCP2_PKT_STATELESS_RESET,
+ };
- log->log_printf(
- log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"),
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_PKT, NGTCP2_LOG_PKT " token=0x%s randlen=%zu",
NGTCP2_LOG_PKT_HD_FIELDS("rx"),
- (const char *)ngtcp2_encode_hex(buf, sr->stateless_reset_token,
- sizeof(sr->stateless_reset_token)),
+ ngtcp2_encode_hex_cstr(buf, sr->token.data, sizeof(sr->token.data)),
sr->randlen);
}
void ngtcp2_log_remote_tp(ngtcp2_log *log,
const ngtcp2_transport_params *params) {
- uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1];
- uint8_t addr[16 * 2 + 7 + 1];
- uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1];
+ char token[sizeof(params->stateless_reset_token) * 2 + 1];
+ char addr[16 * 2 + 7 + 1];
+ char cid[NGTCP2_MAX_CIDLEN * 2 + 1];
size_t i;
const ngtcp2_sockaddr_in *sa_in;
const ngtcp2_sockaddr_in6 *sa_in6;
@@ -622,127 +577,121 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log,
}
if (params->stateless_reset_token_present) {
- log->log_printf(
- log->user_data, (NGTCP2_LOG_TP " stateless_reset_token=0x%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_hex(token, params->stateless_reset_token,
- sizeof(params->stateless_reset_token)));
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_CRY, NGTCP2_LOG_TP " stateless_reset_token=0x%s",
+ ngtcp2_encode_hex_cstr(token, params->stateless_reset_token,
+ sizeof(params->stateless_reset_token)));
}
if (params->preferred_addr_present) {
if (params->preferred_addr.ipv4_present) {
sa_in = &params->preferred_addr.ipv4;
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " preferred_address.ipv4_addr=%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_ipv4(
- addr, (const uint8_t *)&sa_in->sin_addr));
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"),
- NGTCP2_LOG_TP_HD_FIELDS, ngtcp2_ntohs(sa_in->sin_port));
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " preferred_address.ipv4_addr=%s",
+ ngtcp2_encode_ipv4_cstr(addr, (const uint8_t *)&sa_in->sin_addr));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " preferred_address.ipv4_port=%u",
+ ngtcp2_ntohs(sa_in->sin_port));
}
if (params->preferred_addr.ipv6_present) {
sa_in6 = &params->preferred_addr.ipv6;
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " preferred_address.ipv6_addr=%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_ipv6(
- addr, (const uint8_t *)&sa_in6->sin6_addr));
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"),
- NGTCP2_LOG_TP_HD_FIELDS, ngtcp2_ntohs(sa_in6->sin6_port));
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " preferred_address.ipv6_addr=%s",
+ ngtcp2_encode_ipv6_cstr(addr, (const uint8_t *)&sa_in6->sin6_addr));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " preferred_address.ipv6_port=%u",
+ ngtcp2_ntohs(sa_in6->sin6_port));
}
- log->log_printf(
- log->user_data, (NGTCP2_LOG_TP " preferred_address.cid=0x%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_hex(cid, params->preferred_addr.cid.data,
- params->preferred_addr.cid.datalen));
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_hex(
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_CRY, NGTCP2_LOG_TP " preferred_address.cid=0x%s",
+ ngtcp2_encode_hex_cstr(cid, params->preferred_addr.cid.data,
+ params->preferred_addr.cid.datalen));
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s",
+ ngtcp2_encode_hex_cstr(
token, params->preferred_addr.stateless_reset_token,
sizeof(params->preferred_addr.stateless_reset_token)));
}
if (params->original_dcid_present) {
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_TP " original_destination_connection_id=0x%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_hex(cid, params->original_dcid.data,
- params->original_dcid.datalen));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP
+ " original_destination_connection_id=0x%s",
+ ngtcp2_encode_hex_cstr(cid, params->original_dcid.data,
+ params->original_dcid.datalen));
}
if (params->retry_scid_present) {
- log->log_printf(
- log->user_data, (NGTCP2_LOG_TP " retry_source_connection_id=0x%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_hex(cid, params->retry_scid.data,
- params->retry_scid.datalen));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " retry_source_connection_id=0x%s",
+ ngtcp2_encode_hex_cstr(cid, params->retry_scid.data,
+ params->retry_scid.datalen));
}
if (params->initial_scid_present) {
- log->log_printf(
- log->user_data, (NGTCP2_LOG_TP " initial_source_connection_id=0x%s"),
- NGTCP2_LOG_TP_HD_FIELDS,
- (const char *)ngtcp2_encode_hex(cid, params->initial_scid.data,
- params->initial_scid.datalen));
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " initial_source_connection_id=0x%s",
+ ngtcp2_encode_hex_cstr(cid, params->initial_scid.data,
+ params->initial_scid.datalen));
}
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local);
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni);
- log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " initial_max_streams_bidi=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " initial_max_streams_uni=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni);
- log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS,
- params->max_idle_timeout / NGTCP2_MILLISECONDS);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->max_udp_payload_size);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent);
- log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS,
- params->max_ack_delay / NGTCP2_MILLISECONDS);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64),
- NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " disable_active_migration=%d"),
- NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration);
- log->log_printf(log->user_data,
- (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64),
- 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);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP
+ " initial_max_stream_data_bidi_local=%" PRIu64,
+ params->initial_max_stream_data_bidi_local);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP
+ " initial_max_stream_data_bidi_remote=%" PRIu64,
+ params->initial_max_stream_data_bidi_remote);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64,
+ params->initial_max_stream_data_uni);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " initial_max_data=%" PRIu64,
+ params->initial_max_data);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " initial_max_streams_bidi=%" PRIu64,
+ params->initial_max_streams_bidi);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " initial_max_streams_uni=%" PRIu64,
+ params->initial_max_streams_uni);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64,
+ params->max_udp_payload_size);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64,
+ params->ack_delay_exponent);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " max_ack_delay=%" PRIu64,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64,
+ params->active_connection_id_limit);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " disable_active_migration=%d",
+ params->disable_active_migration);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64,
+ params->max_datagram_frame_size);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " grease_quic_bit=%d",
+ 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);
+ ngtcp2_log_infof_raw(log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP
+ " version_information.chosen_version=0x%08x",
+ params->version_info.chosen_version);
assert(!(params->version_info.available_versionslen & 0x3));
@@ -751,10 +700,10 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log,
i += sizeof(uint32_t)) {
p = ngtcp2_get_uint32be(&version, p);
- log->log_printf(
- log->user_data,
- (NGTCP2_LOG_TP " version_information.available_versions[%zu]=0x%08x"),
- NGTCP2_LOG_TP_HD_FIELDS, i >> 2, version);
+ ngtcp2_log_infof_raw(
+ log, NGTCP2_LOG_EVENT_CRY,
+ NGTCP2_LOG_TP " version_information.available_versions[%zu]=0x%08x",
+ i >> 2, version);
}
}
}
@@ -765,33 +714,33 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
return;
}
- ngtcp2_log_info(log, NGTCP2_LOG_EVENT_LDC,
- "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num,
- strpkttype_type_flags(type, flags), sent_ts);
+ ngtcp2_log_infof(log, NGTCP2_LOG_EVENT_LDC,
+ "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,
const char *dir) {
- uint8_t dcid[sizeof(hd->dcid.data) * 2 + 1];
- uint8_t scid[sizeof(hd->scid.data) * 2 + 1];
+ char dcid[sizeof(hd->dcid.data) * 2 + 1];
+ char scid[sizeof(hd->scid.data) * 2 + 1];
if (!log->log_printf || !(log->events & NGTCP2_LOG_EVENT_PKT)) {
return;
}
if (hd->type == NGTCP2_PKT_1RTT) {
- ngtcp2_log_info(
+ ngtcp2_log_infof(
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),
+ ngtcp2_encode_hex_cstr(dcid, hd->dcid.data, hd->dcid.datalen),
strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
} else {
- ngtcp2_log_info(
+ ngtcp2_log_infof(
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),
+ ngtcp2_encode_hex_cstr(dcid, hd->dcid.data, hd->dcid.datalen),
+ ngtcp2_encode_hex_cstr(scid, hd->scid.data, hd->scid.datalen),
hd->version, strpkttype(hd), hd->len);
}
}
@@ -804,31 +753,6 @@ void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
log_pkt_hd(log, hd, "tx");
}
-void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
- ...) {
- va_list ap;
- int n;
- char buf[NGTCP2_LOG_BUFLEN];
-
- if (!log->log_printf || !(log->events & ev)) {
- return;
- }
-
- va_start(ap, fmt);
- n = vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- if (n < 0 || (size_t)n >= sizeof(buf)) {
- return;
- }
-
- log->log_printf(log->user_data, (NGTCP2_LOG_HD " %s"),
- timestamp_cast(log->last_ts - log->ts), log->scid,
- strevent(ev), buf);
-}
-
-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", hd->pkt_num,
- strpkttype(hd));
+uint64_t ngtcp2_log_timestamp(const ngtcp2_log *log) {
+ return (log->last_ts - log->ts) / NGTCP2_MILLISECONDS;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_log.h b/third_party/ngtcp2/lib/ngtcp2_log.h
index 13fb81a72e1..b8bccb9d09e 100644
--- a/third_party/ngtcp2/lib/ngtcp2_log.h
+++ b/third_party/ngtcp2/lib/ngtcp2_log.h
@@ -49,7 +49,7 @@ typedef struct ngtcp2_log {
log_pritnf. */
void *user_data;
/* scid is SCID encoded as NULL-terminated hex string. */
- uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1];
+ char scid[NGTCP2_MAX_CIDLEN * 2 + 1];
} ngtcp2_log;
/**
@@ -107,7 +107,7 @@ void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
const uint32_t *sv, size_t nsv);
-void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr);
+void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset2 *sr);
void ngtcp2_log_remote_tp(ngtcp2_log *log,
const ngtcp2_transport_params *params);
@@ -119,14 +119,71 @@ void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
-void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
+#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s"
+
+uint64_t ngtcp2_log_timestamp(const ngtcp2_log *log);
+
+static inline const char *ngtcp2_log_event_str(ngtcp2_log_event ev) {
+ switch (ev) {
+ case NGTCP2_LOG_EVENT_CON:
+ return "con";
+ case NGTCP2_LOG_EVENT_PKT:
+ return "pkt";
+ case NGTCP2_LOG_EVENT_FRM:
+ return "frm";
+ case NGTCP2_LOG_EVENT_LDC:
+ return "ldc";
+ case NGTCP2_LOG_EVENT_CRY:
+ return "cry";
+ case NGTCP2_LOG_EVENT_PTV:
+ return "ptv";
+ case NGTCP2_LOG_EVENT_CCA:
+ return "cca";
+ case NGTCP2_LOG_EVENT_NONE:
+ default:
+ return "non";
+ }
+}
+
+#define ngtcp2_log_infof_raw(LOG, EV, FMT, ...) \
+ (LOG)->log_printf((LOG)->user_data, NGTCP2_LOG_HD " " FMT, \
+ ngtcp2_log_timestamp(LOG), (LOG)->scid, \
+ ngtcp2_log_event_str(EV), __VA_ARGS__);
/**
* @function
*
- * `ngtcp2_log_info` writes info level log.
+ * `ngtcp2_log_infof` writes info level log with printf like
+ * formatting.
*/
-void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
- ...);
+#define ngtcp2_log_infof(LOG, EV, FMT, ...) \
+ do { \
+ if (!(LOG)->log_printf || !((LOG)->events & (EV))) { \
+ break; \
+ } \
+ \
+ ngtcp2_log_infof_raw((LOG), (EV), FMT, __VA_ARGS__); \
+ } while (0)
+
+#define ngtcp2_log_info_raw(LOG, EV, FMT) \
+ (LOG)->log_printf((LOG)->user_data, NGTCP2_LOG_HD " " FMT, \
+ ngtcp2_log_timestamp(LOG), (LOG)->scid, \
+ ngtcp2_log_event_str(EV))
+
+/**
+ * @function
+ *
+ * `ngtcp2_log_info` writes info level log. FMT should not contain
+ * formatting directive. This function exists to workaround the issue
+ * that __VA_ARGS__ cannot be empty.
+ */
+#define ngtcp2_log_info(LOG, EV, FMT) \
+ do { \
+ if (!(LOG)->log_printf || !((LOG)->events & (EV))) { \
+ break; \
+ } \
+ \
+ ngtcp2_log_info_raw((LOG), (EV), FMT); \
+ } while (0)
#endif /* !defined(NGTCP2_LOG_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_macro.h b/third_party/ngtcp2/lib/ngtcp2_macro.h
index dfe5e0aed22..12cba12719a 100644
--- a/third_party/ngtcp2/lib/ngtcp2_macro.h
+++ b/third_party/ngtcp2/lib/ngtcp2_macro.h
@@ -52,6 +52,13 @@
*/
#define ngtcp2_arraylen(A) (sizeof(A) / sizeof(A[0]))
+/*
+ * ngtcp2_strlen_lit returns the length of string literal |S|. This
+ * macro assumes |S| is NULL-terminated string literal. It must not
+ * be used with pointers.
+ */
+#define ngtcp2_strlen_lit(S) (sizeof(S) - 1)
+
#define ngtcp2_max_def(SUFFIX, T) \
static inline T ngtcp2_max_##SUFFIX(T a, T b) { return a < b ? b : a; }
diff --git a/third_party/ngtcp2/lib/ngtcp2_map.c b/third_party/ngtcp2/lib/ngtcp2_map.c
index 5e4726e63ff..3ad4b9f1ef0 100644
--- a/third_party/ngtcp2/lib/ngtcp2_map.c
+++ b/third_party/ngtcp2/lib/ngtcp2_map.c
@@ -33,11 +33,11 @@
#define NGTCP2_INITIAL_HASHBITS 4
-void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
- map->mem = mem;
- map->hashbits = 0;
- map->table = NULL;
- map->size = 0;
+void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem) {
+ *map = (ngtcp2_map){
+ .mem = mem,
+ .seed = seed,
+ };
}
void ngtcp2_map_free(ngtcp2_map *map) {
@@ -45,30 +45,27 @@ void ngtcp2_map_free(ngtcp2_map *map) {
return;
}
- ngtcp2_mem_free(map->mem, map->table);
+ ngtcp2_mem_free(map->mem, map->keys);
}
int ngtcp2_map_each(const ngtcp2_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
int rv;
size_t i;
- ngtcp2_map_bucket *bkt;
size_t tablelen;
if (map->size == 0) {
return 0;
}
- tablelen = 1u << map->hashbits;
+ tablelen = (size_t)1 << map->hashbits;
for (i = 0; i < tablelen; ++i) {
- bkt = &map->table[i];
-
- if (bkt->data == NULL) {
+ if (map->psl[i] == 0) {
continue;
}
- rv = func(bkt->data, ptr);
+ rv = func(map->data[i], ptr);
if (rv != 0) {
return rv;
}
@@ -77,168 +74,228 @@ int ngtcp2_map_each(const ngtcp2_map *map, int (*func)(void *data, void *ptr),
return 0;
}
-static size_t hash(ngtcp2_map_key_type key, size_t bits) {
- return (size_t)((key * 11400714819323198485llu) >> (64 - bits));
-}
-
-static void map_bucket_swap(ngtcp2_map_bucket *a, ngtcp2_map_bucket *b) {
- ngtcp2_map_bucket c = *a;
-
- *a = *b;
- *b = c;
+/* Hasher from
+ https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
+ to maximize the output's sensitivity to all input bits. */
+#define NGTCP2_MAP_HASHER 0xF1357AEA2E62A9C5ULL
+/* 64-bit Fibonacci hashing constant, Golden Ratio constant, to get
+ the high bits with the good distribution. */
+#define NGTCP2_MAP_FIBO 0x9E3779B97F4A7C15ULL
+
+static size_t map_index(const ngtcp2_map *map, ngtcp2_map_key_type key) {
+ key += map->seed;
+ key *= NGTCP2_MAP_HASHER;
+ return (size_t)((key * NGTCP2_MAP_FIBO) >> (64 - map->hashbits));
}
#ifndef WIN32
void ngtcp2_map_print_distance(const ngtcp2_map *map) {
size_t i;
size_t idx;
- ngtcp2_map_bucket *bkt;
size_t tablelen;
if (map->size == 0) {
return;
}
- tablelen = 1u << map->hashbits;
+ tablelen = (size_t)1 << map->hashbits;
for (i = 0; i < tablelen; ++i) {
- bkt = &map->table[i];
-
- if (bkt->data == NULL) {
+ if (map->psl[i] == 0) {
fprintf(stderr, "@%zu <EMPTY>\n", i);
continue;
}
- idx = hash(bkt->key, map->hashbits);
- fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i,
- hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl);
+ idx = map_index(map, map->keys[i]);
+ fprintf(stderr, "@%zu key=%" PRIu64 " base=%zu distance=%u\n", i,
+ map->keys[i], idx, map->psl[i] - 1);
}
}
#endif /* !defined(WIN32) */
-static int insert(ngtcp2_map_bucket *table, size_t hashbits,
- ngtcp2_map_key_type key, void *data) {
- size_t idx = hash(key, hashbits);
- ngtcp2_map_bucket b = {
- .key = key,
- .data = data,
- };
- ngtcp2_map_bucket *bkt;
- size_t mask = (1u << hashbits) - 1;
+static void map_set_entry(ngtcp2_map *map, size_t idx, ngtcp2_map_key_type key,
+ void *data, size_t psl) {
+ map->keys[idx] = key;
+ map->data[idx] = data;
+ map->psl[idx] = (uint8_t)psl;
+}
+
+#define NGTCP2_SWAP(TYPE, A, B) \
+ do { \
+ TYPE t = (TYPE) * (A); \
+ \
+ *(A) = *(B); \
+ *(B) = t; \
+ } while (0)
+
+/*
+ * map_insert inserts |key| and |data| to |map|, and returns the index
+ * where the pair is stored if it succeeds. Otherwise, it returns one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * The another data associated to |key| is already present.
+ */
+static ngtcp2_ssize map_insert(ngtcp2_map *map, ngtcp2_map_key_type key,
+ void *data) {
+ size_t idx = map_index(map, key);
+ size_t mask = ((size_t)1 << map->hashbits) - 1;
+ size_t psl = 1;
+ size_t kpsl;
for (;;) {
- bkt = &table[idx];
+ kpsl = map->psl[idx];
- if (bkt->data == NULL) {
- *bkt = b;
- return 0;
+ if (kpsl == 0) {
+ map_set_entry(map, idx, key, data, psl);
+ ++map->size;
+
+ return (ngtcp2_ssize)idx;
}
- if (b.psl > bkt->psl) {
- map_bucket_swap(bkt, &b);
- } else if (bkt->key == key) {
- /* TODO This check is just a waste after first swap or if this
- function is called from map_resize. That said, there is no
- difference with or without this conditional in performance
- wise. */
+ if (psl > kpsl) {
+ NGTCP2_SWAP(ngtcp2_map_key_type, &key, &map->keys[idx]);
+ NGTCP2_SWAP(void *, &data, &map->data[idx]);
+ NGTCP2_SWAP(uint8_t, &psl, &map->psl[idx]);
+ } else if (map->keys[idx] == key) {
+ /* This check ensures that no duplicate keys are inserted. But
+ it is just a waste after first swap or if this function is
+ called from map_resize. That said, there is no difference
+ with or without this conditional in performance wise. */
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- ++b.psl;
+ ++psl;
idx = (idx + 1) & mask;
}
}
+/* NGTCP2_MAP_MAX_HASHBITS is the maximum number of bits used for hash
+ table. The theoretical limit of the maximum number of keys that
+ can be stored is 1 << NGTCP2_MAP_MAX_HASHBITS. */
+#define NGTCP2_MAP_MAX_HASHBITS (sizeof(size_t) * 8 - 1)
+
static int map_resize(ngtcp2_map *map, size_t new_hashbits) {
size_t i;
- ngtcp2_map_bucket *new_table;
- ngtcp2_map_bucket *bkt;
size_t tablelen;
- int rv;
- (void)rv;
+ ngtcp2_ssize idx;
+ ngtcp2_map new_map = {
+ .mem = map->mem,
+ .seed = map->seed,
+ .hashbits = new_hashbits,
+ };
+ void *buf;
+ (void)idx;
+
+ if (new_hashbits > NGTCP2_MAP_MAX_HASHBITS) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ tablelen = (size_t)1 << new_hashbits;
- new_table =
- ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, sizeof(ngtcp2_map_bucket));
- if (new_table == NULL) {
+ buf = ngtcp2_mem_calloc(map->mem, tablelen,
+ sizeof(ngtcp2_map_key_type) + sizeof(void *) +
+ sizeof(uint8_t));
+ if (buf == NULL) {
return NGTCP2_ERR_NOMEM;
}
+ new_map.keys = buf;
+ new_map.data =
+ (void *)((uint8_t *)new_map.keys + tablelen * sizeof(ngtcp2_map_key_type));
+ new_map.psl = (uint8_t *)new_map.data + tablelen * sizeof(void *);
+
if (map->size) {
- tablelen = 1u << map->hashbits;
+ tablelen = (size_t)1 << map->hashbits;
for (i = 0; i < tablelen; ++i) {
- bkt = &map->table[i];
- if (bkt->data == NULL) {
+ if (map->psl[i] == 0) {
continue;
}
- rv = insert(new_table, new_hashbits, bkt->key, bkt->data);
+ idx = map_insert(&new_map, map->keys[i], map->data[i]);
- assert(0 == rv);
+ /* map_insert must not fail because all keys are unique during
+ resize. */
+ assert(idx >= 0);
}
}
- ngtcp2_mem_free(map->mem, map->table);
+ ngtcp2_mem_free(map->mem, map->keys);
+ map->keys = new_map.keys;
+ map->data = new_map.data;
+ map->psl = new_map.psl;
map->hashbits = new_hashbits;
- map->table = new_table;
return 0;
}
+/* NGTCP2_MAX_PSL_RESIZE_THRESH is the maximum psl threshold. If
+ reached, resize the table. */
+#define NGTCP2_MAX_PSL_RESIZE_THRESH 128
+
int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
int rv;
+ size_t tablelen;
+ ngtcp2_ssize idx;
assert(data);
- /* Load factor is 0.75 */
- /* Under the very initial condition, that is map->size == 0 and
- map->hashbits == 0, 4 > 3 still holds nicely. */
- if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) {
- if (map->hashbits) {
- rv = map_resize(map, map->hashbits + 1);
- if (rv != 0) {
- return rv;
- }
- } else {
- rv = map_resize(map, NGTCP2_INITIAL_HASHBITS);
- if (rv != 0) {
- return rv;
- }
+ /* tablelen is incorrect if map->hashbits == 0 which leads to
+ tablelen = 1, but it is only used to check the load factor, and
+ it works in this special case. */
+ tablelen = (size_t)1 << map->hashbits;
+
+ /* Load factor is 7 / 8. Because tablelen is power of 2, (tablelen
+ - (tablelen >> 3)) computes tablelen * 7 / 8. */
+ if (map->size + 1 >= (tablelen - (tablelen >> 3))) {
+ rv = map_resize(map, map->hashbits ? map->hashbits + 1
+ : NGTCP2_INITIAL_HASHBITS);
+ if (rv != 0) {
+ return rv;
}
+
+ idx = map_insert(map, key, data);
+ if (idx < 0) {
+ return (int)idx;
+ }
+
+ return 0;
}
- rv = insert(map->table, map->hashbits, key, data);
- if (rv != 0) {
- return rv;
+ idx = map_insert(map, key, data);
+ if (idx < 0) {
+ return (int)idx;
}
- ++map->size;
+ /* Resize if psl reaches really large value which is almost
+ improbable, but just in case. */
+ if (map->psl[idx] - 1 < NGTCP2_MAX_PSL_RESIZE_THRESH) {
+ return 0;
+ }
- return 0;
+ return map_resize(map, map->hashbits + 1);
}
void *ngtcp2_map_find(const ngtcp2_map *map, ngtcp2_map_key_type key) {
size_t idx;
- ngtcp2_map_bucket *bkt;
- size_t psl = 0;
+ size_t psl = 1;
size_t mask;
if (map->size == 0) {
return NULL;
}
- idx = hash(key, map->hashbits);
- mask = (1u << map->hashbits) - 1;
+ idx = map_index(map, key);
+ mask = ((size_t)1 << map->hashbits) - 1;
for (;;) {
- bkt = &map->table[idx];
-
- if (bkt->data == NULL || psl > bkt->psl) {
+ if (psl > map->psl[idx]) {
return NULL;
}
- if (bkt->key == key) {
- return bkt->data;
+ if (map->keys[idx] == key) {
+ return map->data[idx];
}
++psl;
@@ -248,38 +305,36 @@ void *ngtcp2_map_find(const ngtcp2_map *map, ngtcp2_map_key_type key) {
int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) {
size_t idx;
- ngtcp2_map_bucket *b, *bkt;
- size_t psl = 0;
+ size_t dest;
+ size_t psl = 1, kpsl;
size_t mask;
if (map->size == 0) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- idx = hash(key, map->hashbits);
- mask = (1u << map->hashbits) - 1;
+ idx = map_index(map, key);
+ mask = ((size_t)1 << map->hashbits) - 1;
for (;;) {
- bkt = &map->table[idx];
-
- if (bkt->data == NULL || psl > bkt->psl) {
+ if (psl > map->psl[idx]) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- if (bkt->key == key) {
- b = bkt;
+ if (map->keys[idx] == key) {
+ dest = idx;
idx = (idx + 1) & mask;
for (;;) {
- bkt = &map->table[idx];
- if (bkt->data == NULL || bkt->psl == 0) {
- b->data = NULL;
+ kpsl = map->psl[idx];
+ if (kpsl <= 1) {
+ map->psl[dest] = 0;
break;
}
- --bkt->psl;
- *b = *bkt;
- b = bkt;
+ map_set_entry(map, dest, map->keys[idx], map->data[idx], kpsl - 1);
+
+ dest = idx;
idx = (idx + 1) & mask;
}
@@ -299,7 +354,7 @@ void ngtcp2_map_clear(ngtcp2_map *map) {
return;
}
- memset(map->table, 0, sizeof(*map->table) * (1u << map->hashbits));
+ memset(map->psl, 0, sizeof(*map->psl) * ((size_t)1 << map->hashbits));
map->size = 0;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_map.h b/third_party/ngtcp2/lib/ngtcp2_map.h
index 9d882fb2008..1afe3167e65 100644
--- a/third_party/ngtcp2/lib/ngtcp2_map.h
+++ b/third_party/ngtcp2/lib/ngtcp2_map.h
@@ -38,15 +38,15 @@
typedef uint64_t ngtcp2_map_key_type;
-typedef struct ngtcp2_map_bucket {
- uint32_t psl;
- ngtcp2_map_key_type key;
- void *data;
-} ngtcp2_map_bucket;
-
typedef struct ngtcp2_map {
- ngtcp2_map_bucket *table;
+ ngtcp2_map_key_type *keys;
+ void **data;
+ /* psl is the Probe Sequence Length. 0 has special meaning that the
+ element is not stored at i-th position if psl[i] == 0. Because
+ of this, the actual psl value is psl[i] - 1 if psl[i] > 0. */
+ uint8_t *psl;
const ngtcp2_mem *mem;
+ uint64_t seed;
size_t size;
size_t hashbits;
} ngtcp2_map;
@@ -54,7 +54,7 @@ typedef struct ngtcp2_map {
/*
* ngtcp2_map_init initializes the map |map|.
*/
-void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
+void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem);
/*
* ngtcp2_map_free deallocates any resources allocated for |map|. The
diff --git a/third_party/ngtcp2/lib/ngtcp2_net.h b/third_party/ngtcp2/lib/ngtcp2_net.h
index 103a2fb2d80..35a807f9fed 100644
--- a/third_party/ngtcp2/lib/ngtcp2_net.h
+++ b/third_party/ngtcp2/lib/ngtcp2_net.h
@@ -97,9 +97,9 @@ STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
uint32_t res;
unsigned char *p = (unsigned char *)&res;
*p++ = (unsigned char)(hostlong >> 24);
- *p++ = (hostlong >> 16) & 0xffu;
- *p++ = (hostlong >> 8) & 0xffu;
- *p = hostlong & 0xffu;
+ *p++ = (hostlong >> 16) & 0xFFU;
+ *p++ = (hostlong >> 8) & 0xFFU;
+ *p = hostlong & 0xFFU;
return res;
}
@@ -107,7 +107,7 @@ STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
uint16_t res;
unsigned char *p = (unsigned char *)&res;
*p++ = (unsigned char)(hostshort >> 8);
- *p = hostshort & 0xffu;
+ *p = hostshort & 0xFFU;
return res;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_objalloc.h b/third_party/ngtcp2/lib/ngtcp2_objalloc.h
index cf23de7b2b7..4b6df53857a 100644
--- a/third_party/ngtcp2/lib/ngtcp2_objalloc.h
+++ b/third_party/ngtcp2/lib/ngtcp2_objalloc.h
@@ -69,7 +69,7 @@ void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc);
inline static void ngtcp2_objalloc_##NAME##_init( \
ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
ngtcp2_objalloc_init( \
- objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ objalloc, ((sizeof(TYPE) + 0xFU) & ~(uintptr_t)0xFU) * nmemb, mem); \
} \
\
TYPE *ngtcp2_objalloc_##NAME##_get(ngtcp2_objalloc *objalloc); \
@@ -123,7 +123,7 @@ void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc);
inline static void ngtcp2_objalloc_##NAME##_init( \
ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
ngtcp2_objalloc_init( \
- objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ objalloc, ((sizeof(TYPE) + 0xFU) & ~(uintptr_t)0xFU) * nmemb, mem); \
} \
\
inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
diff --git a/third_party/ngtcp2/lib/ngtcp2_path.c b/third_party/ngtcp2/lib/ngtcp2_path.c
index 83238730033..c9636a8db9a 100644
--- a/third_party/ngtcp2/lib/ngtcp2_path.c
+++ b/third_party/ngtcp2/lib/ngtcp2_path.c
@@ -28,12 +28,6 @@
#include "ngtcp2_addr.h"
-void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
- const ngtcp2_addr *remote) {
- path->local = *local;
- path->remote = *remote;
-}
-
void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) {
ngtcp2_addr_copy(&dest->local, &src->local);
ngtcp2_addr_copy(&dest->remote, &src->remote);
diff --git a/third_party/ngtcp2/lib/ngtcp2_path.h b/third_party/ngtcp2/lib/ngtcp2_path.h
index a708378db32..9d4205cee22 100644
--- a/third_party/ngtcp2/lib/ngtcp2_path.h
+++ b/third_party/ngtcp2/lib/ngtcp2_path.h
@@ -31,14 +31,6 @@
#include <ngtcp2/ngtcp2.h>
-/*
- * ngtcp2_path_init initializes |path| with the given addresses. Note
- * that the buffer pointed by local->addr and remote->addr are not
- * copied. Their pointer values are assigned instead.
- */
-void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
- const ngtcp2_addr *remote);
-
/*
* ngtcp2_path_storage_init2 initializes |ps| using |path| as initial
* data.
diff --git a/third_party/ngtcp2/lib/ngtcp2_pcg.c b/third_party/ngtcp2/lib/ngtcp2_pcg.c
new file mode 100644
index 00000000000..ddaf94a779a
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_pcg.c
@@ -0,0 +1,88 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pcg.h"
+
+#include <assert.h>
+
+/*
+ * PCG implementation from
+ * https://github.com/imneme/pcg-c/blob/83252d9c23df9c82ecb42210afed61a7b42402d7/include/pcg_variants.h
+ *
+ * PCG Random Number Generation for C.
+ *
+ * Copyright 2014-2019 Melissa O'Neill <oneill@pcg-random.org>,
+ * and the PCG Project contributors.
+ *
+ * SPDX-License-Identifier: (Apache-2.0 OR MIT)
+ *
+ * Licensed under the Apache License, Version 2.0 (provided in
+ * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0)
+ * or under the MIT license (provided in LICENSE-MIT.txt and at
+ * http://opensource.org/licenses/MIT), at your option. This file may not
+ * be copied, modified, or distributed except according to those terms.
+ *
+ * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See your chosen license for details.
+ *
+ * For additional information about the PCG random number generation scheme,
+ * visit http://www.pcg-random.org/.
+ */
+
+#define NGTCP2_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL
+#define NGTCP2_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL
+
+static void pcg_oneseq_64_step_r(ngtcp2_pcg32 *pcg) {
+ pcg->state = pcg->state * NGTCP2_PCG_DEFAULT_MULTIPLIER_64 +
+ NGTCP2_PCG_DEFAULT_INCREMENT_64;
+}
+
+void ngtcp2_pcg32_init(ngtcp2_pcg32 *pcg, uint64_t seed) {
+ pcg->state = 0;
+ pcg_oneseq_64_step_r(pcg);
+ pcg->state += seed;
+ pcg_oneseq_64_step_r(pcg);
+}
+
+static uint32_t pcg_rotr_32(uint32_t value, unsigned int rot) {
+ return (value >> rot) | (value << ((32 - rot) & 31));
+}
+
+static uint32_t pcg_output_xsh_rr_64_32(uint64_t state) {
+ return pcg_rotr_32((uint32_t)(((state >> 18U) ^ state) >> 27U),
+ (unsigned int)(state >> 59U));
+}
+
+uint32_t ngtcp2_pcg32_rand(ngtcp2_pcg32 *pcg) {
+ uint64_t oldstate = pcg->state;
+
+ pcg_oneseq_64_step_r(pcg);
+
+ return pcg_output_xsh_rr_64_32(oldstate);
+}
+
+uint32_t ngtcp2_pcg32_rand_n(ngtcp2_pcg32 *pcg, uint32_t n) {
+ assert(n);
+ return (uint32_t)(((uint64_t)ngtcp2_pcg32_rand(pcg) * n) >> 32);
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_pcg.h b/third_party/ngtcp2/lib/ngtcp2_pcg.h
new file mode 100644
index 00000000000..a637183efc1
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_pcg.h
@@ -0,0 +1,54 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PCG_H
+#define NGTCP2_PCG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* defined(HAVE_CONFIG_H) */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_pcg32 {
+ uint64_t state;
+} ngtcp2_pcg32;
+
+/*
+ * ngtcp2_pcg32_init initializes |pcg| with |seed|.
+ */
+void ngtcp2_pcg32_init(ngtcp2_pcg32 *pcg, uint64_t seed);
+
+/*
+ * ngtcp2_pcg32_rand returns a random value in [0, UINT32_MAX].
+ */
+uint32_t ngtcp2_pcg32_rand(ngtcp2_pcg32 *pcg);
+
+/*
+ * ngtcp2_pcg32_rand_n returns a random value in [0, n). |n| must not
+ * be zero.
+ */
+uint32_t ngtcp2_pcg32_rand_n(ngtcp2_pcg32 *pcg, uint32_t n);
+
+#endif /* !defined(NGTCP2_PCG_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_pkt.c b/third_party/ngtcp2/lib/ngtcp2_pkt.c
index af8a059d08f..f86e65118df 100644
--- a/third_party/ngtcp2/lib/ngtcp2_pkt.c
+++ b/third_party/ngtcp2/lib/ngtcp2_pkt.c
@@ -33,7 +33,9 @@
#include "ngtcp2_cid.h"
#include "ngtcp2_mem.h"
#include "ngtcp2_vec.h"
+#include "ngtcp2_buf.h"
#include "ngtcp2_unreachable.h"
+#include "ngtcp2_pcg.h"
int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
const ngtcp2_pkt_info *pi, const uint8_t *pkt,
@@ -480,8 +482,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
return (ngtcp2_ssize)len;
}
-ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
- size_t payloadlen) {
+ngtcp2_ssize ngtcp2_frame_decoder_decode(ngtcp2_frame_decoder *frd,
+ ngtcp2_frame *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
uint8_t type;
if (payloadlen == 0) {
@@ -530,6 +534,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
payloadlen);
case NGTCP2_FRAME_ACK:
case NGTCP2_FRAME_ACK_ECN:
+ dest->ack.ranges = frd->buf.ack_ranges;
return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen);
case NGTCP2_FRAME_PATH_CHALLENGE:
return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge,
@@ -538,6 +543,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload,
payloadlen);
case NGTCP2_FRAME_CRYPTO:
+ dest->stream.data = &frd->buf.data;
return ngtcp2_pkt_decode_crypto_frame(&dest->stream, payload, payloadlen);
case NGTCP2_FRAME_NEW_TOKEN:
return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload,
@@ -550,14 +556,16 @@ ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
payload, payloadlen);
case NGTCP2_FRAME_DATAGRAM:
case NGTCP2_FRAME_DATAGRAM_LEN:
+ dest->datagram.data = &frd->buf.data;
return ngtcp2_pkt_decode_datagram_frame(&dest->datagram, payload,
payloadlen);
default:
if ((type & ~(NGTCP2_FRAME_STREAM - 1)) == NGTCP2_FRAME_STREAM) {
+ dest->stream.data = &frd->buf.data;
return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen);
}
- /* For frame types > 0xff, use ngtcp2_get_uvarintlen and
+ /* For frame types > 0xFF, use ngtcp2_get_uvarintlen and
ngtcp2_get_uvarint to get a frame type, and then switch over
it. Verify that payloadlen >= ngtcp2_get_uvarintlen(payload)
before calling ngtcp2_get_uvarint(payload). */
@@ -916,7 +924,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame(
return NGTCP2_ERR_FRAME_ENCODING;
}
- p = ngtcp2_get_uvarint(&vi, p);
+ ngtcp2_get_uvarint(&vi, p);
if (payloadlen - len < vi) {
return NGTCP2_ERR_FRAME_ENCODING;
}
@@ -1051,6 +1059,8 @@ ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest,
(void)payload;
(void)payloadlen;
+ assert(payloadlen > 0);
+
dest->type = NGTCP2_FRAME_PING;
return 1;
}
@@ -1198,8 +1208,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame(
++p;
ngtcp2_cid_init(&dest->cid, p, cil);
p += cil;
- p = ngtcp2_get_bytes(dest->stateless_reset_token, p,
- NGTCP2_STATELESS_RESET_TOKENLEN);
+ p = ngtcp2_get_bytes(dest->token.data, p, sizeof(dest->token.data));
assert((size_t)(p - payload) == len);
@@ -1258,7 +1267,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest,
p = payload + 1;
dest->type = NGTCP2_FRAME_PATH_CHALLENGE;
- ngtcp2_cpymem(dest->data, p, sizeof(dest->data));
+ ngtcp2_cpymem(dest->data.data, p, sizeof(dest->data.data));
p += sizeof(dest->data);
assert((size_t)(p - payload) == len);
@@ -1279,7 +1288,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest,
p = payload + 1;
dest->type = NGTCP2_FRAME_PATH_RESPONSE;
- ngtcp2_cpymem(dest->data, p, sizeof(dest->data));
+ ngtcp2_cpymem(dest->data.data, p, sizeof(dest->data.data));
p += sizeof(dest->data);
assert((size_t)(p - payload) == len);
@@ -1319,7 +1328,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_stream *dest,
return NGTCP2_ERR_FRAME_ENCODING;
}
- p = ngtcp2_get_uvarint(&vi, p);
+ ngtcp2_get_uvarint(&vi, p);
if (payloadlen - len < vi) {
return NGTCP2_ERR_FRAME_ENCODING;
}
@@ -1426,6 +1435,8 @@ ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest,
(void)payload;
(void)payloadlen;
+ assert(payloadlen > 0);
+
dest->type = NGTCP2_FRAME_HANDSHAKE_DONE;
return 1;
}
@@ -1440,9 +1451,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
size_t n;
uint64_t vi;
- if (payloadlen < len) {
- return NGTCP2_ERR_FRAME_ENCODING;
- }
+ assert(payloadlen > 0);
type = payload[0];
@@ -1481,16 +1490,13 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
dest->type = type;
- if (datalen == 0) {
- dest->datacnt = 0;
- dest->data = NULL;
- } else {
+ if (datalen) {
+ dest->data[0].len = datalen;
+ dest->data[0].base = (uint8_t *)p;
dest->datacnt = 1;
- dest->data = dest->rdata;
- dest->rdata[0].len = datalen;
-
- dest->rdata[0].base = (uint8_t *)p;
p += datalen;
+ } else {
+ dest->datacnt = 0;
}
assert((size_t)(p - payload) == len);
@@ -1500,7 +1506,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
ngtcp2_frame *fr) {
- switch (fr->type) {
+ switch (fr->hd.type) {
case NGTCP2_FRAME_STREAM:
return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream);
case NGTCP2_FRAME_ACK:
@@ -1876,7 +1882,7 @@ ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
const ngtcp2_new_connection_id *fr) {
size_t len = 1 + ngtcp2_put_uvarintlen(fr->seq) +
ngtcp2_put_uvarintlen(fr->retire_prior_to) + 1 +
- fr->cid.datalen + NGTCP2_STATELESS_RESET_TOKENLEN;
+ fr->cid.datalen + sizeof(fr->token.data);
uint8_t *p;
if (outlen < len) {
@@ -1890,8 +1896,7 @@ ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
p = ngtcp2_put_uvarint(p, fr->retire_prior_to);
*p++ = (uint8_t)fr->cid.datalen;
p = ngtcp2_cpymem(p, fr->cid.data, fr->cid.datalen);
- p = ngtcp2_cpymem(p, fr->stateless_reset_token,
- NGTCP2_STATELESS_RESET_TOKENLEN);
+ p = ngtcp2_cpymem(p, fr->token.data, sizeof(fr->token.data));
assert((size_t)(p - out) == len);
@@ -1933,7 +1938,7 @@ ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen,
p = out;
*p++ = NGTCP2_FRAME_PATH_CHALLENGE;
- p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data));
+ p = ngtcp2_cpymem(p, fr->data.data, sizeof(fr->data.data));
assert((size_t)(p - out) == len);
@@ -1953,7 +1958,7 @@ ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen,
p = out;
*p++ = NGTCP2_FRAME_PATH_RESPONSE;
- p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data));
+ p = ngtcp2_cpymem(p, fr->data.data, sizeof(fr->data.data));
assert((size_t)(p - out) == len);
@@ -2109,7 +2114,7 @@ ngtcp2_ssize ngtcp2_pkt_write_version_negotiation(
p = dest;
- *p++ = 0xc0 | unused_random;
+ *p++ = 0xC0 | unused_random;
p = ngtcp2_put_uint32be(p, 0);
*p++ = (uint8_t)dcidlen;
@@ -2146,20 +2151,20 @@ size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest,
return payloadlen / sizeof(uint32_t);
}
-int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr,
+int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset2 *sr,
const uint8_t *payload,
size_t payloadlen) {
const uint8_t *p = payload;
if (payloadlen <
- NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) {
+ NGTCP2_MIN_STATELESS_RESET_RANDLEN + sizeof(sr->token.data)) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
sr->rand = p;
- sr->randlen = payloadlen - NGTCP2_STATELESS_RESET_TOKENLEN;
+ sr->randlen = payloadlen - sizeof(sr->token.data);
p += sr->randlen;
- memcpy(sr->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN);
+ memcpy(sr->token.data, p, sizeof(sr->token.data));
return 0;
}
@@ -2238,10 +2243,21 @@ ngtcp2_ssize
ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen,
const uint8_t *stateless_reset_token,
const uint8_t *rand, size_t randlen) {
+ ngtcp2_stateless_reset_token token;
+
+ memcpy(token.data, stateless_reset_token, sizeof(token.data));
+
+ return ngtcp2_pkt_write_stateless_reset2(dest, destlen, &token, rand,
+ randlen);
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_write_stateless_reset2(uint8_t *dest, size_t destlen,
+ const ngtcp2_stateless_reset_token *token,
+ const uint8_t *rand, size_t randlen) {
uint8_t *p;
- if (destlen <
- NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) {
+ if (destlen < NGTCP2_MIN_STATELESS_RESET_RANDLEN + sizeof(token->data)) {
return NGTCP2_ERR_NOBUF;
}
@@ -2251,11 +2267,11 @@ ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen,
p = dest;
- randlen = ngtcp2_min_size(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen);
+ randlen = ngtcp2_min_size(destlen - sizeof(token->data), randlen);
p = ngtcp2_cpymem(p, rand, randlen);
- p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN);
- *dest = (uint8_t)((*dest & 0x7fu) | 0x40u);
+ p = ngtcp2_cpymem(p, token->data, sizeof(token->data));
+ *dest = (uint8_t)((*dest & 0x3FU) | 0x40U);
return p - dest;
}
@@ -2299,11 +2315,11 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
case NGTCP2_PROTO_VER_V1:
default:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
- noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V1);
break;
case NGTCP2_PROTO_VER_V2:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2;
- noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V2);
break;
}
@@ -2349,7 +2365,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry(
return NGTCP2_ERR_NOBUF;
}
- *p &= 0xf0;
+ *p &= 0xF0;
*p |= unused;
p += nwrite;
@@ -2389,11 +2405,11 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
case NGTCP2_PROTO_VER_V1:
default:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
- noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V1);
break;
case NGTCP2_PROTO_VER_V2:
nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2;
- noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ noncelen = ngtcp2_strlen_lit(NGTCP2_RETRY_NONCE_V2);
break;
}
@@ -2423,7 +2439,7 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
left -= n;
if (left > 8 + 1073741823 && len > 1073741823) {
- len = ngtcp2_min_uint64(len, 4611686018427387903lu);
+ len = ngtcp2_min_uint64(len, 4611686018427387903UL);
return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 8));
}
@@ -2454,7 +2470,7 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
if (left > 8 + 1073741823 && len > 1073741823) {
#if SIZE_MAX == UINT64_MAX
- len = ngtcp2_min_size(len, 4611686018427387903lu);
+ len = ngtcp2_min_size(len, 4611686018427387903UL);
#endif /* SIZE_MAX == UINT64_MAX */
return ngtcp2_min_size(len, left - 8);
}
@@ -2571,3 +2587,299 @@ int ngtcp2_pkt_verify_reserved_bits(uint8_t c) {
return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO;
}
+
+size_t ngtcp2_pkt_split_vec_rand(ngtcp2_vec *data, size_t datacnt,
+ uint64_t *offsets, ngtcp2_pcg32 *pcg,
+ size_t max_add) {
+ ngtcp2_vec *v;
+ size_t idx;
+ size_t len;
+
+ for (; max_add; --max_add) {
+ idx = ngtcp2_pcg32_rand_n(pcg, (uint32_t)datacnt);
+ assert(idx < datacnt);
+
+ v = &data[idx];
+
+ if (v->len <= 1) {
+ continue;
+ }
+
+ len = v->len / 2;
+
+ ngtcp2_vec_split_at(&data[datacnt], v, len);
+
+ offsets[datacnt] = offsets[idx] + len;
+
+ ++datacnt;
+ }
+
+ return datacnt;
+}
+
+size_t ngtcp2_pkt_split_vec_at(ngtcp2_vec *data, size_t datacnt,
+ uint64_t *offsets, size_t at) {
+ assert(at < data[0].len);
+
+ ngtcp2_vec_split_at(&data[datacnt], &data[0], at);
+
+ offsets[datacnt] = offsets[0] + at;
+
+ return datacnt + 1;
+}
+
+static int pkt_tls_skip8(ngtcp2_buf *buf) {
+ size_t len;
+
+ if (ngtcp2_buf_len(buf) < 1) {
+ return -1;
+ }
+
+ len = *buf->pos++;
+
+ if (ngtcp2_buf_len(buf) < len) {
+ return -1;
+ }
+
+ buf->pos += len;
+
+ return 0;
+}
+
+static int pkt_tls_skip16(ngtcp2_buf *buf) {
+ uint16_t len;
+
+ if (ngtcp2_buf_len(buf) < sizeof(len)) {
+ return -1;
+ }
+
+ buf->pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf->pos);
+
+ if (ngtcp2_buf_len(buf) < len) {
+ return -1;
+ }
+
+ buf->pos += len;
+
+ return 0;
+}
+
+int ngtcp2_pkt_find_server_name(ngtcp2_vec *server_name, const ngtcp2_vec *v) {
+ ngtcp2_buf buf;
+ uint32_t msglen;
+ uint16_t len;
+ uint16_t legacy_ver;
+ uint16_t ext_type;
+
+ assert(v->len);
+
+ ngtcp2_buf_init(&buf, v->base, v->len);
+ buf.last += v->len;
+
+ /* Handshake msg_type and length */
+ if (ngtcp2_buf_len(&buf) < 1 + 3) {
+ return 0;
+ }
+
+ /* Keep parsing only when msg_type is client_hello(1). */
+ if (*buf.pos++ != 1) {
+ return 0;
+ }
+
+ buf.pos = (uint8_t *)ngtcp2_get_uint24be(&msglen, buf.pos);
+
+ /* Truncate the buffer to msglen */
+ ngtcp2_buf_trunc(&buf, msglen);
+
+ /* legacy_version(0x0303) */
+ if (ngtcp2_buf_len(&buf) < sizeof(uint16_t)) {
+ return 0;
+ }
+
+ buf.pos = (uint8_t *)ngtcp2_get_uint16be(&legacy_ver, buf.pos);
+ if (legacy_ver != 0x0303) {
+ return 0;
+ }
+
+ /* random */
+ if (ngtcp2_buf_len(&buf) < 32) {
+ return 0;
+ }
+
+ buf.pos += 32;
+
+ /* legacy_session_id */
+ if (pkt_tls_skip8(&buf) != 0) {
+ return 0;
+ }
+
+ /* cipher_suites */
+ if (pkt_tls_skip16(&buf) != 0) {
+ return 0;
+ }
+
+ /* legacy_compression_methods */
+ if (pkt_tls_skip8(&buf) != 0) {
+ return 0;
+ }
+
+ /* extensions */
+ if (ngtcp2_buf_len(&buf) < sizeof(uint16_t)) {
+ return 0;
+ }
+
+ buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos);
+
+ /* Truncate the buffer to extensions length */
+ ngtcp2_buf_trunc(&buf, len);
+
+ for (;;) {
+ /* Verify that extension_type and length of extension_data are
+ available */
+ if (ngtcp2_buf_len(&buf) < sizeof(uint16_t) * 2) {
+ return 0;
+ }
+
+ /* extension_type */
+ buf.pos = (uint8_t *)ngtcp2_get_uint16be(&ext_type, buf.pos);
+ if (ext_type != 0) {
+ /* extension_data */
+ if (pkt_tls_skip16(&buf) != 0) {
+ return 0;
+ }
+
+ continue;
+ }
+
+ /* Server Name Indication extension(0) */
+
+ /* extension_data */
+ buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos);
+ if (ngtcp2_buf_len(&buf) < len || len < 2) {
+ return 0;
+ }
+
+ /* Truncate the buffer to extension_data length */
+ ngtcp2_buf_trunc(&buf, len);
+
+ /* server_name_list */
+ buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos);
+ if (ngtcp2_buf_len(&buf) < len || len < 1 + 2) {
+ return 0;
+ }
+
+ /* We deliberately do not check server_name_list length + 2 ==
+ extension_data length. They most likely match, and even if
+ not, no problem at all. */
+
+ /* Truncate the buffer to server_name_list length */
+ ngtcp2_buf_trunc(&buf, len);
+
+ /* name_type */
+ if (*buf.pos++ != 0) {
+ return 0;
+ }
+
+ /* name */
+ buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos);
+ if (ngtcp2_buf_len(&buf) < len) {
+ return 0;
+ }
+
+ server_name->base = buf.pos;
+ server_name->len = len;
+
+ return 1;
+ }
+}
+
+size_t ngtcp2_pkt_append_ping_and_padding(ngtcp2_vec *data, size_t datacnt,
+ ngtcp2_pcg32 *pcg, size_t n) {
+ uint32_t k;
+
+ for (; n && datacnt < NGTCP2_MAX_STREAM_DATACNT;) {
+ k = ngtcp2_pcg32_rand_n(pcg, (uint32_t)n + 1);
+ if (k == 0) {
+ /* PING */
+ data[datacnt] = (ngtcp2_vec){
+ .base = NULL,
+ .len = 0,
+ };
+
+ ++k;
+ } else {
+ /* PADDING of k length */
+ data[datacnt] = (ngtcp2_vec){
+ .base = NULL,
+ .len = k,
+ };
+ }
+
+ ++datacnt;
+ n -= k;
+ }
+
+ return datacnt;
+}
+
+void ngtcp2_pkt_permutate_vec(ngtcp2_vec *data, size_t datacnt,
+ uint64_t *offsets, ngtcp2_pcg32 *pcg) {
+ size_t i, j;
+ ngtcp2_vec v;
+ uint64_t o;
+
+ if (datacnt < 2) {
+ return;
+ }
+
+ for (i = datacnt - 1; i > 0; --i) {
+ j = ngtcp2_pcg32_rand_n(pcg, (uint32_t)i);
+
+ if (i == j) {
+ continue;
+ }
+
+ v = data[i];
+ data[i] = data[j];
+ data[j] = v;
+
+ o = offsets[i];
+ offsets[i] = offsets[j];
+ offsets[j] = o;
+ }
+}
+
+size_t ngtcp2_pkt_remove_vec_partial(ngtcp2_vec *removed_data, ngtcp2_vec *data,
+ size_t datacnt, uint64_t *offsets,
+ ngtcp2_pcg32 *pcg,
+ const ngtcp2_vec *part) {
+ ngtcp2_vec *v = &data[0];
+ size_t len;
+
+ assert(datacnt);
+ assert(v->base < part->base);
+ assert(ngtcp2_vec_end(part) <= ngtcp2_vec_end(v));
+
+ len = (size_t)(part->base - v->base) + part->len / 2;
+
+ ngtcp2_vec_split_at(removed_data, v, len);
+
+ if (removed_data->len == 1) {
+ return datacnt;
+ }
+
+ len = 1 + ngtcp2_pcg32_rand_n(
+ pcg, (uint32_t)ngtcp2_min_size(30, removed_data->len - 1));
+ assert(len < removed_data->len);
+
+ ngtcp2_vec_split_at(&data[datacnt], removed_data, len);
+
+ offsets[datacnt] = offsets[0] + v->len + removed_data->len;
+
+ return datacnt + 1;
+}
+
+int ngtcp2_stateless_reset_token_eq(const ngtcp2_stateless_reset_token *a,
+ const ngtcp2_stateless_reset_token *b) {
+ return memcmp(a->data, b->data, sizeof(a->data)) == 0;
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_pkt.h b/third_party/ngtcp2/lib/ngtcp2_pkt.h
index 756076e7a7f..bfdb065b74c 100644
--- a/third_party/ngtcp2/lib/ngtcp2_pkt.h
+++ b/third_party/ngtcp2/lib/ngtcp2_pkt.h
@@ -38,14 +38,14 @@
/* Long header specific macros */
#define NGTCP2_LONG_TYPE_MASK 0x30
-#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c
+#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0C
/* Short header specific macros */
#define NGTCP2_SHORT_RESERVED_BIT_MASK 0x18
#define NGTCP2_SHORT_KEY_PHASE_BIT 0x04
/* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */
-#define NGTCP2_SR_TYPE 0x1f
+#define NGTCP2_SR_TYPE 0x1F
/* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header.
That is (1|1|TT|RR|PP)<1> + VERSION<4> + DCIL<1> + SCIL<1> +
@@ -78,23 +78,23 @@
/* NGTCP2_MAX_SERVER_STREAM_ID_BIDI is the maximum bidirectional
server stream ID. */
-#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffdll)
+#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3FFFFFFFFFFFFFFDLL)
/* NGTCP2_MAX_CLIENT_STREAM_ID_BIDI is the maximum bidirectional
client stream ID. */
-#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffcll)
+#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3FFFFFFFFFFFFFFCLL)
/* NGTCP2_MAX_SERVER_STREAM_ID_UNI is the maximum unidirectional
server stream ID. */
-#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3fffffffffffffffll)
+#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3FFFFFFFFFFFFFFFLL)
/* NGTCP2_MAX_CLIENT_STREAM_ID_UNI is the maximum unidirectional
client stream ID. */
-#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3ffffffffffffffell)
+#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3FFFFFFFFFFFFFFELL)
/* NGTCP2_MAX_NUM_ACK_RANGES is the maximum number of Additional ACK
ranges which this library can create, or decode. */
#define NGTCP2_MAX_ACK_RANGES 32
/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */
-#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1))
+#define NGTCP2_MAX_PKT_NUM ((int64_t)((1LL << 62) - 1))
/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion to
hide/trigger Stateless Reset. */
@@ -103,10 +103,6 @@
/* 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 datagram
- 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
@@ -137,6 +133,19 @@
v2. */
#define NGTCP2_PKT_TYPE_RETRY_V2 0x0
+/* NGTCP2_MIN_STREAM_DATALEN is the minimum length of STREAM frame to
+ avoid too small frame. It is not always enforced for various
+ reasons. For example, due to flow control, we might have fewer
+ bytes available to send. Therefore, it is only applied when the
+ length of data to send is larger than this limit. */
+#define NGTCP2_MIN_STREAM_DATALEN 256
+
+/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that
+ a ngtcp2_stream can include. */
+#define NGTCP2_MAX_STREAM_DATACNT 256
+
+typedef struct ngtcp2_pcg32 ngtcp2_pcg32;
+
typedef struct ngtcp2_pkt_retry {
ngtcp2_cid odcid;
uint8_t *token;
@@ -163,14 +172,18 @@ typedef struct ngtcp2_pkt_retry {
#define NGTCP2_FRAME_STREAMS_BLOCKED_UNI 0x17
#define NGTCP2_FRAME_NEW_CONNECTION_ID 0x18
#define NGTCP2_FRAME_RETIRE_CONNECTION_ID 0x19
-#define NGTCP2_FRAME_PATH_CHALLENGE 0x1a
-#define NGTCP2_FRAME_PATH_RESPONSE 0x1b
-#define NGTCP2_FRAME_CONNECTION_CLOSE 0x1c
-#define NGTCP2_FRAME_CONNECTION_CLOSE_APP 0x1d
-#define NGTCP2_FRAME_HANDSHAKE_DONE 0x1e
+#define NGTCP2_FRAME_PATH_CHALLENGE 0x1A
+#define NGTCP2_FRAME_PATH_RESPONSE 0x1B
+#define NGTCP2_FRAME_CONNECTION_CLOSE 0x1C
+#define NGTCP2_FRAME_CONNECTION_CLOSE_APP 0x1D
+#define NGTCP2_FRAME_HANDSHAKE_DONE 0x1E
#define NGTCP2_FRAME_DATAGRAM 0x30
#define NGTCP2_FRAME_DATAGRAM_LEN 0x31
+typedef struct ngtcp2_frame_hd {
+ uint64_t type;
+} ngtcp2_frame_hd;
+
/* ngtcp2_stream represents STREAM and CRYPTO frames. */
typedef struct ngtcp2_stream {
uint64_t type;
@@ -182,7 +195,7 @@ typedef struct ngtcp2_stream {
uint8_t flags;
/* CRYPTO frame does not include this field, and must set it to
0. */
- uint8_t fin;
+ int fin;
/* CRYPTO frame does not include this field, and must set it to
0. */
int64_t stream_id;
@@ -191,8 +204,9 @@ typedef struct ngtcp2_stream {
the length of data is 1 in this definition, the library may
allocate extra bytes to hold more elements. */
size_t datacnt;
- /* data is the array of ngtcp2_vec which references data. */
- ngtcp2_vec data[1];
+ /* data points to ngtcp2_vec array which references data. If
+ datacnt == 0, this field may be NULL. */
+ ngtcp2_vec *data;
} ngtcp2_stream;
typedef struct ngtcp2_ack_range {
@@ -216,7 +230,7 @@ typedef struct ngtcp2_ack {
} ecn;
uint64_t first_ack_range;
size_t rangecnt;
- ngtcp2_ack_range ranges[1];
+ ngtcp2_ack_range *ranges;
} ngtcp2_ack;
typedef struct ngtcp2_padding {
@@ -286,7 +300,7 @@ typedef struct ngtcp2_new_connection_id {
uint64_t seq;
uint64_t retire_prior_to;
ngtcp2_cid cid;
- uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ ngtcp2_stateless_reset_token token;
} ngtcp2_new_connection_id;
typedef struct ngtcp2_stop_sending {
@@ -297,12 +311,12 @@ typedef struct ngtcp2_stop_sending {
typedef struct ngtcp2_path_challenge {
uint64_t type;
- uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
+ ngtcp2_path_challenge_data data;
} ngtcp2_path_challenge;
typedef struct ngtcp2_path_response {
uint64_t type;
- uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
+ ngtcp2_path_challenge_data data;
} ngtcp2_path_response;
typedef struct ngtcp2_new_token {
@@ -326,17 +340,13 @@ typedef struct ngtcp2_datagram {
uint64_t dgram_id;
/* datacnt is the number of elements that data contains. */
size_t datacnt;
- /* data is a pointer to ngtcp2_vec array that stores data. */
+ /* data is a pointer to ngtcp2_vec array that stores data. If
+ datacnt == 0, this field may be NULL.*/
ngtcp2_vec *data;
- /* rdata is conveniently embedded to ngtcp2_datagram, so that data
- field can just point to the address of this field to store a
- single vector which is the case when DATAGRAM is received from a
- remote endpoint. */
- ngtcp2_vec rdata[1];
} ngtcp2_datagram;
typedef union ngtcp2_frame {
- uint64_t type;
+ ngtcp2_frame_hd hd;
ngtcp2_stream stream;
ngtcp2_ack ack;
ngtcp2_padding padding;
@@ -357,11 +367,6 @@ typedef union ngtcp2_frame {
ngtcp2_retire_connection_id retire_connection_id;
ngtcp2_handshake_done handshake_done;
ngtcp2_datagram datagram;
- /* Extend ngtcp2_frame so that ngtcp2_stream has at least additional
- 3 ngtcp2_vec, totaling 4 slots, which can store HEADERS header,
- HEADERS payload, DATA header, and DATA payload in the standard
- sized ngtcp2_frame_chain. */
- uint8_t pad[sizeof(ngtcp2_stream) + sizeof(ngtcp2_vec) * 3];
} ngtcp2_frame;
typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain;
@@ -441,10 +446,22 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
const ngtcp2_pkt_hd *hd);
+/*
+ * ngtcp2_frame_decoder is QUIC frame decoder. For frames that
+ * require the external buffers (e.g., ngtcp2_stream and ngtcp2_ack),
+ * it provides those buffers on demand.
+ */
+typedef struct ngtcp2_frame_decoder {
+ union {
+ ngtcp2_vec data;
+ ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES];
+ } buf;
+} ngtcp2_frame_decoder;
+
/**
* @function
*
- * `ngtcp2_pkt_decode_frame` decodes a QUIC frame from the buffer
+ * `ngtcp2_frame_decoder_decode` decodes a QUIC frame from the buffer
* pointed by |payload| whose length is |payloadlen|.
*
* This function returns the number of bytes read to decode a single
@@ -454,8 +471,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
* Frame is badly formatted; or frame type is unknown; or
* |payloadlen| is 0.
*/
-ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
- size_t payloadlen);
+ngtcp2_ssize ngtcp2_frame_decoder_decode(ngtcp2_frame_decoder *frd,
+ ngtcp2_frame *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
/**
* @function
@@ -495,7 +514,7 @@ size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest,
* NGTCP2_ERR_INVALID_ARGUMENT
* Payloadlen is too short.
*/
-int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr,
+int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset2 *sr,
const uint8_t *payload,
size_t payloadlen);
@@ -1227,4 +1246,88 @@ uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type);
*/
uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c);
+/*
+ * ngtcp2_pkt_split_vec_rand appends ngtcp2_vec at most |max_add|
+ * times to the array pointed by |data| of length |datacnt| by
+ * splitting the existing ngtcp2_vec into two. Which ngtcp2_vec to
+ * split is chosen randomly. |offsets| contains the offset of each
+ * ngtcp2_vec pointed by |data|. |offsets| is also updated. The
+ * arrays must have the capacity at least |datacnt| + |max_add|.
+ * |pcg| is a random number generator.
+ *
+ * This function returns |datacnt| plus the number of ngtcp2_vec that
+ * are appended.
+ */
+size_t ngtcp2_pkt_split_vec_rand(ngtcp2_vec *data, size_t datacnt,
+ uint64_t *offsets, ngtcp2_pcg32 *pcg,
+ size_t max_add);
+
+/*
+ * ngtcp2_pkt_split_vec_at splits data[0] at offset |at|, and the
+ * right side of ngtcp2_vec is assigned to data[datacnt]. Similarly,
+ * offsets[0] + |at| is assigned to offsets[datacnt]. |data| must
+ * point to the array of ngtcp2_vec of length |datacnt|, and |datacnt|
+ * must be greater than 0. |at| must be strictly less than data->len.
+ *
+ * This function returns |datacnt| + 1.
+ */
+size_t ngtcp2_pkt_split_vec_at(ngtcp2_vec *data, size_t datacnt,
+ uint64_t *offsets, size_t at);
+
+/*
+ * ngtcp2_pkt_find_server_name searches TLS Server Name Indication
+ * extension in |v|. If it is found, assign the portion of server
+ * name to the object pointed by |server_name|, and returns nonzero.
+ * Otherwise, it returns 0. If |v| contains the extension partially,
+ * the function returns 0. |v| must not be empty.
+ */
+int ngtcp2_pkt_find_server_name(ngtcp2_vec *server_name, const ngtcp2_vec *v);
+
+/*
+ * ngtcp2_pkt_append_ping_and_padding appends PING and PADDING frames
+ * to the array pointed by |data| of length |datacnt|. The capacity
+ * of array must be at least NGTCP2_MAX_STREAM_DATACNT. |n| is the
+ * number of bytes available for serialized PING and PADDING frames.
+ * |pcg| is a random number generator. Which frames to add is
+ * determined randomly.
+ *
+ * The special encoding of PING and PADDING frames into ngtcp2_vec:
+ *
+ * - .base is NULL.
+ * - If .len is 0, it represents PING. Otherwise, PADDING of .len
+ * length.
+ *
+ * This function returns |datacnt| plus the number of frames added.
+ */
+size_t ngtcp2_pkt_append_ping_and_padding(ngtcp2_vec *data, size_t datacnt,
+ ngtcp2_pcg32 *pcg, size_t n);
+
+/*
+ * ngtcp2_pkt_permutate_vec permutates |data| and |offsets|, both have
+ * the |datacnt| elements. |pcg| is a random number generator.
+ */
+void ngtcp2_pkt_permutate_vec(ngtcp2_vec *data, size_t datacnt,
+ uint64_t *offsets, ngtcp2_pcg32 *pcg);
+
+/*
+ * ngtcp2_pkt_remove_vec_partial removes the portion of data that
+ * contains part of |part| from data[0]. This function does not
+ * remove whole range of |part|. The length of removed data is chosen
+ * randomly. The removed portion of data is assigned to the object
+ * pointed by |removed_data|. If there is data located after the
+ * removed data, it will be assigned to data[datacnt].
+ * offsets[datacnt] is also updated, and the function returns
+ * |datacnt| + 1. Otherwise, this function returns |datacnt|.
+ */
+size_t ngtcp2_pkt_remove_vec_partial(ngtcp2_vec *removed_data, ngtcp2_vec *data,
+ size_t datacnt, uint64_t *offsets,
+ ngtcp2_pcg32 *pcg, const ngtcp2_vec *part);
+
+/*
+ * ngtcp2_stateless_reset_token_eq returns nonzero if |a| and |b|
+ * share the same token.
+ */
+int ngtcp2_stateless_reset_token_eq(const ngtcp2_stateless_reset_token *a,
+ const ngtcp2_stateless_reset_token *b);
+
#endif /* !defined(NGTCP2_PKT_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_ppe.c b/third_party/ngtcp2/lib/ngtcp2_ppe.c
index 4d193125ae8..3054732db04 100644
--- a/third_party/ngtcp2/lib/ngtcp2_ppe.c
+++ b/third_party/ngtcp2/lib/ngtcp2_ppe.c
@@ -154,9 +154,9 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) {
p = buf->begin;
if (*p & NGTCP2_HEADER_FORM_BIT) {
- *p = (uint8_t)(*p ^ (mask[0] & 0x0f));
+ *p = (uint8_t)(*p ^ (mask[0] & 0x0F));
} else {
- *p = (uint8_t)(*p ^ (mask[0] & 0x1f));
+ *p = (uint8_t)(*p ^ (mask[0] & 0x1F));
}
p = buf->begin + ppe->pkt_num_offset;
diff --git a/third_party/ngtcp2/lib/ngtcp2_pv.c b/third_party/ngtcp2/lib/ngtcp2_pv.c
index 471f84c7644..972c27f395a 100644
--- a/third_party/ngtcp2/lib/ngtcp2_pv.c
+++ b/third_party/ngtcp2/lib/ngtcp2_pv.c
@@ -31,12 +31,16 @@
#include "ngtcp2_log.h"
#include "ngtcp2_macro.h"
#include "ngtcp2_addr.h"
+#include "ngtcp2_str.h"
-void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
+void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent,
+ const ngtcp2_path_challenge_data *data,
ngtcp2_tstamp expiry, uint8_t flags) {
- memcpy(pvent->data, data, sizeof(pvent->data));
- pvent->expiry = expiry;
- pvent->flags = flags;
+ *pvent = (ngtcp2_pv_entry){
+ .expiry = expiry,
+ .flags = flags,
+ .data = *data,
+ };
}
int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid,
@@ -70,7 +74,7 @@ void ngtcp2_pv_del(ngtcp2_pv *pv) {
ngtcp2_mem_free(pv->mem, pv);
}
-void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
+void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const ngtcp2_path_challenge_data *data,
ngtcp2_tstamp expiry, uint8_t flags,
ngtcp2_tstamp ts) {
ngtcp2_pv_entry *ent;
@@ -88,7 +92,8 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
--pv->probe_pkt_left;
}
-int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
+int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags,
+ const ngtcp2_path_challenge_data *data) {
size_t len = ngtcp2_ringbuf_len(&pv->ents.rb);
size_t i;
ngtcp2_pv_entry *ent;
@@ -99,7 +104,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.rb, i);
- if (memcmp(ent->data, data, sizeof(ent->data)) == 0) {
+ if (ngtcp2_cmemeq(ent->data.data, data->data, sizeof(ent->data.data))) {
*pflags = ent->flags;
ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated");
return 0;
diff --git a/third_party/ngtcp2/lib/ngtcp2_pv.h b/third_party/ngtcp2/lib/ngtcp2_pv.h
index 2d07e41648d..28bdf722b20 100644
--- a/third_party/ngtcp2/lib/ngtcp2_pv.h
+++ b/third_party/ngtcp2/lib/ngtcp2_pv.h
@@ -46,10 +46,10 @@ typedef struct ngtcp2_log ngtcp2_log;
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00u
+#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00U
/* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which
contains PATH_CHALLENGE is undersized (< 1200 bytes) */
-#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01u
+#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01U
typedef struct ngtcp2_pv_entry {
/* expiry is the timestamp when this PATH_CHALLENGE expires. */
@@ -57,30 +57,31 @@ typedef struct ngtcp2_pv_entry {
/* flags is zero or more of NGTCP2_PV_ENTRY_FLAG_*. */
uint8_t flags;
/* data is a byte string included in PATH_CHALLENGE. */
- uint8_t data[8];
+ ngtcp2_path_challenge_data data;
} ngtcp2_pv_entry;
-void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
+void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent,
+ const ngtcp2_path_challenge_data *data,
ngtcp2_tstamp expiry, uint8_t flags);
/* NGTCP2_PV_FLAG_NONE indicates no flag is set. */
-#define NGTCP2_PV_FLAG_NONE 0x00u
+#define NGTCP2_PV_FLAG_NONE 0x00U
/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path
validation should be ignored entirely. */
-#define NGTCP2_PV_FLAG_DONT_CARE 0x01u
+#define NGTCP2_PV_FLAG_DONT_CARE 0x01U
/* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is
cancelled. */
-#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u
+#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02U
/* NGTCP2_PV_FLAG_FALLBACK_PRESENT indicates that a fallback
Destination Connection ID and PTO are available in ngtcp2_pv. If
path validation fails, then fallback to them. If path validation
succeeds, the fallback Destination Connection ID is retired if it
is not zero length, and does not equal to the current Destination
Connection ID. */
-#define NGTCP2_PV_FLAG_FALLBACK_PRESENT 0x04u
+#define NGTCP2_PV_FLAG_FALLBACK_PRESENT 0x04U
/* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to
server's preferred address. This flag is only used by client. */
-#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u
+#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10U
typedef struct ngtcp2_pv ngtcp2_pv;
@@ -141,7 +142,7 @@ void ngtcp2_pv_del(ngtcp2_pv *pv);
* ngtcp2_pv_add_entry adds new entry with |data|. |expiry| is the
* expiry time of the entry.
*/
-void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
+void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const ngtcp2_path_challenge_data *data,
ngtcp2_tstamp expiry, uint8_t flags, ngtcp2_tstamp ts);
/*
@@ -164,7 +165,8 @@ int ngtcp2_pv_full(ngtcp2_pv *pv);
* NGTCP2_ERR_INVALID_ARGUMENT
* |pv| does not have an entry which has |data| and |path|
*/
-int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data);
+int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags,
+ const ngtcp2_path_challenge_data *data);
/*
* ngtcp2_pv_handle_entry_expiry checks expiry of existing entries.
diff --git a/third_party/ngtcp2/lib/ngtcp2_qlog.c b/third_party/ngtcp2/lib/ngtcp2_qlog.c
index c0f920746a4..42609481ec4 100644
--- a/third_party/ngtcp2/lib/ngtcp2_qlog.c
+++ b/third_party/ngtcp2/lib/ngtcp2_qlog.c
@@ -40,7 +40,7 @@ void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
qlog->user_data = user_data;
}
-#define write_verbatim(DEST, S) ngtcp2_cpymem((DEST), (S), sizeof(S) - 1)
+#define write_verbatim(DEST, S) ngtcp2_cpymem((DEST), (S), ngtcp2_strlen_lit(S))
static uint8_t *write_string_impl(uint8_t *p, const uint8_t *data,
size_t datalen) {
@@ -53,17 +53,11 @@ static uint8_t *write_string_impl(uint8_t *p, const uint8_t *data,
}
#define write_string(DEST, S) \
- write_string_impl((DEST), (const uint8_t *)(S), sizeof(S) - 1)
-
-#define NGTCP2_LOWER_XDIGITS "0123456789abcdef"
+ write_string_impl((DEST), (const uint8_t *)(S), ngtcp2_strlen_lit(S))
static uint8_t *write_hex(uint8_t *p, const uint8_t *data, size_t datalen) {
- const uint8_t *b = data, *end = data + datalen;
*p++ = '"';
- for (; b != end; ++b) {
- *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b >> 4];
- *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b & 0xf];
- }
+ p = ngtcp2_encode_hex(p, data, datalen);
*p++ = '"';
return p;
}
@@ -72,38 +66,19 @@ static uint8_t *write_cid(uint8_t *p, const ngtcp2_cid *cid) {
return write_hex(p, cid->data, cid->datalen);
}
-static uint8_t *write_number(uint8_t *p, uint64_t n) {
- size_t nlen = 0;
- uint64_t t;
- uint8_t *res;
-
- if (n == 0) {
- *p++ = '0';
- return p;
- }
- for (t = n; t; t /= 10, ++nlen)
- ;
- p += nlen;
- res = p;
- for (; n; n /= 10) {
- *--p = (uint8_t)((n % 10) + '0');
- }
- return res;
-}
-
static uint8_t *write_tstamp(uint8_t *p, ngtcp2_tstamp ts) {
- return write_number(p, ts / NGTCP2_MILLISECONDS);
+ return ngtcp2_encode_uint(p, ts / NGTCP2_MILLISECONDS);
}
static uint8_t *write_duration(uint8_t *p, ngtcp2_duration duration) {
- return write_number(p, duration / NGTCP2_MILLISECONDS);
+ return ngtcp2_encode_uint(p, duration / NGTCP2_MILLISECONDS);
}
static uint8_t *write_bool(uint8_t *p, int b) {
if (b) {
- return ngtcp2_cpymem(p, "true", sizeof("true") - 1);
+ return ngtcp2_cpymem(p, "true", ngtcp2_strlen_lit("true"));
}
- return ngtcp2_cpymem(p, "false", sizeof("false") - 1);
+ return ngtcp2_cpymem(p, "false", ngtcp2_strlen_lit("false"));
}
static uint8_t *write_pair_impl(uint8_t *p, const uint8_t *name, size_t namelen,
@@ -114,7 +89,8 @@ static uint8_t *write_pair_impl(uint8_t *p, const uint8_t *name, size_t namelen,
}
#define write_pair(DEST, NAME, VALUE) \
- write_pair_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, (VALUE))
+ write_pair_impl((DEST), (const uint8_t *)(NAME), ngtcp2_strlen_lit(NAME), \
+ (VALUE))
static uint8_t *write_pair_hex_impl(uint8_t *p, const uint8_t *name,
size_t namelen, const uint8_t *value,
@@ -125,19 +101,19 @@ static uint8_t *write_pair_hex_impl(uint8_t *p, const uint8_t *name,
}
#define write_pair_hex(DEST, NAME, VALUE, VALUELEN) \
- write_pair_hex_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
- (VALUE), (VALUELEN))
+ write_pair_hex_impl((DEST), (const uint8_t *)(NAME), \
+ ngtcp2_strlen_lit(NAME), (VALUE), (VALUELEN))
static uint8_t *write_pair_number_impl(uint8_t *p, const uint8_t *name,
size_t namelen, uint64_t value) {
p = write_string_impl(p, name, namelen);
*p++ = ':';
- return write_number(p, value);
+ return ngtcp2_encode_uint(p, value);
}
#define write_pair_number(DEST, NAME, VALUE) \
- write_pair_number_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
- (VALUE))
+ write_pair_number_impl((DEST), (const uint8_t *)(NAME), \
+ ngtcp2_strlen_lit(NAME), (VALUE))
static uint8_t *write_pair_duration_impl(uint8_t *p, const uint8_t *name,
size_t namelen,
@@ -148,8 +124,8 @@ static uint8_t *write_pair_duration_impl(uint8_t *p, const uint8_t *name,
}
#define write_pair_duration(DEST, NAME, VALUE) \
- write_pair_duration_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
- (VALUE))
+ write_pair_duration_impl((DEST), (const uint8_t *)(NAME), \
+ ngtcp2_strlen_lit(NAME), (VALUE))
static uint8_t *write_pair_tstamp_impl(uint8_t *p, const uint8_t *name,
size_t namelen, ngtcp2_tstamp ts) {
@@ -159,8 +135,8 @@ static uint8_t *write_pair_tstamp_impl(uint8_t *p, const uint8_t *name,
}
#define write_pair_tstamp(DEST, NAME, VALUE) \
- write_pair_tstamp_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
- (VALUE))
+ write_pair_tstamp_impl((DEST), (const uint8_t *)(NAME), \
+ ngtcp2_strlen_lit(NAME), (VALUE))
static uint8_t *write_pair_bool_impl(uint8_t *p, const uint8_t *name,
size_t namelen, int b) {
@@ -170,8 +146,8 @@ static uint8_t *write_pair_bool_impl(uint8_t *p, const uint8_t *name,
}
#define write_pair_bool(DEST, NAME, VALUE) \
- write_pair_bool_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
- (VALUE))
+ write_pair_bool_impl((DEST), (const uint8_t *)(NAME), \
+ ngtcp2_strlen_lit(NAME), (VALUE))
static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name,
size_t namelen, const ngtcp2_cid *cid) {
@@ -181,10 +157,10 @@ static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name,
}
#define write_pair_cid(DEST, NAME, VALUE) \
- write_pair_cid_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
- (VALUE))
+ write_pair_cid_impl((DEST), (const uint8_t *)(NAME), \
+ ngtcp2_strlen_lit(NAME), (VALUE))
-#define ngtcp2_make_vec_lit(S) {(uint8_t *)(S), sizeof((S)) - 1}
+#define ngtcp2_make_vec_lit(S) {(uint8_t *)(S), ngtcp2_strlen_lit((S))}
static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) {
p = write_verbatim(
@@ -218,7 +194,7 @@ void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) {
}
p = write_verbatim(
- p, "\x1e{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\",");
+ p, "\x1E{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\",");
p = write_trace(p, server, odcid);
p = write_verbatim(p, "}\n");
@@ -227,49 +203,41 @@ void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) {
}
void ngtcp2_qlog_end(ngtcp2_qlog *qlog) {
- uint8_t buf[1] = {0};
-
if (!qlog->write) {
return;
}
- qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, &buf, 0);
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, "", 0);
}
-static ngtcp2_vec vec_pkt_type_initial = ngtcp2_make_vec_lit("initial");
-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 =
+static const ngtcp2_vec vec_pkt_type_initial = ngtcp2_make_vec_lit("initial");
+static const ngtcp2_vec vec_pkt_type_handshake =
+ ngtcp2_make_vec_lit("handshake");
+static const ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT");
+static const ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT");
+static const ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry");
+static const ngtcp2_vec vec_pkt_type_version_negotiation =
ngtcp2_make_vec_lit("version_negotiation");
-static ngtcp2_vec vec_pkt_type_stateless_reset =
+static const 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 vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown");
static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
- if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
- switch (hd->type) {
- case NGTCP2_PKT_INITIAL:
- return &vec_pkt_type_initial;
- case NGTCP2_PKT_HANDSHAKE:
- return &vec_pkt_type_handshake;
- case NGTCP2_PKT_0RTT:
- return &vec_pkt_type_0rtt;
- case NGTCP2_PKT_RETRY:
- return &vec_pkt_type_retry;
- default:
- return &vec_pkt_type_unknown;
- }
- }
-
switch (hd->type) {
+ case NGTCP2_PKT_INITIAL:
+ return &vec_pkt_type_initial;
+ case NGTCP2_PKT_0RTT:
+ return &vec_pkt_type_0rtt;
+ case NGTCP2_PKT_HANDSHAKE:
+ return &vec_pkt_type_handshake;
+ case NGTCP2_PKT_RETRY:
+ return &vec_pkt_type_retry;
+ case NGTCP2_PKT_1RTT:
+ return &vec_pkt_type_1rtt;
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;
}
@@ -340,10 +308,10 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
min_ack = fr->largest_ack - (int64_t)fr->first_ack_range;
*p++ = '[';
- p = write_number(p, (uint64_t)min_ack);
+ p = ngtcp2_encode_uint(p, (uint64_t)min_ack);
if (largest_ack != min_ack) {
*p++ = ',';
- p = write_number(p, (uint64_t)largest_ack);
+ p = ngtcp2_encode_uint(p, (uint64_t)largest_ack);
}
*p++ = ']';
@@ -353,10 +321,10 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
min_ack = largest_ack - (int64_t)range->len;
*p++ = ',';
*p++ = '[';
- p = write_number(p, (uint64_t)min_ack);
+ p = ngtcp2_encode_uint(p, (uint64_t)min_ack);
if (largest_ack != min_ack) {
*p++ = ',';
- p = write_number(p, (uint64_t)largest_ack);
+ p = ngtcp2_encode_uint(p, (uint64_t)largest_ack);
}
*p++ = ']';
}
@@ -579,8 +547,7 @@ write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) {
*p++ = ',';
p = write_pair_cid(p, "connection_id", &fr->cid);
p = write_verbatim(p, ",\"stateless_reset_token\":{");
- p = write_pair_hex(p, "data", fr->stateless_reset_token,
- sizeof(fr->stateless_reset_token));
+ p = write_pair_hex(p, "data", fr->token.data, sizeof(fr->token.data));
*p++ = '}';
*p++ = '}';
@@ -610,7 +577,7 @@ static uint8_t *write_path_challenge_frame(uint8_t *p,
#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57
p = write_verbatim(p, "{\"frame_type\":\"path_challenge\",");
- p = write_pair_hex(p, "data", fr->data, sizeof(fr->data));
+ p = write_pair_hex(p, "data", fr->data.data, sizeof(fr->data.data));
*p++ = '}';
return p;
@@ -624,7 +591,7 @@ static uint8_t *write_path_response_frame(uint8_t *p,
#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56
p = write_verbatim(p, "{\"frame_type\":\"path_response\",");
- p = write_pair_hex(p, "data", fr->data, sizeof(fr->data));
+ p = write_pair_hex(p, "data", fr->data.data, sizeof(fr->data.data));
*p++ = '}';
return p;
@@ -694,7 +661,7 @@ static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) {
ngtcp2_buf_reset(&qlog->buf);
p = qlog->buf.last;
- *p++ = '\x1e';
+ *p++ = '\x1E';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(p, ",\"name\":");
@@ -738,7 +705,7 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
p = write_verbatim(p, "],\"header\":");
p = write_pkt_hd(p, hd);
p = write_verbatim(p, ",\"raw\":{\"length\":");
- p = write_number(p, pktlen);
+ p = ngtcp2_encode_uint(p, pktlen);
p = write_verbatim(p, "}}}\n");
qlog->buf.last = p;
@@ -754,7 +721,7 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
return;
}
- switch (fr->type) {
+ switch (fr->hd.type) {
case NGTCP2_FRAME_PADDING:
if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) {
return;
@@ -771,7 +738,7 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
case NGTCP2_FRAME_ACK_ECN:
if (ngtcp2_buf_left(&qlog->buf) <
NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD +
- (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN
+ (size_t)(fr->ack.type == NGTCP2_FRAME_ACK_ECN
? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD
: 0) +
NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.rangecnt) + 1) {
@@ -935,7 +902,7 @@ void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
void ngtcp2_qlog_parameters_set_transport_params(
ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server,
ngtcp2_qlog_side side) {
- uint8_t buf[1024];
+ uint8_t buf[2048];
uint8_t *p = buf;
const ngtcp2_preferred_addr *paddr;
const ngtcp2_sockaddr_in *sa_in;
@@ -945,7 +912,7 @@ void ngtcp2_qlog_parameters_set_transport_params(
return;
}
- *p++ = '\x1e';
+ *p++ = '\x1E';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(
@@ -1061,7 +1028,7 @@ void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
return;
}
- *p++ = '\x1e';
+ *p++ = '\x1E';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{");
@@ -1095,23 +1062,22 @@ void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) {
uint8_t buf[256];
uint8_t *p = buf;
- ngtcp2_pkt_hd hd = {0};
if (!qlog->write) {
return;
}
- *p++ = '\x1e';
+ *p++ = '\x1E';
*p++ = '{';
p = qlog_write_time(qlog, p);
p = write_verbatim(
p, ",\"name\":\"recovery:packet_lost\",\"data\":{\"header\":");
- hd.type = ent->hd.type;
- hd.flags = ent->hd.flags;
- hd.pkt_num = ent->hd.pkt_num;
-
- p = write_pkt_hd(p, &hd);
+ p = write_pkt_hd(p, &(ngtcp2_pkt_hd){
+ .pkt_num = ent->hd.pkt_num,
+ .type = ent->hd.type,
+ .flags = ent->hd.flags,
+ });
p = write_verbatim(p, "}}\n");
qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
@@ -1129,7 +1095,7 @@ void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
- *buf.last++ = '\x1e';
+ *buf.last++ = '\x1E';
*buf.last++ = '{';
buf.last = qlog_write_time(qlog, buf.last);
buf.last = write_verbatim(
@@ -1151,26 +1117,25 @@ void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
}
void ngtcp2_qlog_stateless_reset_pkt_received(
- ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) {
+ ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset2 *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++ = '\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, &(ngtcp2_pkt_hd){
+ .type = NGTCP2_PKT_STATELESS_RESET,
+ });
*p++ = ',';
- p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token,
- NGTCP2_STATELESS_RESET_TOKENLEN);
+ p = write_pair_hex(p, "stateless_reset_token", sr->token.data,
+ sizeof(sr->token.data));
p = write_verbatim(p, "}}\n");
qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
@@ -1192,7 +1157,7 @@ void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
- *buf.last++ = '\x1e';
+ *buf.last++ = '\x1E';
*buf.last++ = '{';
buf.last = qlog_write_time(qlog, buf.last);
buf.last = write_verbatim(
@@ -1201,8 +1166,8 @@ void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
buf.last = write_verbatim(buf.last, ",\"supported_versions\":[");
if (nsv) {
- if (ngtcp2_buf_left(&buf) <
- (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) {
+ if (ngtcp2_buf_left(&buf) < ngtcp2_strlen_lit("\"xxxxxxxx\",") * nsv - 1 +
+ ngtcp2_strlen_lit("]}}\n")) {
return;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_qlog.h b/third_party/ngtcp2/lib/ngtcp2_qlog.h
index d2a5f1038c0..17668a50233 100644
--- a/third_party/ngtcp2/lib/ngtcp2_qlog.h
+++ b/third_party/ngtcp2/lib/ngtcp2_qlog.h
@@ -147,7 +147,7 @@ void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
* 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 *qlog, const ngtcp2_pkt_stateless_reset2 *sr);
/*
* ngtcp2_qlog_version_negotiation_pkt_received writes packet_received
diff --git a/third_party/ngtcp2/lib/ngtcp2_range.c b/third_party/ngtcp2/lib/ngtcp2_range.c
index e8989153293..a949a657338 100644
--- a/third_party/ngtcp2/lib/ngtcp2_range.c
+++ b/third_party/ngtcp2/lib/ngtcp2_range.c
@@ -26,18 +26,22 @@
#include "ngtcp2_macro.h"
void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) {
- r->begin = begin;
- r->end = end;
+ *r = (ngtcp2_range){
+ .begin = begin,
+ .end = end,
+ };
}
ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a,
const ngtcp2_range *b) {
- ngtcp2_range r = {0};
+ ngtcp2_range r;
uint64_t begin = ngtcp2_max_uint64(a->begin, b->begin);
uint64_t end = ngtcp2_min_uint64(a->end, b->end);
if (begin < end) {
ngtcp2_range_init(&r, begin, end);
+ } else {
+ r = (ngtcp2_range){0};
}
return r;
@@ -52,10 +56,14 @@ int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b) {
void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right,
const ngtcp2_range *a, const ngtcp2_range *b) {
/* Assume that b is included in a */
- left->begin = a->begin;
- left->end = b->begin;
- right->begin = b->end;
- right->end = a->end;
+ *left = (ngtcp2_range){
+ .begin = a->begin,
+ .end = b->begin,
+ };
+ *right = (ngtcp2_range){
+ .begin = b->end,
+ .end = a->end,
+ };
}
int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b) {
diff --git a/third_party/ngtcp2/lib/ngtcp2_ratelim.c b/third_party/ngtcp2/lib/ngtcp2_ratelim.c
new file mode 100644
index 00000000000..d13ed9a14aa
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_ratelim.c
@@ -0,0 +1,84 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ * Copyright (c) 2023 nghttp2 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_ratelim.h"
+
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+void ngtcp2_ratelim_init(ngtcp2_ratelim *rlim, uint64_t burst, uint64_t rate,
+ ngtcp2_tstamp ts) {
+ *rlim = (ngtcp2_ratelim){
+ .burst = burst,
+ .rate = rate,
+ .tokens = burst,
+ .ts = ts,
+ };
+}
+
+/* ratelim_update updates rlim->tokens with the current |ts|. */
+static void ratelim_update(ngtcp2_ratelim *rlim, ngtcp2_tstamp ts) {
+ uint64_t d, gain, gps;
+
+ assert(ts >= rlim->ts);
+
+ if (ts == rlim->ts) {
+ return;
+ }
+
+ d = ts - rlim->ts;
+ rlim->ts = ts;
+
+ if (rlim->rate > (UINT64_MAX - rlim->carry) / d) {
+ gain = UINT64_MAX;
+ } else {
+ gain = rlim->rate * d + rlim->carry;
+ }
+
+ gps = gain / NGTCP2_SECONDS;
+
+ if (gps < rlim->burst && rlim->tokens < rlim->burst - gps) {
+ rlim->tokens += gps;
+ rlim->carry = gain % NGTCP2_SECONDS;
+
+ return;
+ }
+
+ rlim->tokens = rlim->burst;
+ rlim->carry = 0;
+}
+
+int ngtcp2_ratelim_drain(ngtcp2_ratelim *rlim, uint64_t n, ngtcp2_tstamp ts) {
+ ratelim_update(rlim, ts);
+
+ if (rlim->tokens < n) {
+ return -1;
+ }
+
+ rlim->tokens -= n;
+
+ return 0;
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_ratelim.h b/third_party/ngtcp2/lib/ngtcp2_ratelim.h
new file mode 100644
index 00000000000..231f5d1b018
--- /dev/null
+++ b/third_party/ngtcp2/lib/ngtcp2_ratelim.h
@@ -0,0 +1,59 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ * Copyright (c) 2023 nghttp2 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_RATELIM_H
+#define NGTCP2_RATELIM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* defined(HAVE_CONFIG_H) */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_ratelim {
+ /* burst is the maximum number of tokens. */
+ uint64_t burst;
+ /* rate is the rate of token generation measured by token /
+ second. */
+ uint64_t rate;
+ /* tokens is the amount of tokens available to drain. */
+ uint64_t tokens;
+ /* carry is the partial token gained in sub-second period. It is
+ added to the computation in the next update round. */
+ uint64_t carry;
+ /* ts is the last timestamp that is known to this object. */
+ ngtcp2_tstamp ts;
+} ngtcp2_ratelim;
+
+/* ngtcp2_ratelim_init initializes |rlim| with the given
+ parameters. */
+void ngtcp2_ratelim_init(ngtcp2_ratelim *rlim, uint64_t burst, uint64_t rate,
+ ngtcp2_tstamp ts);
+
+/* ngtcp2_ratelim_drain drains |n| from rlim->tokens. It returns 0 if
+ it succeeds, or -1. */
+int ngtcp2_ratelim_drain(ngtcp2_ratelim *rlim, uint64_t n, ngtcp2_tstamp ts);
+
+#endif /* !defined(NGTCP2_RATELIM_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_ringbuf.c b/third_party/ngtcp2/lib/ngtcp2_ringbuf.c
index 353afca4d48..40c25f2a3d1 100644
--- a/third_party/ngtcp2/lib/ngtcp2_ringbuf.c
+++ b/third_party/ngtcp2/lib/ngtcp2_ringbuf.c
@@ -32,18 +32,8 @@
#include "ngtcp2_macro.h"
#ifndef NDEBUG
-static int ispow2(size_t n) {
-# if defined(_MSC_VER) && !defined(__clang__) && \
- (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941))
- return n && !(n & (n - 1));
-# elif defined(WIN32)
- return 1 == __popcnt((unsigned int)n);
-# else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
- (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
- return 1 == __builtin_popcount((unsigned int)n);
-# endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
- (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
-}
+/* Power-of-two test; simple portable bit trick. */
+static int ispow2(size_t n) { return n && !(n & (n - 1)); }
#endif /* !defined(NDEBUG) */
int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
diff --git a/third_party/ngtcp2/lib/ngtcp2_ringbuf.h b/third_party/ngtcp2/lib/ngtcp2_ringbuf.h
index d490524805b..73efb5255fc 100644
--- a/third_party/ngtcp2/lib/ngtcp2_ringbuf.h
+++ b/third_party/ngtcp2/lib/ngtcp2_ringbuf.h
@@ -110,7 +110,9 @@ void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len);
void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset);
/* ngtcp2_ringbuf_len returns the number of elements stored. */
-#define ngtcp2_ringbuf_len(RB) ((RB)->len)
+static inline size_t ngtcp2_ringbuf_len(const ngtcp2_ringbuf *rb) {
+ return rb->len;
+}
/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */
int ngtcp2_ringbuf_full(const ngtcp2_ringbuf *rb);
diff --git a/third_party/ngtcp2/lib/ngtcp2_rob.c b/third_party/ngtcp2/lib/ngtcp2_rob.c
index 853f1d650ea..20eae1d8819 100644
--- a/third_party/ngtcp2/lib/ngtcp2_rob.c
+++ b/third_party/ngtcp2/lib/ngtcp2_rob.c
@@ -36,8 +36,13 @@ int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end,
return NGTCP2_ERR_NOMEM;
}
- (*pg)->range.begin = begin;
- (*pg)->range.end = end;
+ **pg = (ngtcp2_rob_gap){
+ .range =
+ {
+ .begin = begin,
+ .end = end,
+ },
+ };
return 0;
}
@@ -53,9 +58,14 @@ int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk,
return NGTCP2_ERR_NOMEM;
}
- (*pd)->range.begin = offset;
- (*pd)->range.end = offset + chunk;
- (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data);
+ **pd = (ngtcp2_rob_data){
+ .range =
+ {
+ .begin = offset,
+ .end = offset + chunk,
+ },
+ .begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data),
+ };
return 0;
}
@@ -162,8 +172,8 @@ static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
return 0;
}
-int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
- size_t datalen) {
+ngtcp2_ssize ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset,
+ const uint8_t *data, size_t datalen) {
int rv;
ngtcp2_rob_gap *g;
ngtcp2_range m, l, r;
@@ -172,6 +182,8 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
.end = offset + datalen,
};
ngtcp2_ksl_it it;
+ ngtcp2_ssize nwrite = 0;
+ size_t mlen;
it = ngtcp2_ksl_lower_bound_search(&rob->gapksl, &q,
ngtcp2_ksl_range_exclusive_search);
@@ -180,7 +192,9 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
g = ngtcp2_ksl_it_get(&it);
m = ngtcp2_range_intersect(&q, &g->range);
- if (!ngtcp2_range_len(&m)) {
+
+ mlen = (size_t)ngtcp2_range_len(&m);
+ if (mlen == 0) {
break;
}
@@ -188,12 +202,13 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range);
ngtcp2_rob_gap_del(g, rob->mem);
- rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
- (size_t)ngtcp2_range_len(&m));
+ rv = rob_write_data(rob, m.begin, data + (m.begin - offset), mlen);
if (rv != 0) {
return rv;
}
+ nwrite += (ngtcp2_ssize)mlen;
+
continue;
}
@@ -222,16 +237,17 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
g->range = r;
}
- rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
- (size_t)ngtcp2_range_len(&m));
+ rv = rob_write_data(rob, m.begin, data + (m.begin - offset), mlen);
if (rv != 0) {
return rv;
}
+ nwrite += (ngtcp2_ssize)mlen;
+
ngtcp2_ksl_it_next(&it);
}
- return 0;
+ return nwrite;
}
void ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
@@ -248,9 +264,11 @@ void ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
}
if (offset < g->range.end) {
- ngtcp2_range r = {offset, g->range.end};
-
- ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r);
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range,
+ &(ngtcp2_range){
+ .begin = offset,
+ .end = g->range.end,
+ });
g->range.begin = offset;
break;
diff --git a/third_party/ngtcp2/lib/ngtcp2_rob.h b/third_party/ngtcp2/lib/ngtcp2_rob.h
index d53b5160b10..60a1c5b46a0 100644
--- a/third_party/ngtcp2/lib/ngtcp2_rob.h
+++ b/third_party/ngtcp2/lib/ngtcp2_rob.h
@@ -138,14 +138,14 @@ void ngtcp2_rob_free(ngtcp2_rob *rob);
* ngtcp2_rob_push adds new data pointed by |data| of length |datalen|
* at the stream offset |offset|.
*
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
+ * This function returns the number of data newly buffered if it
+ * succeeds, or one of the following negative error codes:
*
* NGTCP2_ERR_NOMEM
* Out of memory
*/
-int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
- size_t datalen);
+ngtcp2_ssize ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset,
+ const uint8_t *data, size_t datalen);
/*
* ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It
diff --git a/third_party/ngtcp2/lib/ngtcp2_rst.c b/third_party/ngtcp2/lib/ngtcp2_rst.c
index 181691f3e69..89b395cd954 100644
--- a/third_party/ngtcp2/lib/ngtcp2_rst.c
+++ b/third_party/ngtcp2/lib/ngtcp2_rst.c
@@ -32,17 +32,11 @@
#include "ngtcp2_conn_stat.h"
void ngtcp2_rs_init(ngtcp2_rs *rs) {
- rs->interval = UINT64_MAX;
- rs->delivered = 0;
- rs->prior_delivered = 0;
- rs->prior_ts = UINT64_MAX;
- rs->tx_in_flight = 0;
- rs->lost = 0;
- rs->prior_lost = 0;
- rs->send_elapsed = 0;
- rs->ack_elapsed = 0;
- rs->last_end_seq = -1;
- rs->is_app_limited = 0;
+ *rs = (ngtcp2_rs){
+ .interval = UINT64_MAX,
+ .prior_ts = UINT64_MAX,
+ .last_end_seq = -1,
+ };
}
void ngtcp2_rst_init(ngtcp2_rst *rst) {
@@ -58,19 +52,29 @@ void ngtcp2_rst_reset(ngtcp2_rst *rst) {
rst->app_limited = 0;
rst->is_cwnd_limited = 0;
rst->lost = 0;
- rst->valid_after_seq = rst->last_seq;
+}
+
+void ngtcp2_rst_reset_rate_sample(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
+ ngtcp2_rs *rs = &rst->rs;
+
+ rs->interval = UINT64_MAX;
+ rs->prior_ts = UINT64_MAX;
+
+ cstat->delivery_rate_sec = 0;
}
void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
const ngtcp2_conn_stat *cstat) {
- if (cstat->bytes_in_flight == 0) {
+ /* cstat->bytes_in_flight includes ent->pktlen. If they are the
+ same, there is no in-flight packets. */
+ if (cstat->bytes_in_flight == ent->pktlen) {
rst->first_sent_ts = rst->delivered_ts = ent->ts;
}
ent->rst.first_sent_ts = rst->first_sent_ts;
ent->rst.delivered_ts = rst->delivered_ts;
ent->rst.delivered = rst->delivered;
ent->rst.is_app_limited = rst->app_limited != 0;
- ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen;
+ ent->rst.tx_in_flight = cstat->bytes_in_flight;
ent->rst.lost = rst->lost;
ent->rst.end_seq = ++rst->last_seq;
}
@@ -89,10 +93,8 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
rs->interval = ngtcp2_max_uint64(rs->send_elapsed, rs->ack_elapsed);
rs->delivered = rst->delivered - rs->prior_delivered;
- rs->lost = rst->lost - rs->prior_lost;
if (rs->interval < cstat->min_rtt) {
- rs->interval = UINT64_MAX;
return;
}
@@ -103,31 +105,23 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval;
}
-static int rst_is_newest_pkt(const ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
- const ngtcp2_rs *rs) {
- return ent->ts > rst->first_sent_ts ||
- (ent->ts == rst->first_sent_ts && ent->rst.end_seq > rs->last_end_seq);
+static int is_newest_pkt(const ngtcp2_rtb_entry *ent, const ngtcp2_rs *rs) {
+ return ent->rst.end_seq > rs->last_end_seq;
}
void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
ngtcp2_tstamp ts) {
ngtcp2_rs *rs = &rst->rs;
- if (ent->rst.end_seq <= rst->valid_after_seq) {
- return;
- }
-
rst->delivered += ent->pktlen;
rst->delivered_ts = ts;
- if (rs->prior_ts == UINT64_MAX || rst_is_newest_pkt(rst, ent, rs)) {
+ if (rs->prior_ts == UINT64_MAX || is_newest_pkt(ent, rs)) {
rs->prior_delivered = ent->rst.delivered;
rs->prior_ts = ent->rst.delivered_ts;
rs->is_app_limited = ent->rst.is_app_limited;
rs->send_elapsed = ent->ts - ent->rst.first_sent_ts;
rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts;
- rs->tx_in_flight = ent->rst.tx_in_flight;
- rs->prior_lost = ent->rst.lost;
rs->last_end_seq = ent->rst.end_seq;
rst->first_sent_ts = ent->ts;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_rst.h b/third_party/ngtcp2/lib/ngtcp2_rst.h
index c2580306cc5..b0bc2d06c9c 100644
--- a/third_party/ngtcp2/lib/ngtcp2_rst.h
+++ b/third_party/ngtcp2/lib/ngtcp2_rst.h
@@ -48,7 +48,6 @@ typedef struct ngtcp2_rs {
ngtcp2_tstamp prior_ts;
uint64_t tx_in_flight;
uint64_t lost;
- uint64_t prior_lost;
ngtcp2_duration send_elapsed;
ngtcp2_duration ack_elapsed;
int64_t last_end_seq;
@@ -73,10 +72,6 @@ typedef struct ngtcp2_rst {
across all packet number spaces, we can replace this with a
packet number. */
int64_t last_seq;
- /* valid_after_seq is the sequence number, and ignore a packet if
- the sequence number of the packet is less than or equal to this
- number. */
- int64_t valid_after_seq;
int is_cwnd_limited;
} ngtcp2_rst;
@@ -84,6 +79,8 @@ void ngtcp2_rst_init(ngtcp2_rst *rst);
void ngtcp2_rst_reset(ngtcp2_rst *rst);
+void ngtcp2_rst_reset_rate_sample(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
+
void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
const ngtcp2_conn_stat *cstat);
void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
diff --git a/third_party/ngtcp2/lib/ngtcp2_rtb.c b/third_party/ngtcp2/lib/ngtcp2_rtb.c
index 101dcaa99f8..67aaa049302 100644
--- a/third_party/ngtcp2/lib/ngtcp2_rtb.c
+++ b/third_party/ngtcp2/lib/ngtcp2_rtb.c
@@ -43,16 +43,19 @@ ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent)
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 = (ngtcp2_rtb_entry){
+ .hd =
+ {
+ .pkt_num = hd->pkt_num,
+ .type = hd->type,
+ .flags = hd->flags,
+ },
+ .frc = frc,
+ .ts = ts,
+ .lost_ts = UINT64_MAX,
+ .pktlen = pktlen,
+ .flags = flags,
+ };
}
int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
@@ -125,13 +128,12 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
ngtcp2_conn_stat *cstat) {
- ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat);
-
assert(rtb->cc_pkt_num <= ent->hd.pkt_num);
cstat->bytes_in_flight += ent->pktlen;
rtb->cc_bytes_in_flight += ent->pktlen;
+ ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat);
ngtcp2_rst_update_app_limited(rtb->rst, cstat);
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
@@ -198,10 +200,10 @@ static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
}
/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_RECLAIM_FLAG_NONE 0x00u
+#define NGTCP2_RECLAIM_FLAG_NONE 0x00U
/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed
because of the packet loss.*/
-#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u
+#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01U
/*
* rtb_reclaim_frame copies and queues frames included in |ent| for
@@ -231,7 +233,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags,
continue;
}
- switch (frc->fr.type) {
+ switch (frc->fr.hd.type) {
case NGTCP2_FRAME_STREAM:
strm = ngtcp2_conn_find_stream(conn, fr->stream.stream_id);
if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM)) {
@@ -269,7 +271,12 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags,
return rv;
}
- nfrc->fr = *fr;
+ nfrc->fr.stream.type = fr->stream.type;
+ nfrc->fr.stream.flags = fr->stream.flags;
+ nfrc->fr.stream.fin = fr->stream.fin;
+ nfrc->fr.stream.stream_id = fr->stream.stream_id;
+ nfrc->fr.stream.offset = fr->stream.offset;
+ nfrc->fr.stream.datacnt = fr->stream.datacnt;
ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data,
fr->stream.datacnt);
@@ -312,7 +319,12 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags,
return rv;
}
- nfrc->fr = *fr;
+ nfrc->fr.stream.type = fr->stream.type;
+ nfrc->fr.stream.flags = 0;
+ nfrc->fr.stream.fin = 0;
+ nfrc->fr.stream.stream_id = 0;
+ nfrc->fr.stream.offset = fr->stream.offset;
+ nfrc->fr.stream.datacnt = fr->stream.datacnt;
ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data,
fr->stream.datacnt);
@@ -413,7 +425,7 @@ static int conn_process_lost_datagram(ngtcp2_conn *conn,
int rv;
for (frc = ent->frc; frc; frc = frc->next) {
- switch (frc->fr.type) {
+ switch (frc->fr.hd.type) {
case NGTCP2_FRAME_DATAGRAM:
case NGTCP2_FRAME_DATAGRAM_LEN:
assert(conn->callbacks.lost_datagram);
@@ -451,19 +463,28 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
if (ent->flags &
(NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
++rtb->num_lost_ignore_pkts;
- } else if (rtb->cc->on_pkt_lost) {
- cc->on_pkt_lost(cc, cstat,
- ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
- pktns->id, ent->ts, ent->rst.lost,
- ent->rst.tx_in_flight,
- ent->rst.is_app_limited),
- ts);
+ } else {
+ ++cstat->pkt_lost;
+ cstat->bytes_lost += ent->pktlen;
+
+ if (ent->hd.pkt_num >= rtb->cc_pkt_num) {
+ rtb->rst->lost += ent->pktlen;
+
+ if (rtb->cc->on_pkt_lost) {
+ cc->on_pkt_lost(cc, cstat,
+ ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
+ pktns->id, ent->ts, ent->rst.lost,
+ ent->rst.tx_in_flight,
+ ent->rst.is_app_limited),
+ ts);
+ }
+ }
}
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "pkn=%" PRId64 " has already been reclaimed on PTO",
- ent->hd.pkt_num);
+ ngtcp2_log_infof(rtb->log, NGTCP2_LOG_EVENT_LDC,
+ "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);
} else {
@@ -520,9 +541,10 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
int rv;
(void)rv;
+ rtb_on_remove(rtb, ent, cstat);
+
rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num);
assert(0 == rv);
- rtb_on_remove(rtb, ent, cstat);
assert(ent->next == NULL);
@@ -591,7 +613,7 @@ static int process_acked_pkt(ngtcp2_rtb_entry *ent, ngtcp2_conn *conn,
frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK;
}
- switch (frc->fr.type) {
+ switch (frc->fr.hd.type) {
case NGTCP2_FRAME_STREAM:
strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id);
if (strm == NULL) {
@@ -704,6 +726,8 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
ngtcp2_cc *cc = rtb->cc;
ngtcp2_cc_pkt pkt;
+ assert(ent->hd.pkt_num >= rtb->cc_pkt_num);
+
ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts);
if (cc->on_pkt_acked) {
@@ -724,8 +748,7 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_ack *fr, size_t ecn_acked,
- ngtcp2_tstamp largest_pkt_sent_ts,
- ngtcp2_tstamp ts) {
+ const ngtcp2_cc_ack *cc_ack, ngtcp2_tstamp ts) {
if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) {
return;
}
@@ -752,9 +775,9 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
}
if (fr->type == NGTCP2_FRAME_ACK_ECN) {
- if (cc->congestion_event && largest_pkt_sent_ts != UINT64_MAX &&
+ if (cc->congestion_event && cc_ack->largest_pkt_sent_ts != UINT64_MAX &&
fr->ecn.ce > pktns->acktr.ecn.ack.ce) {
- cc->congestion_event(cc, cstat, largest_pkt_sent_ts, 0, ts);
+ cc->congestion_event(cc, cstat, cc_ack->largest_pkt_sent_ts, cc_ack, ts);
}
pktns->acktr.ecn.ack.ect0 = fr->ecn.ect0;
@@ -763,7 +786,7 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
}
}
-static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_cc_ack *cc_ack,
ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
@@ -777,24 +800,23 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
int rv;
ngtcp2_ksl_it it;
size_t num_acked = 0;
- ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX;
int64_t pkt_num;
ngtcp2_cc *cc = rtb->cc;
ngtcp2_rtb_entry *acked_ent = NULL;
int ack_eliciting_pkt_acked = 0;
size_t ecn_acked = 0;
int verify_ecn = 0;
- ngtcp2_cc_ack cc_ack = {0};
+ ngtcp2_cc_ack cc_ack = {
+ .largest_pkt_sent_ts = UINT64_MAX,
+ .rtt = UINT64_MAX,
+ };
size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_ignore_pkts;
- cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight;
- 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 &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED |
- NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR);
+ 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");
@@ -805,12 +827,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
verify_ecn = 1;
}
+ ngtcp2_rst_reset_rate_sample(rtb->rst, cstat);
+
/* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
if (ngtcp2_ksl_it_end(&it)) {
if (conn && verify_ecn) {
- conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
- largest_pkt_sent_ts, ts);
+ conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, &cc_ack, ts);
+ }
+
+ if (cc->on_ack_recv) {
+ cc->on_ack_recv(cc, cstat, &cc_ack, ts);
}
return 0;
@@ -834,8 +861,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
goto fail;
}
- if (largest_ack == pkt_num) {
- largest_pkt_sent_ts = ent->ts;
+ if (rtb->largest_acked_tx_pkt_num == pkt_num) {
+ cc_ack.largest_pkt_sent_ts = ent->ts;
}
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
@@ -843,7 +870,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
}
rtb_remove(rtb, &it, &acked_ent, ent, cstat);
- ++num_acked;
}
for (i = 0; i < fr->rangecnt; ++i) {
@@ -873,18 +899,14 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
}
rtb_remove(rtb, &it, &acked_ent, ent, cstat);
- ++num_acked;
}
}
- if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
- cc_ack.rtt =
- ngtcp2_max_uint64(pkt_ts - largest_pkt_sent_ts, NGTCP2_NANOSECONDS);
+ if (cc_ack.largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
+ cc_ack.rtt = ngtcp2_max_uint64(pkt_ts - cc_ack.largest_pkt_sent_ts,
+ NGTCP2_NANOSECONDS);
- 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);
- }
+ ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts);
}
if (conn) {
@@ -904,23 +926,22 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
cc_ack.bytes_delivered += ent->pktlen;
cc_ack.pkt_delivered = ent->rst.delivered;
+
+ rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts);
+
+ ++num_acked;
}
- rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts);
acked_ent = ent->next;
ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
rtb->frc_objalloc, rtb->mem);
}
-
- if (verify_ecn) {
- conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
- largest_pkt_sent_ts, ts);
- }
} else {
/* For unit tests */
for (ent = acked_ent; ent; ent = acked_ent) {
rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts);
acked_ent = ent->next;
+ ++num_acked;
ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
rtb->frc_objalloc, rtb->mem);
}
@@ -935,17 +956,18 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
ngtcp2_rst_on_ack_recv(rtb->rst, cstat);
if (conn) {
- rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts);
+ rv = rtb_detect_lost_pkt(rtb, &cc_ack, conn, pktns, cstat, ts);
if (rv != 0) {
return rv;
}
}
}
- rtb->rst->lost += cc_ack.bytes_lost;
+ if (conn && verify_ecn) {
+ conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, &cc_ack, ts);
+ }
- cc_ack.largest_pkt_sent_ts = largest_pkt_sent_ts;
- if (num_acked && cc->on_ack_recv) {
+ if (cc->on_ack_recv) {
cc->on_ack_recv(cc, cstat, &cc_ack, ts);
}
@@ -1011,7 +1033,11 @@ static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) {
pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost;
}
-static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+/*
+ * This function assigns the number of bytes lost to
+ * |cc_ack|->bytes_lost if any.
+ */
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_cc_ack *cc_ack,
ngtcp2_conn *conn, ngtcp2_pktns *pktns,
ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
ngtcp2_rtb_entry *ent;
@@ -1129,8 +1155,10 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
break;
}
+ cc_ack->bytes_lost = bytes_lost;
+
if (cc->congestion_event) {
- cc->congestion_event(cc, cstat, latest_ts, bytes_lost, ts);
+ cc->congestion_event(cc, cstat, latest_ts, cc_ack, ts);
}
loss_window = latest_ts - oldest_ts;
@@ -1143,10 +1171,10 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
*/
if (pktns->id == NGTCP2_PKTNS_ID_APPLICATION && loss_window &&
loss_window >= congestion_period) {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "persistent congestion loss_window=%" PRIu64
- " congestion_period=%" PRIu64,
- loss_window, congestion_period);
+ ngtcp2_log_infof(rtb->log, NGTCP2_LOG_EVENT_LDC,
+ "persistent congestion loss_window=%" PRIu64
+ " congestion_period=%" PRIu64,
+ loss_window, congestion_period);
/* Reset min_rtt, srtt, and rttvar here. Next new RTT
sample will be used to recalculate these values. */
@@ -1166,18 +1194,18 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres);
- if (ppkt_lost) {
- *ppkt_lost = bytes_lost;
- }
-
return 0;
}
int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts) {
- return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat,
- ts);
+ return rtb_detect_lost_pkt(rtb,
+ &(ngtcp2_cc_ack){
+ .largest_pkt_sent_ts = UINT64_MAX,
+ .rtt = UINT64_MAX,
+ },
+ conn, pktns, cstat, ts);
}
void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
@@ -1193,8 +1221,8 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED);
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
+ ngtcp2_log_infof(rtb->log, NGTCP2_LOG_EVENT_LDC,
+ "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
--rtb->num_lost_pkts;
@@ -1230,13 +1258,14 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb,
ngtcp2_ksl_it_prev(&it);
ent = ngtcp2_ksl_it_get(&it);
- if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) ||
- ts - ent->lost_ts < timeout) {
+ assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED);
+
+ if (ts - ent->lost_ts < timeout) {
return;
}
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
+ ngtcp2_log_infof(rtb->log, NGTCP2_LOG_EVENT_LDC,
+ "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
--rtb->num_lost_pkts;
@@ -1280,7 +1309,7 @@ static int rtb_reclaim_frame_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
int rv;
for (; *pfrc;) {
- switch ((*pfrc)->fr.type) {
+ switch ((*pfrc)->fr.hd.type) {
case NGTCP2_FRAME_STREAM:
frc = *pfrc;
@@ -1388,15 +1417,22 @@ int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE));
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "pkn=%" PRId64 " has already been reclaimed on PTO",
- ent->hd.pkt_num);
+ ngtcp2_log_infof(rtb->log, NGTCP2_LOG_EVENT_LDC,
+ "pkn=%" PRId64 " has already been reclaimed on PTO",
+ ent->hd.pkt_num);
+
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+
continue;
}
if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) ||
!conn->callbacks.lost_datagram)) {
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+
continue;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_rtb.h b/third_party/ngtcp2/lib/ngtcp2_rtb.h
index 14684a458a6..62bcb381fa1 100644
--- a/third_party/ngtcp2/lib/ngtcp2_rtb.h
+++ b/third_party/ngtcp2/lib/ngtcp2_rtb.h
@@ -48,41 +48,41 @@ typedef struct ngtcp2_conn_stat ngtcp2_conn_stat;
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00u
+#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00U
/* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a
probe packet. */
-#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01u
+#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01U
/* 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 and
NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */
-#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02u
+#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02U
/* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry
elicits acknowledgement. */
-#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04u
+#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04U
/* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has
been reclaimed on PTO. It is not marked lost yet and still
consumes congestion window. */
-#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08u
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08U
/* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry
has been marked lost and, optionally, scheduled to retransmit. */
-#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10u
+#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10U
/* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a
UDP datagram with ECN marking. */
-#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20u
+#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20U
/* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes
DATAGRAM frame. */
-#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40u
+#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40U
/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes
a PMTUD probe packet. */
-#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80u
+#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80U
/* 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 0x100u
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100U
/* NGTCP2_RTB_ENTRY_FLAG_SKIP indicates that the entry has the skipped
packet number. */
-#define NGTCP2_RTB_ENTRY_FLAG_SKIP 0x200u
+#define NGTCP2_RTB_ENTRY_FLAG_SKIP 0x200U
typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
diff --git a/third_party/ngtcp2/lib/ngtcp2_settings.c b/third_party/ngtcp2/lib/ngtcp2_settings.c
index 77a68bd112e..f774504282e 100644
--- a/third_party/ngtcp2/lib/ngtcp2_settings.c
+++ b/third_party/ngtcp2/lib/ngtcp2_settings.c
@@ -37,6 +37,10 @@ void ngtcp2_settings_default_versioned(int settings_version,
switch (settings_version) {
case NGTCP2_SETTINGS_VERSION:
+ settings->glitch_ratelim_burst = NGTCP2_DEFAULT_GLITCH_RATELIM_BURST;
+ settings->glitch_ratelim_rate = NGTCP2_DEFAULT_GLITCH_RATELIM_RATE;
+ /* fall through */
+ case NGTCP2_SETTINGS_V2:
case NGTCP2_SETTINGS_V1:
settings->cc_algo = NGTCP2_CC_ALGO_CUBIC;
settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
@@ -82,6 +86,9 @@ size_t ngtcp2_settingslen_version(int settings_version) {
switch (settings_version) {
case NGTCP2_SETTINGS_VERSION:
return sizeof(settings);
+ case NGTCP2_SETTINGS_V2:
+ return offsetof(ngtcp2_settings, pmtud_probeslen) +
+ sizeof(settings.pmtud_probeslen);
case NGTCP2_SETTINGS_V1:
return offsetof(ngtcp2_settings, initial_pkt_num) +
sizeof(settings.initial_pkt_num);
diff --git a/third_party/ngtcp2/lib/ngtcp2_settings.h b/third_party/ngtcp2/lib/ngtcp2_settings.h
index 80466d43e47..b40b6c4a895 100644
--- a/third_party/ngtcp2/lib/ngtcp2_settings.h
+++ b/third_party/ngtcp2/lib/ngtcp2_settings.h
@@ -31,6 +31,13 @@
#include <ngtcp2/ngtcp2.h>
+/* NGTCP2_DEFAULT_GLITCH_RATELIM_BURST is the maximum number of tokens
+ in glitch rate limiter. It is also the initial value. */
+#define NGTCP2_DEFAULT_GLITCH_RATELIM_BURST 10000
+/* NGTCP2_DEFAULT_GLITCH_RATELIM_RATE is the rate of tokens generated
+ per second for glitch rate limiter. */
+#define NGTCP2_DEFAULT_GLITCH_RATELIM_RATE 330
+
/*
* ngtcp2_settings_convert_to_latest converts |src| of version
* |settings_version| to the latest version NGTCP2_SETTINGS_VERSION.
diff --git a/third_party/ngtcp2/lib/ngtcp2_str.c b/third_party/ngtcp2/lib/ngtcp2_str.c
index a61636d188f..2726571a504 100644
--- a/third_party/ngtcp2/lib/ngtcp2_str.c
+++ b/third_party/ngtcp2/lib/ngtcp2_str.c
@@ -47,27 +47,32 @@ const void *ngtcp2_get_bytes(void *dest, const void *src, size_t n) {
uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len) {
size_t i;
- uint8_t *p = dest;
for (i = 0; i < len; ++i) {
- *p++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4];
- *p++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xf];
+ *dest++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4];
+ *dest++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xF];
}
+ return dest;
+}
+
+char *ngtcp2_encode_hex_cstr(char *dest, const uint8_t *data, size_t len) {
+ uint8_t *p = ngtcp2_encode_hex((uint8_t *)dest, data, len);
+
*p = '\0';
return dest;
}
-char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
- size_t len) {
+char *ngtcp2_encode_printable_ascii_cstr(char *dest, const uint8_t *data,
+ size_t len) {
size_t i;
char *p = dest;
uint8_t c;
for (i = 0; i < len; ++i) {
c = data[i];
- if (0x20 <= c && c <= 0x7e) {
+ if (0x20 <= c && c <= 0x7E) {
*p++ = (char)c;
} else {
*p++ = '.';
@@ -79,40 +84,15 @@ char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
return dest;
}
-/*
- * write_uint writes |n| to the buffer pointed by |p| in decimal
- * representation. It returns |p| plus the number of bytes written.
- * The function assumes that the buffer has enough capacity to contain
- * a string.
- */
-static uint8_t *write_uint(uint8_t *p, uint64_t n) {
- size_t nlen = 0;
- uint64_t t;
- uint8_t *res;
-
- if (n == 0) {
- *p++ = '0';
- return p;
- }
- for (t = n; t; t /= 10, ++nlen)
- ;
- p += nlen;
- res = p;
- for (; n; n /= 10) {
- *--p = (uint8_t)((n % 10) + '0');
- }
- return res;
-}
-
-uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr) {
+char *ngtcp2_encode_ipv4_cstr(char *dest, const uint8_t *addr) {
size_t i;
- uint8_t *p = dest;
+ char *p = dest;
- p = write_uint(p, addr[0]);
+ p = (char *)ngtcp2_encode_uint((uint8_t *)p, addr[0]);
for (i = 1; i < 4; ++i) {
*p++ = '.';
- p = write_uint(p, addr[i]);
+ p = (char *)ngtcp2_encode_uint((uint8_t *)p, addr[i]);
}
*p = '\0';
@@ -125,9 +105,9 @@ uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr) {
* length |len| to |dest| in hex string. Any leading zeros are
* suppressed. It returns |dest| plus the number of bytes written.
*/
-static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) {
+static char *write_hex_zsup(char *dest, const uint8_t *data, size_t len) {
size_t i;
- uint8_t *p = dest;
+ char *p = dest;
uint8_t d;
for (i = 0; i < len; ++i) {
@@ -136,10 +116,10 @@ static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) {
break;
}
- d &= 0xf;
+ d &= 0xF;
if (d) {
- *p++ = (uint8_t)LOWER_XDIGITS[d];
+ *p++ = LOWER_XDIGITS[d];
++i;
break;
}
@@ -152,19 +132,19 @@ static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) {
for (; i < len; ++i) {
d = data[i];
- *p++ = (uint8_t)LOWER_XDIGITS[d >> 4];
- *p++ = (uint8_t)LOWER_XDIGITS[d & 0xf];
+ *p++ = LOWER_XDIGITS[d >> 4];
+ *p++ = LOWER_XDIGITS[d & 0xF];
}
return p;
}
-uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) {
+char *ngtcp2_encode_ipv6_cstr(char *dest, const uint8_t *addr) {
uint16_t blks[8];
size_t i;
size_t zlen, zoff;
size_t max_zlen = 0, max_zoff = 8;
- uint8_t *p = dest;
+ char *p = dest;
for (i = 0; i < 16; i += sizeof(uint16_t)) {
/* Copy in network byte order. */
@@ -231,3 +211,124 @@ int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) {
return rv == 0;
}
+
+/* countl_zero counts the number of leading zeros in |x|. It is
+ undefined if |x| is 0. */
+static int countl_zero(uint64_t x) {
+#ifdef __GNUC__
+ return __builtin_clzll(x);
+#else /* !defined(__GNUC__) */
+ /* This is the same implementation of Go's LeadingZeros64 in
+ math/bits package. */
+ static const uint8_t len8tab[] = {
+ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ };
+ int n = 0;
+
+ if (x >= 1ULL << 32) {
+ x >>= 32;
+ n += 32;
+ }
+
+ if (x >= 1 << 16) {
+ x >>= 16;
+ n += 16;
+ }
+
+ if (x >= 1 << 8) {
+ x >>= 8;
+ n += 8;
+ }
+
+ return 64 - (n + len8tab[x]);
+#endif /* !defined(__GNUC__) */
+}
+
+/*
+ * count_digit returns the minimum number of digits to represent |x|
+ * in base 10.
+ *
+ * credit:
+ * https://lemire.me/blog/2025/01/07/counting-the-digits-of-64-bit-integers/
+ */
+static size_t count_digit(uint64_t x) {
+ static const uint64_t count_digit_tbl[] = {
+ 9ULL,
+ 99ULL,
+ 999ULL,
+ 9999ULL,
+ 99999ULL,
+ 999999ULL,
+ 9999999ULL,
+ 99999999ULL,
+ 999999999ULL,
+ 9999999999ULL,
+ 99999999999ULL,
+ 999999999999ULL,
+ 9999999999999ULL,
+ 99999999999999ULL,
+ 999999999999999ULL,
+ 9999999999999999ULL,
+ 99999999999999999ULL,
+ 999999999999999999ULL,
+ 9999999999999999999ULL,
+ };
+ size_t y = (size_t)(19 * (63 - countl_zero(x | 1)) >> 6);
+
+ y += x > count_digit_tbl[y];
+
+ return y + 1;
+}
+
+uint8_t *ngtcp2_encode_uint(uint8_t *dest, uint64_t n) {
+ static const uint8_t uint_digits[] =
+ "00010203040506070809101112131415161718192021222324252627282930313233343536"
+ "37383940414243444546474849505152535455565758596061626364656667686970717273"
+ "7475767778798081828384858687888990919293949596979899";
+ uint8_t *p;
+ const uint8_t *tp;
+
+ if (n < 10) {
+ *dest++ = (uint8_t)('0' + n);
+ return dest;
+ }
+
+ if (n < 100) {
+ tp = &uint_digits[n * 2];
+ *dest++ = *tp++;
+ *dest++ = *tp;
+ return dest;
+ }
+
+ dest += count_digit(n);
+ p = dest;
+
+ for (; n >= 100; n /= 100) {
+ p -= 2;
+ tp = &uint_digits[(n % 100) * 2];
+ p[0] = *tp++;
+ p[1] = *tp;
+ }
+
+ if (n < 10) {
+ *--p = (uint8_t)('0' + n);
+ return dest;
+ }
+
+ p -= 2;
+ tp = &uint_digits[n * 2];
+ p[0] = *tp++;
+ p[1] = *tp;
+
+ return dest;
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_str.h b/third_party/ngtcp2/lib/ngtcp2_str.h
index f970c153e80..051053d75b7 100644
--- a/third_party/ngtcp2/lib/ngtcp2_str.h
+++ b/third_party/ngtcp2/lib/ngtcp2_str.h
@@ -46,24 +46,31 @@ uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n);
const void *ngtcp2_get_bytes(void *dest, const void *src, size_t n);
/*
- * ngtcp2_encode_hex encodes |data| of length |len| in hex string. It
- * writes additional NULL bytes at the end of the buffer. The buffer
- * pointed by |dest| must have at least |len| * 2 + 1 bytes space.
- * This function returns |dest|.
+ * ngtcp2_encode_hex encodes |data| of length |len| in hex string.
+ * The buffer pointed by |dest| must have at least |len| * 2 bytes
+ * space. This function returns |dest| + |len| * 2.
*/
uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len);
/*
- * ngtcp2_encode_ipv4 encodes binary form IPv4 address stored in
+ * ngtcp2_encode_hex_cstr encodes |data| of length |len| in hex
+ * string. It writes additional NULL bytes at the end of the buffer.
+ * The buffer pointed by |dest| must have at least |len| * 2 + 1 bytes
+ * space. This function returns |dest|.
+ */
+char *ngtcp2_encode_hex_cstr(char *dest, const uint8_t *data, size_t len);
+
+/*
+ * ngtcp2_encode_ipv4_cstr encodes binary form IPv4 address stored in
* |addr| to human readable text form in the buffer pointed by |dest|.
* The capacity of buffer must have enough length to store a text form
* plus a terminating NULL byte. The resulting text form ends with
* NULL byte. The function returns |dest|.
*/
-uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr);
+char *ngtcp2_encode_ipv4_cstr(char *dest, const uint8_t *addr);
/*
- * ngtcp2_encode_ipv6 encodes binary form IPv6 address stored in
+ * ngtcp2_encode_ipv6_cstr encodes binary form IPv6 address stored in
* |addr| to human readable text form in the buffer pointed by |dest|.
* The capacity of buffer must have enough length to store a text form
* plus a terminating NULL byte. The resulting text form ends with
@@ -72,7 +79,7 @@ uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr);
* https://tools.ietf.org/html/rfc5952#section-4. The function
* returns |dest|.
*/
-uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr);
+char *ngtcp2_encode_ipv6_cstr(char *dest, const uint8_t *addr);
/*
* ngtcp2_encode_printable_ascii encodes |data| of length |len| in
@@ -81,8 +88,8 @@ uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr);
* writes additional NULL bytes at the end of the buffer. |dest| must
* have at least |len| + 1 bytes. This function returns |dest|.
*/
-char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
- size_t len);
+char *ngtcp2_encode_printable_ascii_cstr(char *dest, const uint8_t *data,
+ size_t len);
/*
* ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers
@@ -91,4 +98,12 @@ char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
*/
int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n);
+/*
+ * ngtcp2_encode_uint encodes |n| as a decimal integer to the buffer
+ * pointed by |dest|. This function assumes that the buffer contains
+ * the sufficient capacity to write the number. This function returns
+ * the pointer to the buffer past the last byte written.
+ */
+uint8_t *ngtcp2_encode_uint(uint8_t *dest, uint64_t n);
+
#endif /* !defined(NGTCP2_STR_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_strm.c b/third_party/ngtcp2/lib/ngtcp2_strm.c
index 8ea969c4add..70aa8933a3b 100644
--- a/third_party/ngtcp2/lib/ngtcp2_strm.c
+++ b/third_party/ngtcp2/lib/ngtcp2_strm.c
@@ -36,30 +36,27 @@ 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->pe.index = NGTCP2_PQ_BAD_INDEX;
- strm->cycle = 0;
- strm->frc_objalloc = frc_objalloc;
- strm->tx.acked_offset = NULL;
- strm->tx.cont_acked_offset = 0;
- strm->tx.streamfrq = NULL;
- strm->tx.offset = 0;
- strm->tx.max_offset = max_tx_offset;
- strm->tx.last_blocked_offset = UINT64_MAX;
- strm->tx.last_max_stream_data_ts = UINT64_MAX;
- strm->tx.loss_count = 0;
- strm->tx.last_lost_pkt_num = -1;
- strm->tx.stop_sending_app_error_code = 0;
- strm->tx.reset_stream_app_error_code = 0;
- strm->rx.rob = NULL;
- strm->rx.cont_offset = 0;
- strm->rx.last_offset = 0;
- strm->rx.max_offset = strm->rx.unsent_max_offset = strm->rx.window =
- max_rx_offset;
- strm->mem = mem;
- strm->stream_id = stream_id;
- strm->stream_user_data = stream_user_data;
- strm->flags = flags;
- strm->app_error_code = 0;
+ *strm = (ngtcp2_strm){
+ .pe.index = NGTCP2_PQ_BAD_INDEX,
+ .frc_objalloc = frc_objalloc,
+ .tx =
+ {
+ .max_offset = max_tx_offset,
+ .last_blocked_offset = UINT64_MAX,
+ .last_max_stream_data_ts = UINT64_MAX,
+ .last_lost_pkt_num = -1,
+ },
+ .rx =
+ {
+ .max_offset = max_rx_offset,
+ .unsent_max_offset = max_rx_offset,
+ .window = max_rx_offset,
+ },
+ .mem = mem,
+ .stream_id = stream_id,
+ .stream_user_data = stream_user_data,
+ .flags = flags,
+ };
}
void ngtcp2_strm_free(ngtcp2_strm *strm) {
@@ -120,12 +117,13 @@ uint64_t ngtcp2_strm_rx_offset(const ngtcp2_strm *strm) {
/* strm_rob_heavily_fragmented returns nonzero if the number of gaps
in |rob| exceeds the limit. */
static int strm_rob_heavily_fragmented(const ngtcp2_rob *rob) {
- return ngtcp2_ksl_len(&rob->gapksl) >= 5000;
+ return ngtcp2_ksl_len(&rob->gapksl) >= 4000;
}
-int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
- size_t datalen, uint64_t offset) {
+ngtcp2_ssize ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
+ size_t datalen, uint64_t offset) {
int rv;
+ ngtcp2_ssize nwrite;
if (strm->rx.rob == NULL) {
rv = strm_rob_init(strm);
@@ -138,11 +136,16 @@ int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
}
}
+ nwrite = ngtcp2_rob_push(strm->rx.rob, offset, data, datalen);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
if (strm_rob_heavily_fragmented(strm->rx.rob)) {
return NGTCP2_ERR_INTERNAL;
}
- return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen);
+ return nwrite;
}
void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) {
@@ -187,8 +190,8 @@ static int strm_streamfrq_init(ngtcp2_strm *strm) {
int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) {
int rv;
- assert(frc->fr.type == NGTCP2_FRAME_STREAM ||
- frc->fr.type == NGTCP2_FRAME_CRYPTO);
+ assert(frc->fr.hd.type == NGTCP2_FRAME_STREAM ||
+ frc->fr.hd.type == NGTCP2_FRAME_CRYPTO);
assert(frc->next == NULL);
if (strm->tx.streamfrq == NULL) {
@@ -196,6 +199,8 @@ int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) {
if (rv != 0) {
return rv;
}
+ } else if (ngtcp2_ksl_len(strm->tx.streamfrq) >= 8000) {
+ return NGTCP2_ERR_INTERNAL;
}
return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset,
@@ -300,8 +305,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
fr->offset = offset + base_offset;
fr->datacnt = end_idx - idx;
- fr->data[0].base += base_offset;
- fr->data[0].len -= (size_t)base_offset;
+ ngtcp2_vec_drop(&fr->data[0], (size_t)base_offset);
*pfrc = frc;
@@ -327,8 +331,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
nfr->stream_id = fr->stream_id;
nfr->offset = end_offset + end_base_offset;
nfr->datacnt = fr->datacnt - end_idx;
- nfr->data[0].base += end_base_offset;
- nfr->data[0].len -= (size_t)end_base_offset;
+ ngtcp2_vec_drop(&nfr->data[0], (size_t)end_base_offset);
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
@@ -356,8 +359,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
fr->data[fr->datacnt - 1].len = (size_t)end_base_offset;
}
- fr->data[0].base += base_offset;
- fr->data[0].len -= (size_t)base_offset;
+ ngtcp2_vec_drop(&fr->data[0], (size_t)base_offset);
*pfrc = frc;
@@ -370,13 +372,12 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
size_t left) {
ngtcp2_stream *fr, *nfr;
- ngtcp2_frame_chain *frc, *nfrc, *sfrc;
+ ngtcp2_frame_chain *frc, *nfrc;
int rv;
size_t nmerged;
uint64_t datalen;
- ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT];
- ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT];
- size_t acnt, bcnt;
+ ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
+ size_t datacnt;
uint64_t unacked_offset;
if (strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0) {
@@ -398,7 +399,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
/* datalen could be zero if 0 length STREAM has been sent */
- if (left == 0 && datalen) {
+ /* We might see more data in the queue, then left < datalen could be
+ true. We only see the first one for now. */
+ if ((fr->type == NGTCP2_FRAME_STREAM &&
+ (left < datalen && left < NGTCP2_MIN_STREAM_DATALEN)) ||
+ (left == 0 && datalen)) {
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
@@ -412,17 +417,15 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
}
if (datalen > left) {
- ngtcp2_vec_copy(a, fr->data, fr->datacnt);
- acnt = fr->datacnt;
-
- bcnt = 0;
- ngtcp2_vec_split(b, &bcnt, a, &acnt, left, NGTCP2_MAX_STREAM_DATACNT);
+ datacnt = 0;
+ ngtcp2_vec_split(data, &datacnt, fr->data, &fr->datacnt, left,
+ NGTCP2_MAX_STREAM_DATACNT);
- assert(acnt > 0);
- assert(bcnt > 0);
+ assert(fr->datacnt > 0);
+ assert(datacnt > 0);
rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
- &nfrc, bcnt, strm->frc_objalloc, strm->mem);
+ &nfrc, datacnt, strm->frc_objalloc, strm->mem);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
@@ -435,8 +438,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
nfr->fin = fr->fin;
nfr->stream_id = fr->stream_id;
nfr->offset = fr->offset + left;
- nfr->datacnt = bcnt;
- ngtcp2_vec_copy(nfr->data, b, bcnt);
+ nfr->datacnt = datacnt;
+ ngtcp2_vec_copy(nfr->data, data, datacnt);
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
if (rv != 0) {
@@ -447,31 +450,17 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
return rv;
}
- 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_objalloc_del(frc, strm->frc_objalloc, strm->mem);
- return rv;
- }
-
- nfr = &nfrc->fr.stream;
- *nfr = *fr;
- nfr->fin = 0;
- nfr->datacnt = acnt;
- ngtcp2_vec_copy(nfr->data, a, acnt);
-
- ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ fr->fin = 0;
- *pfrc = nfrc;
+ *pfrc = frc;
return 0;
}
left -= (size_t)datalen;
- ngtcp2_vec_copy(a, fr->data, fr->datacnt);
- acnt = fr->datacnt;
+ ngtcp2_vec_copy(data, fr->data, fr->datacnt);
+ datacnt = fr->datacnt;
for (; left && ngtcp2_ksl_len(strm->tx.streamfrq);) {
unacked_offset = ngtcp2_strm_streamfrq_unacked_offset(strm);
@@ -498,9 +487,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
break;
}
- bcnt = nfr->datacnt;
-
- nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &bcnt, left,
+ nmerged = ngtcp2_vec_merge(data, &datacnt, nfr->data, &nfr->datacnt, left,
NGTCP2_MAX_STREAM_DATACNT);
if (nmerged == 0) {
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
@@ -518,54 +505,27 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
datalen += nmerged;
left -= nmerged;
- if (bcnt == 0) {
+ if (nfr->datacnt == 0) {
fr->fin = nfr->fin;
ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
continue;
}
- if (nfr->datacnt <= NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES ||
- bcnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) {
- nfr->offset += nmerged;
- nfr->datacnt = bcnt;
-
- rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
- if (rv != 0) {
- ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
- ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
- return rv;
- }
- } else {
- rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
- &sfrc, bcnt, strm->frc_objalloc, strm->mem);
- if (rv != 0) {
- ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
- ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
- return rv;
- }
-
- sfrc->fr.stream = nfrc->fr.stream;
- sfrc->fr.stream.offset += nmerged;
- sfrc->fr.stream.datacnt = bcnt;
- ngtcp2_vec_copy(sfrc->fr.stream.data, nfrc->fr.stream.data, bcnt);
+ nfr->offset += nmerged;
+ rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
-
- rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &sfrc->fr.stream.offset,
- sfrc);
- if (rv != 0) {
- ngtcp2_frame_chain_objalloc_del(sfrc, strm->frc_objalloc, strm->mem);
- ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
- return rv;
- }
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
}
break;
}
- if (acnt == fr->datacnt) {
- if (acnt > 0) {
- fr->data[acnt - 1] = a[acnt - 1];
+ if (datacnt == fr->datacnt) {
+ if (datacnt > 0) {
+ fr->data[datacnt - 1] = data[datacnt - 1];
}
*pfrc = frc;
@@ -573,19 +533,23 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
return 0;
}
- assert(acnt > fr->datacnt);
+ assert(datacnt > fr->datacnt);
rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
- &nfrc, acnt, strm->frc_objalloc, strm->mem);
+ &nfrc, datacnt, strm->frc_objalloc, strm->mem);
if (rv != 0) {
ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
return rv;
}
nfr = &nfrc->fr.stream;
- *nfr = *fr;
- nfr->datacnt = acnt;
- ngtcp2_vec_copy(nfr->data, a, acnt);
+ nfr->type = fr->type;
+ nfr->flags = fr->flags;
+ nfr->fin = fr->fin;
+ nfr->stream_id = fr->stream_id;
+ nfr->offset = fr->offset;
+ nfr->datacnt = datacnt;
+ ngtcp2_vec_copy(nfr->data, data, datacnt);
ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
@@ -682,12 +646,11 @@ int ngtcp2_strm_is_all_tx_data_fin_acked(const ngtcp2_strm *strm) {
ngtcp2_range ngtcp2_strm_get_unacked_range_after(const ngtcp2_strm *strm,
uint64_t offset) {
- ngtcp2_range gap;
-
if (strm->tx.acked_offset == NULL) {
- gap.begin = strm->tx.cont_acked_offset;
- gap.end = UINT64_MAX;
- return gap;
+ return (ngtcp2_range){
+ .begin = strm->tx.cont_acked_offset,
+ .end = UINT64_MAX,
+ };
}
return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
@@ -737,7 +700,16 @@ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) {
}
}
- return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
+ rv = ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_ksl_len(&strm->tx.acked_offset->gap) >= 4000) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
+ return 0;
}
void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm,
diff --git a/third_party/ngtcp2/lib/ngtcp2_strm.h b/third_party/ngtcp2/lib/ngtcp2_strm.h
index c72f8b9dc89..0d5d8a63a62 100644
--- a/third_party/ngtcp2/lib/ngtcp2_strm.h
+++ b/third_party/ngtcp2/lib/ngtcp2_strm.h
@@ -41,54 +41,54 @@
typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
/* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_STRM_FLAG_NONE 0x00u
+#define NGTCP2_STRM_FLAG_NONE 0x00U
/* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream
data is not allowed. */
-#define NGTCP2_STRM_FLAG_SHUT_RD 0x01u
+#define NGTCP2_STRM_FLAG_SHUT_RD 0x01U
/* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of
stream data is not allowed. */
-#define NGTCP2_STRM_FLAG_SHUT_WR 0x02u
+#define NGTCP2_STRM_FLAG_SHUT_WR 0x02U
#define NGTCP2_STRM_FLAG_SHUT_RDWR \
(NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR)
/* NGTCP2_STRM_FLAG_RESET_STREAM indicates that RESET_STREAM is sent
from the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is
also set. */
-#define NGTCP2_STRM_FLAG_RESET_STREAM 0x04u
+#define NGTCP2_STRM_FLAG_RESET_STREAM 0x04U
/* NGTCP2_STRM_FLAG_RESET_STREAM_RECVED indicates that RESET_STREAM is
received from the remote endpoint. In this case,
NGTCP2_STRM_FLAG_SHUT_RD is also set. */
-#define NGTCP2_STRM_FLAG_RESET_STREAM_RECVED 0x08u
+#define NGTCP2_STRM_FLAG_RESET_STREAM_RECVED 0x08U
/* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent
from the local endpoint. */
-#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10u
+#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10U
/* NGTCP2_STRM_FLAG_RESET_STREAM_ACKED indicates that the outgoing
RESET_STREAM is acknowledged by peer. */
-#define NGTCP2_STRM_FLAG_RESET_STREAM_ACKED 0x20u
+#define NGTCP2_STRM_FLAG_RESET_STREAM_ACKED 0x20U
/* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set
is acknowledged by a remote endpoint. */
-#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40u
+#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40U
/* NGTCP2_STRM_FLAG_ANY_ACKED indicates that any portion of stream
data, including 0 length segment, is acknowledged. */
-#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80u
+#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80U
/* NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET indicates that app_error_code
field is set. This resolves the ambiguity that the initial
app_error_code value 0 might be a proper application error code.
In this case, without this flag, we are unable to distinguish
assigned value from unassigned one. */
-#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100u
+#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100U
/* NGTCP2_STRM_FLAG_SEND_STOP_SENDING is set when STOP_SENDING frame
should be sent. */
-#define NGTCP2_STRM_FLAG_SEND_STOP_SENDING 0x200u
+#define NGTCP2_STRM_FLAG_SEND_STOP_SENDING 0x200U
/* NGTCP2_STRM_FLAG_SEND_RESET_STREAM is set when RESET_STREAM frame
should be sent. */
-#define NGTCP2_STRM_FLAG_SEND_RESET_STREAM 0x400u
+#define NGTCP2_STRM_FLAG_SEND_RESET_STREAM 0x400U
/* NGTCP2_STRM_FLAG_STOP_SENDING_RECVED indicates that STOP_SENDING is
received from the remote endpoint. In this case,
NGTCP2_STRM_FLAG_SHUT_WR is also set. */
-#define NGTCP2_STRM_FLAG_STOP_SENDING_RECVED 0x800u
+#define NGTCP2_STRM_FLAG_STOP_SENDING_RECVED 0x800U
/* NGTCP2_STRM_FLAG_ANY_SENT indicates that any STREAM frame,
including empty one, has been sent. */
-#define NGTCP2_STRM_FLAG_ANY_SENT 0x1000u
+#define NGTCP2_STRM_FLAG_ANY_SENT 0x1000U
typedef struct ngtcp2_strm ngtcp2_strm;
@@ -171,14 +171,14 @@ struct ngtcp2_strm {
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;
+ /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */
+ uint32_t flags;
};
ngtcp2_opl_entry oplent;
@@ -208,14 +208,14 @@ uint64_t ngtcp2_strm_rx_offset(const ngtcp2_strm *strm);
/*
* ngtcp2_strm_recv_reordering handles reordered data.
*
- * It returns 0 if it succeeds, or one of the following negative error
- * codes:
+ * It returns the number of bytes newly buffered if it succeeds, or
+ * one of the following negative error codes:
*
* NGTCP2_ERR_NOMEM
* Out of memory
*/
-int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
- size_t datalen, uint64_t offset);
+ngtcp2_ssize ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
+ size_t datalen, uint64_t offset);
/*
* ngtcp2_strm_update_rx_offset tells that data up to |offset| bytes
diff --git a/third_party/ngtcp2/lib/ngtcp2_transport_params.c b/third_party/ngtcp2/lib/ngtcp2_transport_params.c
index ca517532e3a..5d378176e16 100644
--- a/third_party/ngtcp2/lib/ngtcp2_transport_params.c
+++ b/third_party/ngtcp2/lib/ngtcp2_transport_params.c
@@ -150,8 +150,8 @@ ngtcp2_ssize ngtcp2_transport_params_encode_versioned(
if (params->stateless_reset_token_present) {
len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) +
- ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) +
- NGTCP2_STATELESS_RESET_TOKENLEN;
+ ngtcp2_put_uvarintlen(sizeof(params->stateless_reset_token)) +
+ sizeof(params->stateless_reset_token);
}
if (params->preferred_addr_present) {
@@ -160,7 +160,7 @@ ngtcp2_ssize ngtcp2_transport_params_encode_versioned(
preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ +
16 /* ipv6Address */ + 2 /* ipv6Port */
+ 1 + params->preferred_addr.cid.datalen /* CID */ +
- NGTCP2_STATELESS_RESET_TOKENLEN;
+ sizeof(params->preferred_addr.stateless_reset_token);
len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) +
ngtcp2_put_uvarintlen(preferred_addrlen) + preferred_addrlen;
}
@@ -523,12 +523,12 @@ int ngtcp2_transport_params_decode_versioned(int transport_params_version,
}
/* Set default values */
- memset(params, 0, sizeof(*params));
- params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
- params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
- params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
- params->active_connection_id_limit =
- NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ *params = (ngtcp2_transport_params){
+ .max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE,
+ .ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT,
+ .max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY,
+ .active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT,
+ };
p = end = data;
@@ -626,7 +626,8 @@ int ngtcp2_transport_params_decode_versioned(int transport_params_version,
}
len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ +
2 /* ipv6Port */
- + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN;
+ + 1 /* cid length */ +
+ sizeof(params->preferred_addr.stateless_reset_token);
if (valuelen < len) {
return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
}
diff --git a/third_party/ngtcp2/lib/ngtcp2_transport_params.h b/third_party/ngtcp2/lib/ngtcp2_transport_params.h
index c077f06a9dd..d98d034b3c2 100644
--- a/third_party/ngtcp2/lib/ngtcp2_transport_params.h
+++ b/third_party/ngtcp2/lib/ngtcp2_transport_params.h
@@ -45,16 +45,16 @@ typedef uint64_t ngtcp2_transport_param_id;
#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI 0x07
#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI 0x08
#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI 0x09
-#define NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT 0x0a
-#define NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY 0x0b
-#define NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION 0x0c
-#define NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS 0x0d
-#define NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT 0x0e
-#define NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID 0x0f
+#define NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT 0x0A
+#define NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY 0x0B
+#define NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION 0x0C
+#define NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS 0x0D
+#define NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT 0x0E
+#define NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID 0x0F
#define NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID 0x10
/* https://datatracker.ietf.org/doc/html/rfc9221 */
#define NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE 0x20
-#define NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT 0x2ab2
+#define NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT 0x2AB2
/* https://datatracker.ietf.org/doc/html/rfc9368 */
#define NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION 0x11
diff --git a/third_party/ngtcp2/lib/ngtcp2_vec.c b/third_party/ngtcp2/lib/ngtcp2_vec.c
index dbca8691d64..f94c6218d53 100644
--- a/third_party/ngtcp2/lib/ngtcp2_vec.c
+++ b/third_party/ngtcp2/lib/ngtcp2_vec.c
@@ -30,8 +30,11 @@
#include "ngtcp2_str.h"
ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) {
- vec->base = (uint8_t *)base;
- vec->len = len;
+ *vec = (ngtcp2_vec){
+ .base = (uint8_t *)base,
+ .len = len,
+ };
+
return vec;
}
@@ -102,8 +105,7 @@ ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
memcpy(dst, src + i, sizeof(ngtcp2_vec) * nmove);
}
- dst[0].len -= left;
- dst[0].base += left;
+ ngtcp2_vec_drop(&dst[0], left);
src[i].len = left;
if (nmove == 0) {
@@ -136,8 +138,7 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
a->len = left;
a->base = b->base;
- b->len -= left;
- b->base += left;
+ ngtcp2_vec_drop(b, left);
return left;
}
@@ -158,13 +159,14 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
} else if (*pdstcnt == maxcnt) {
break;
} else {
- dst[*pdstcnt].len = left;
- dst[*pdstcnt].base = b->base;
+ dst[*pdstcnt] = (ngtcp2_vec){
+ .base = b->base,
+ .len = left,
+ };
++*pdstcnt;
}
- b->len -= left;
- b->base += left;
+ ngtcp2_vec_drop(b, left);
left = 0;
break;
@@ -199,8 +201,10 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
}
if (src[i].len > left) {
- dst[j].base = src[i].base;
- dst[j].len = left;
+ dst[j] = (ngtcp2_vec){
+ .base = src[i].base,
+ .len = left,
+ };
return j + 1;
}
@@ -217,3 +221,14 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) {
memcpy(dst, src, sizeof(ngtcp2_vec) * cnt);
}
+
+void ngtcp2_vec_split_at(ngtcp2_vec *dst, ngtcp2_vec *src, size_t offset) {
+ assert(offset < src->len);
+
+ *dst = (ngtcp2_vec){
+ .base = src->base + offset,
+ .len = src->len - offset,
+ };
+
+ src->len = offset;
+}
diff --git a/third_party/ngtcp2/lib/ngtcp2_vec.h b/third_party/ngtcp2/lib/ngtcp2_vec.h
index 55e735d164e..fe6a47a029a 100644
--- a/third_party/ngtcp2/lib/ngtcp2_vec.h
+++ b/third_party/ngtcp2/lib/ngtcp2_vec.h
@@ -33,13 +33,6 @@
#include "ngtcp2_mem.h"
-/*
- * ngtcp2_vec_lit is a convenient macro to fill the object pointed by
- * |DEST| with the literal string |LIT|.
- */
-#define ngtcp2_vec_lit(DEST, LIT) \
- ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST))
-
/*
* ngtcp2_vec_init initializes |vec| with the given parameters. It
* returns |vec|.
@@ -103,4 +96,28 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
*/
void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt);
+/*
+ * ngtcp2_vec_split_at splits |src| at the |offset|. Caller must
+ * ensure that offset < src->len. This function assigns the right
+ * part of vector into |dst|.
+ */
+void ngtcp2_vec_split_at(ngtcp2_vec *dst, ngtcp2_vec *src, size_t offset);
+
+/*
+ * ngtcp2_vec_end returns the one beyond the last offset of |v|.
+ */
+static inline uint8_t *ngtcp2_vec_end(const ngtcp2_vec *v) {
+ return v->base + v->len;
+}
+
+/*
+ * ngtcp2_vec_drop removes the first |n| bytes from |v| by adjusting
+ * its base and len fields. This function assumes |v|->len > 0 &&
+ * |v|->len >= n.
+ */
+static inline void ngtcp2_vec_drop(ngtcp2_vec *v, size_t n) {
+ v->base += n;
+ v->len -= n;
+}
+
#endif /* !defined(NGTCP2_VEC_H) */
diff --git a/third_party/ngtcp2/lib/ngtcp2_window_filter.c b/third_party/ngtcp2/lib/ngtcp2_window_filter.c
index 39f3d408a74..707cd570799 100644
--- a/third_party/ngtcp2/lib/ngtcp2_window_filter.c
+++ b/third_party/ngtcp2/lib/ngtcp2_window_filter.c
@@ -39,7 +39,7 @@
void ngtcp2_window_filter_init(ngtcp2_window_filter *wf,
uint64_t window_length) {
wf->window_length = window_length;
- memset(wf->estimates, 0xff, sizeof(wf->estimates));
+ memset(wf->estimates, 0xFF, sizeof(wf->estimates));
}
void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample,
diff --git a/third_party/ngtcp2/wscript b/third_party/ngtcp2/wscript
index f4cfd1c7064..399790c50c1 100644
--- a/third_party/ngtcp2/wscript
+++ b/third_party/ngtcp2/wscript
@@ -72,9 +72,11 @@ def build(bld):
lib/ngtcp2_balloc.c
lib/ngtcp2_bbr.c
lib/ngtcp2_buf.c
+ lib/ngtcp2_callbacks.c
lib/ngtcp2_cc.c
lib/ngtcp2_cid.c
lib/ngtcp2_conn.c
+ lib/ngtcp2_conn_info.c
lib/ngtcp2_conv.c
lib/ngtcp2_crypto.c
lib/ngtcp2_dcidtr.c
@@ -89,6 +91,7 @@ def build(bld):
lib/ngtcp2_objalloc.c
lib/ngtcp2_opl.c
lib/ngtcp2_path.c
+ lib/ngtcp2_pcg.c
lib/ngtcp2_pkt.c
lib/ngtcp2_pmtud.c
lib/ngtcp2_ppe.c
@@ -96,6 +99,7 @@ def build(bld):
lib/ngtcp2_pv.c
lib/ngtcp2_qlog.c
lib/ngtcp2_range.c
+ lib/ngtcp2_ratelim.c
lib/ngtcp2_ringbuf.c
lib/ngtcp2_rob.c
lib/ngtcp2_rst.c
--
2.53.0
From c42695f12eea3b3d113114e535be0de950333278 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@samba.org>
Date: Tue, 24 Mar 2026 15:00:21 +0100
Subject: [PATCH 66/66] wafsamba: Add -D_FORTIFY_SOURCE=3 when stack protector
is enabled
The capability check in SAMBA_CONFIG_H() already tests that the compiler
accepts both -Wp,-D_FORTIFY_SOURCE and the stack protector flag
together, but only the stack protector flag was added to EXTRA_CFLAGS on
success.
The glibc normally silently downgrades to the supported level if the on
specified is not supported.
Note that -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 only sets it if not
already defined.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16040
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Anoop C S <anoopcs@samba.org>
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
Autobuild-Date(master): Fri Mar 27 08:33:09 UTC 2026 on atb-devel-224
(cherry picked from commit 333ac047c3fc151222e5ee6aaa75452276b0031e)
---
buildtools/wafsamba/samba_autoconf.py | 7 ++++++-
script/autobuild.py | 6 ------
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py
index b777391a038..979d5a3972f 100644
--- a/buildtools/wafsamba/samba_autoconf.py
+++ b/buildtools/wafsamba/samba_autoconf.py
@@ -733,11 +733,16 @@ def SAMBA_CONFIG_H(conf, path=None):
}
''',
execute=0,
- cflags=[ '-Werror', '-Wp,-D_FORTIFY_SOURCE=2', stack_protect_flag],
+ cflags=[
+ '-Werror',
+ '-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3',
+ stack_protect_flag
+ ],
mandatory=False,
msg='Checking if compiler accepts %s' % (stack_protect_flag))
if flag_supported:
conf.ADD_CFLAGS('%s' % (stack_protect_flag))
+ conf.ADD_CFLAGS('-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3')
break
flag_supported = conf.check(fragment='''
diff --git a/script/autobuild.py b/script/autobuild.py
index 97d44a07ee4..1324c9b8c2e 100755
--- a/script/autobuild.py
+++ b/script/autobuild.py
@@ -206,13 +206,7 @@ try:
except ImportError:
pass
-# on ubuntu gcc implies _FORTIFY_SOURCE
-# before 24.04 it was _FORTIFY_SOURCE=2
-# and 24.04 has _FORTIFY_SOURCE=3
-# so we do not specify it explicitly.
samba_o3_cflags = "-O3"
-if not is_ubuntu:
- samba_o3_cflags += " -Wp,-D_FORTIFY_SOURCE=2"
def format_option(name, value=None):
"""Format option as str list."""
--
2.53.0