Skip to content

Address Discovery and Server Initiated Connection Migration and NAT Traversal - #3

Merged
masa-koz merged 51 commits into
seera-mainfrom
masa-koz/nat-traversal
Jan 26, 2026
Merged

Address Discovery and Server Initiated Connection Migration and NAT Traversal#3
masa-koz merged 51 commits into
seera-mainfrom
masa-koz/nat-traversal

Conversation

@masa-koz

@masa-koz masa-koz commented Jan 5, 2026

Copy link
Copy Markdown

Description

This pull request introduces several new features and enhancements related to connection migration, address management, and NAT traversal in the QUIC implementation. It adds new transport parameters and connection events, expands the connection state and data structures to support multiple addresses, and updates documentation to reflect these changes. Some CI workflow and build configuration updates are also included.

Protocol and Feature Enhancements:

  • Added new transport parameters for Observed Address, Server Migration, and NAT Traverse, along with their encoding logic in the handshake (src/core/crypto_tls.c, src/core/crypto.c). [1] [2] [3] [4]
  • Introduced new connection state flags and fields to track negotiation and runtime state for Observed Address, Server Migration, and NAT Traverse features (src/core/connection.h). [1] [2] [3] [4]
  • Expanded the QUIC_CONNECTION structure to manage lists of bound and candidate addresses, and defined associated structs (src/core/connection.h). [1] [2]
  • Implemented logic to add the local address to the connection's bound addresses upon creation (src/core/binding.c).
  • Added new functions to process ADD_ADDRESS and REMOVE_ADDRESS frames and to send punch probes (src/core/connection.h).

API and Documentation Updates:

  • Documented new connection settings and parameters for server migration, address handling, and NAT traversal in docs/Settings.md. [1] [2]
  • Added documentation for new connection events: NOTIFY_OBSERVED_ADDRESS, NOTIFY_REMOTE_ADDRESS_ADDED, PATH_VALIDATED, and NOTIFY_REMOTE_ADDRESS_REMOVED, including their data structures and preview status (docs/api/QUIC_CONNECTION_EVENT.md). [1] [2] [3]

Build and CI Configuration:

  • Enabled the preview-api feature by default in Cargo.toml.
  • Removed the macos-latest-xlarge runner from the GitHub Actions matrix in .github/workflows/cargo.yml.
  • Added a step to set up the .NET 10 SDK in the test workflow for Linux and macOS in .github/workflows/test.yml.

Bug Fixes and Minor Improvements:

  • Fixed a logic bug to prevent double-handling of handshake confirmation in QuicCryptoHandshakeConfirmed and reordered key discarding for correctness (src/core/crypto.c). [1] [2]

These changes lay the groundwork for advanced connection migration and NAT traversal features, improve address management, and enhance the QUIC protocol's flexibility and observability.

Testing

Added for Server Initiated Migration

Documentation

Added

Comment thread src/core/settings.c
if (!Settings->IsSet.ServerMigrationEnabled) {
Value = QUIC_DEFAULT_SERVER_MIGRATION_ENABLED;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
CxPlatStorageReadValue
has no external side effects).

Copilot Autofix

AI 8 months ago

In general, when an expression is used only for its side effects and its value is intentionally ignored, you should make that explicit, typically by casting the call to (void) or, alternatively, by assigning it to a dummy variable or checking it. This documents the intent and silences analyzers that otherwise flag the expression as “having no effect”.

Here, the simplest, lowest‑risk fix is to cast the CxPlatStorageReadValue calls to void. We keep the call exactly as is and just wrap it in (void), e.g.:

(void)CxPlatStorageReadValue(...);

This indicates that we are deliberately discarding the return value, while still relying on the function’s side effects on Value and ValueLen. To be consistent and avoid similar warnings at other locations, we should apply this change to all calls in this contiguous block (lines 1472, 1483, 1494, 1504, 1514), not just the one at 1494, since they are structurally identical and likely analyzed the same way. No new methods or imports are required; we only modify the function calls in src/core/settings.c within the shown snippet.

Suggested changeset 1
src/core/settings.c

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/core/settings.c b/src/core/settings.c
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -1469,7 +1469,7 @@
     if (!Settings->IsSet.StreamMultiReceiveEnabled) {
         Value = QUIC_DEFAULT_STREAM_MULTI_RECEIVE_ENABLED;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_STREAM_MULTI_RECEIVE_ENABLED,
             (uint8_t*)&Value,
@@ -1480,7 +1480,7 @@
     if (!Settings->IsSet.ConnIDGenDisabled) {
         Value = QUIC_DEFAULT_CONN_ID_GENERATION_DISABLED;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_CONN_ID_GENERATION_DISABLED,
             (uint8_t*)&Value,
@@ -1491,7 +1491,7 @@
     if (!Settings->IsSet.ServerMigrationEnabled) {
         Value = QUIC_DEFAULT_SERVER_MIGRATION_ENABLED;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_SERVER_MIGRATION_ENABLED,
             (uint8_t*)&Value,
@@ -1501,7 +1501,7 @@
     if (!Settings->IsSet.AddAddressMode) {
         Value = QUIC_DEFAULT_ADD_ADDRESS_MODE;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_ADD_ADDRESS_MODE,
             (uint8_t*)&Value,
@@ -1511,7 +1511,7 @@
     if (!Settings->IsSet.IgnoreUnreachable) {
         Value = QUIC_DEFAULT_IGNORE_UNREACHABLE;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_IGNORE_UNREACHABLE,
             (uint8_t*)&Value,
EOF
@@ -1469,7 +1469,7 @@
if (!Settings->IsSet.StreamMultiReceiveEnabled) {
Value = QUIC_DEFAULT_STREAM_MULTI_RECEIVE_ENABLED;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_STREAM_MULTI_RECEIVE_ENABLED,
(uint8_t*)&Value,
@@ -1480,7 +1480,7 @@
if (!Settings->IsSet.ConnIDGenDisabled) {
Value = QUIC_DEFAULT_CONN_ID_GENERATION_DISABLED;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_CONN_ID_GENERATION_DISABLED,
(uint8_t*)&Value,
@@ -1491,7 +1491,7 @@
if (!Settings->IsSet.ServerMigrationEnabled) {
Value = QUIC_DEFAULT_SERVER_MIGRATION_ENABLED;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_SERVER_MIGRATION_ENABLED,
(uint8_t*)&Value,
@@ -1501,7 +1501,7 @@
if (!Settings->IsSet.AddAddressMode) {
Value = QUIC_DEFAULT_ADD_ADDRESS_MODE;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_ADD_ADDRESS_MODE,
(uint8_t*)&Value,
@@ -1511,7 +1511,7 @@
if (!Settings->IsSet.IgnoreUnreachable) {
Value = QUIC_DEFAULT_IGNORE_UNREACHABLE;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_IGNORE_UNREACHABLE,
(uint8_t*)&Value,
Copilot is powered by AI and may make mistakes. Always verify output.
Comment thread src/core/settings.c
if (!Settings->IsSet.AddAddressMode) {
Value = QUIC_DEFAULT_ADD_ADDRESS_MODE;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
CxPlatStorageReadValue
has no external side effects).

Copilot Autofix

AI 8 months ago

To fix this, the call to CxPlatStorageReadValue should be explicitly cast to void so that it is clear its return value is intentionally ignored. This does not alter program behavior but signals both to static analysis tools and human readers that the function call is kept only for its (potential) side effects, even if those are not visible to the analyzer.

Concretely, in src/core/settings.c, within QuicSettingsSetDefault, locate the block starting at line 1501 (if (!Settings->IsSet.AddAddressMode) {). In that block, change the plain expression statement

CxPlatStorageReadValue(
    Storage,
    QUIC_SETTING_ADD_ADDRESS_MODE,
    (uint8_t*)&Value,
    &ValueLen);

to be explicitly cast to void:

(void)CxPlatStorageReadValue(
    Storage,
    QUIC_SETTING_ADD_ADDRESS_MODE,
    (uint8_t*)&Value,
    &ValueLen);

This aligns with the guidance given in the background and does not require any new methods, imports, or definitions.

Suggested changeset 1
src/core/settings.c

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/core/settings.c b/src/core/settings.c
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -1501,7 +1501,7 @@
     if (!Settings->IsSet.AddAddressMode) {
         Value = QUIC_DEFAULT_ADD_ADDRESS_MODE;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_ADD_ADDRESS_MODE,
             (uint8_t*)&Value,
EOF
@@ -1501,7 +1501,7 @@
if (!Settings->IsSet.AddAddressMode) {
Value = QUIC_DEFAULT_ADD_ADDRESS_MODE;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_ADD_ADDRESS_MODE,
(uint8_t*)&Value,
Copilot is powered by AI and may make mistakes. Always verify output.
Comment thread src/core/settings.c
if (!Settings->IsSet.IgnoreUnreachable) {
Value = QUIC_DEFAULT_IGNORE_UNREACHABLE;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
CxPlatStorageReadValue
has no external side effects).

Copilot Autofix

AI 8 months ago

In general, when a function’s return value is intentionally ignored but the function is still called for its side effects (such as writing via pointer parameters), static analysis tools may flag “expression has no effect”. The standard, explicit way to indicate that the return value is intentionally unused is to cast the call to void, e.g. (void)func(...);. This both preserves behavior and documents intent.

For this file, the best fix without changing functionality is to modify the specific call on line 1514 from

CxPlatStorageReadValue(
    Storage,
    QUIC_SETTING_IGNORE_UNREACHABLE,
    (uint8_t*)&Value,
    &ValueLen);

to explicitly discard the return value:

(void)CxPlatStorageReadValue(
    Storage,
    QUIC_SETTING_IGNORE_UNREACHABLE,
    (uint8_t*)&Value,
    &ValueLen);

This is consistent with the rule’s recommendation and matches standard C practice for intentionally-unused return values. No new methods, imports, or definitions are required. The change should be limited to src/core/settings.c at the shown region; no other behavior in the function is altered.

Suggested changeset 1
src/core/settings.c

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/core/settings.c b/src/core/settings.c
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -1511,7 +1511,7 @@
     if (!Settings->IsSet.IgnoreUnreachable) {
         Value = QUIC_DEFAULT_IGNORE_UNREACHABLE;
         ValueLen = sizeof(Value);
-        CxPlatStorageReadValue(
+        (void)CxPlatStorageReadValue(
             Storage,
             QUIC_SETTING_IGNORE_UNREACHABLE,
             (uint8_t*)&Value,
EOF
@@ -1511,7 +1511,7 @@
if (!Settings->IsSet.IgnoreUnreachable) {
Value = QUIC_DEFAULT_IGNORE_UNREACHABLE;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
(void)CxPlatStorageReadValue(
Storage,
QUIC_SETTING_IGNORE_UNREACHABLE,
(uint8_t*)&Value,
Copilot is powered by AI and may make mistakes. Always verify output.
Comment thread src/core/connection.c Fixed
@masa-koz
masa-koz force-pushed the masa-koz/nat-traversal branch from d84791b to 64873a6 Compare January 5, 2026 11:56
@masa-koz
masa-koz force-pushed the masa-koz/nat-traversal branch from 64873a6 to 9319b4e Compare January 5, 2026 13:05
Comment thread src/core/connection.c Fixed
sudo chmod -R 777 artifacts
- name: Setup .NET 10 SDK
if: matrix.vec.plat == 'linux' || matrix.vec.plat == 'macos'
uses: actions/setup-dotnet@v4

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium test

score is 9: GitHub-owned GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help
@masa-koz masa-koz changed the title Server Initiated Connection Migration and NAT Traversal Address Discovery and Server Initiated Connection Migration and NAT Traversal Jan 26, 2026
@masa-koz
masa-koz merged commit 0c0a4b4 into seera-main Jan 26, 2026
502 of 519 checks passed
@masa-koz
masa-koz deleted the masa-koz/nat-traversal branch February 24, 2026 07:51
masa-koz pushed a commit that referenced this pull request Mar 5, 2026
…back type (microsoft#5839)

Issue: UndefinedBehaviorSanitizer: undefined-behavior
../../src/ext/msquic/src/platform/tls_openssl.c:1251:17

The TicketLength parameter was declared as uint16_t in the function
definition but the CXPLAT_TLS_RECEIVE_TICKET_CALLBACK typedef specifies
uint32_t. This mismatch caused an undefined behavior sanitizer error
when the function was called through the callback pointer.

The following is the stack:
```
/usr/bin/llvm-symbolizer-21: /usr/local/lib/libcurl.so.4: no version information available (required by /usr/bin/llvm-symbolizer-21)
../../src/ext/msquic/src/platform/tls_openssl.c:1251:17: runtime error: call to function QuicConnRecvResumptionTicket through pointer to incorrect function type 'unsigned char (*)(struct QUIC_CONNECTION *, unsigned int, const unsigned char *)'
/build/out/msquic-build/../../src/ext/msquic/src/core/connection.c:2113: note: QuicConnRecvResumptionTicket defined here
    #0 0x780a958641e1 in CxPlatTlsOnClientSessionTicketReceived /build/out/msquic-build/../../src/ext/msquic/src/platform/tls_openssl.c:1251:17
    #1 0x780a958b2ae1 in ssl_update_cache (/build/out/msquic/lib/libmsquic.so.2+0x4b2ae1) (BuildId: 881896c37f67e076004a0fa8f1497de54832e212)
    #2 0x780a958e21fd in tls_process_new_session_ticket (/build/out/msquic/lib/libmsquic.so.2+0x4e21fd) (BuildId: 881896c37f67e076004a0fa8f1497de54832e212)
    #3 0x780a958dd1d7 in state_machine statem.c
    #4 0x780a958c9ca0 in ssl3_read_bytes (/build/out/msquic/lib/libmsquic.so.2+0x4c9ca0) (BuildId: 881896c37f67e076004a0fa8f1497de54832e212)
    #5 0x780a958a4eb5 in ssl3_read_internal s3_lib.c
    #6 0x780a958afebe in SSL_read (/build/out/msquic/lib/libmsquic.so.2+0x4afebe) (BuildId: 881896c37f67e076004a0fa8f1497de54832e212)
    #7 0x780a95871fea in CxPlatTlsProcessData /build/out/msquic-build/../../src/ext/msquic/src/platform/tls_openssl.c:3319:9
    #8 0x780a957678eb in QuicCryptoProcessData /build/out/msquic-build/../../src/ext/msquic/src/core/crypto.c:1959:9
    #9 0x780a95772ca4 in QuicCryptoProcessFrame /build/out/msquic-build/../../src/ext/msquic/src/core/crypto.c:1347:14
    #10 0x780a9573620b in QuicConnRecvFrames /build/out/msquic-build/../../src/ext/msquic/src/core/connection.c:4622:17
    #11 0x780a957405ce in QuicConnRecvDatagramBatch /build/out/msquic-build/../../src/ext/msquic/src/core/connection.c:5604:20
    #12 0x780a95743c88 in QuicConnRecvDatagrams /build/out/msquic-build/../../src/ext/msquic/src/core/connection.c:5848:9
    #13 0x780a95745c40 in QuicConnFlushRecv /build/out/msquic-build/../../src/ext/msquic/src/core/connection.c:5958:5
    #14 0x780a95759e77 in QuicConnDrainOperations /build/out/msquic-build/../../src/ext/msquic/src/core/connection.c:7880:18
    #15 0x780a956cd8de in QuicWorkerProcessConnection /build/out/msquic-build/../../src/ext/msquic/src/core/worker.c:658:9
    #16 0x780a956c708a in QuicWorkerLoop /build/out/msquic-build/../../src/ext/msquic/src/core/worker.c:881:9
    #17 0x780a95852f36 in CxPlatRunExecutionContexts /build/out/msquic-build/../../src/ext/msquic/src/platform/platform_worker.c:667:18
    #18 0x780a95853874 in CxPlatWorkerPoolWorkerPoll /build/out/msquic-build/../../src/ext/msquic/src/platform/platform_worker.c:707:5
    #19 0x780a9563917c in MsQuicExecutionPoll /build/out/msquic-build/../../src/ext/msquic/src/core/library.c:2839:23
 ... ...

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../../src/ext/msquic/src/platform/tls_openssl.c:1251:17 

```

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: leikong <11276068+leikong@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants