Skip to content

Fix OBSERVED_ADDRESS frame codepoints and make address discovery opt-in - #43

Merged
masa-koz merged 5 commits into
seera-networks:seera-mainfrom
masa-koz:observed-address-frame-codepoints
Jul 20, 2026
Merged

Fix OBSERVED_ADDRESS frame codepoints and make address discovery opt-in#43
masa-koz merged 5 commits into
seera-networks:seera-mainfrom
masa-koz:observed-address-frame-codepoints

Conversation

@masa-koz

@masa-koz masa-koz commented Jul 19, 2026

Copy link
Copy Markdown

Two commits against the address discovery extension.

1. Use draft codepoints for OBSERVED_ADDRESS frame types

QUIC_FRAME_OBSERVED_ADDRESS_V4/_V6 in src/core/frame.h were 0x9f81/0x9f82, with the draft-ietf-quic-address-discovery codepoints (0x9f81a6/0x9f81a7) left only as a trailing comment.

The transport parameter ID (QUIC_TP_ID_OBSERVED_ADDRESS = 0x9f81a176) already matches the draft, so the extension negotiates successfully with other implementations — and only then do the frames themselves fail to parse. Reproduced against noq 1.0.1 (quinn-based, draft codepoints) over loopback:

quicsample: Connected -> Shut down by transport, 0x5   (QUIC_STATUS_INTERNAL_ERROR)
noq server: aborted by peer: received a frame that was badly formatted

src/plugins/dbg/quictypes.h already carried the correct draft values, so the two enums were out of sync.

src/core/unittest/SpinFrame.cpp widens FrameType to uint32_t so the case labels stay within the switch operand's range (-Werror=switch-outside-range otherwise fails the build). The fuzzed value is still drawn from the 16-bit space, exactly as before.

2. Add opt-in settings for observed address reports

The extension was unconditionally enabled: msquic always advertised the transport parameter, always sent it with a hardcoded value of 2 ("both"), discarded the peer's value (the TODO - Pass value? in crypto_tls.c), and always accepted OBSERVED_ADDRESS frames. An app had no way to turn any of it off.

Two new settings, mirroring the noq/quinn API:

Setting Default Meaning
SendObservedAddressReports FALSE Report the peer's observed address to it
ReceiveObservedAddressReports FALSE Ask the peer to report ours
  • The extension is now opt-in. Previously it was always on; both settings default to FALSE.
  • The transport parameter is sent only when at least one direction is enabled, and its value now states which — SEND_ONLY (0) / RECEIVE_ONLY (1) / BOTH (2), per the draft.
  • The peer's value is kept in QUIC_TRANSPORT_PARAMETERS::ObservedAddressRole and honored: frames are sent only when this endpoint enabled sending and the peer asked to receive.
  • An OBSERVED_ADDRESS frame arriving when we never asked to receive one is now a PROTOCOL_VIOLATION instead of being silently accepted.
  • quicsample gains -observed_address_send / -observed_address_recv on both client and server.

Plumbed through the usual places: msquic.h (IsSet + Flags bits), msquic.hpp setters, settings.h/settings.c (defaults, copy, apply, registry load, both dumps, to/from-internal), quicdef.h defaults and registry names, docs/Settings.md, SettingsTest SET+GET entries. Clog headers and clog.sidecar were regenerated with the clog tool from submodules/clog.

Verification

Loopback against noq-mp-sample (unmodified; noq enables both directions), with the rebuilt quicsample:

Frame codepoints — before commit 1:

Scenario Result
quicsample -client -> noq server Shut down by transport, 0x5
quicsample -client -multipath -> noq server ❌ same
noq client -> quicsample -server ✅ (noq client never advertises the TP)

After both commits:

quicsample client flags Observed Address reports Data exchange
none (defaults off) 0
-observed_address_recv 1
-observed_address_send 0 (noq honors send-only)
both 1
-multipath -observed_address_recv 2 (one per path)

msquic-to-msquic behaves the same: a server started with -observed_address_send reports only to a client that passed -observed_address_recv.

msquiccoretest: 540 passed, 0 failed (3 pre-existing skips are the Linux-unsupported storage tests). TransportParamTest.ObservedAddress was extended to round-trip all three role values.

Notes

  • Because the frame codepoints no longer fit in 16 bits, SpinFrame can no longer generate them, so its OBSERVED_ADDRESS cases become unreachable — the same situation as the existing ADD_ADDRESS/PUNCH_ME_NOW cases. Fuzzing those would need the loop to draw from a table of known frame types rather than rejection-sample a 16-bit space; left out of this change.
  • Rust bindings are updated: src/rs/settings.rs gains the two setters, and linux_bindings.rs was regenerated with cargo build --features overwrite. win_bindings.rs cannot be regenerated on Linux, so the identical hunks were applied by hand — the diffs of the two files match line for line and both parse, but it has not been compiled for a Windows target. Verified with cargo test --features preview-api (13 passed).
  • The C# bindings were not updated; they need their generator re-run. Note they are already missing MultipathEnabled.
  • Only the Linux clog artifacts were regenerated; Windows builds regenerate theirs as part of the build.

🤖 Generated with Claude Code

masa-koz and others added 2 commits July 19, 2026 12:47
QUIC_FRAME_OBSERVED_ADDRESS_V4/V6 were 0x9f81/0x9f82, with the
draft-ietf-quic-address-discovery codepoints (0x9f81a6/0x9f81a7) left
only as a comment. The transport parameter ID (0x9f81a176) already
matched the draft, so the extension negotiates successfully with other
implementations and then the frames themselves fail to parse.

Against noq 1.0.1 (quinn-based, draft codepoints) this reproduced as:

  quicsample: Connected -> Shut down by transport, 0x5
  noq server: aborted by peer: received a frame that was badly formatted

Restore the draft values. SpinFrame's FrameType widens to uint32_t so
the case labels for these frame types stay within the switch operand's
range; the fuzzed value is still drawn from the 16-bit space, as before.

Verified against noq-mp-sample over loopback: the msquic client now logs
"Local Address: ... Observed Address: ...", and plain, -multipath, and
reverse-direction (noq client -> msquic server) runs all complete.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The address discovery extension was unconditionally enabled: msquic
always advertised the observed_address transport parameter, always sent
it with the hardcoded value 2 ("both"), discarded the value the peer
sent (the "TODO - Pass value?" in crypto_tls.c), and always accepted
OBSERVED_ADDRESS frames. An app had no way to turn any of it off.

Add two settings, mirroring the noq/quinn API:

  SendObservedAddressReports     - report the peer's observed address
  ReceiveObservedAddressReports  - ask the peer for ours

Both default to FALSE, so the extension is now opt-in. The transport
parameter is sent only when at least one direction is enabled, and its
value states which (SEND_ONLY / RECEIVE_ONLY / BOTH per
draft-ietf-quic-address-discovery). The peer's value is now kept in
QUIC_TRANSPORT_PARAMETERS::ObservedAddressRole and honored: frames are
sent only when this endpoint enabled sending AND the peer asked to
receive, and an OBSERVED_ADDRESS frame arriving when we never asked to
receive one is a PROTOCOL_VIOLATION rather than silently accepted.

quicsample gains -observed_address_send / -observed_address_recv on both
the client and the server to exercise this.

Verified over loopback against noq-mp-sample (which enables both
directions): with no flags no reports are exchanged; -observed_address_recv
yields one report per path (two under -multipath); -observed_address_send
alone yields none, as noq honors the send-only role. msquic-to-msquic
behaves the same, and all 540 msquiccoretest tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@masa-koz masa-koz changed the title Use draft codepoints for OBSERVED_ADDRESS frame types Fix OBSERVED_ADDRESS frame codepoints and make address discovery opt-in Jul 19, 2026
Comment thread src/core/settings.c
if (!Settings->IsSet.SendObservedAddressReports) {
Value = QUIC_DEFAULT_SEND_OBSERVED_ADDRESS_REPORTS;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
Comment thread src/core/settings.c
if (!Settings->IsSet.ReceiveObservedAddressReports) {
Value = QUIC_DEFAULT_RECEIVE_OBSERVED_ADDRESS_REPORTS;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
masa-koz and others added 2 commits July 19, 2026 13:46
Add set_SendObservedAddressReports / set_ReceiveObservedAddressReports
to the Settings wrapper, alongside the other preview-api bit flags.

linux_bindings.rs was regenerated with `cargo build --features overwrite`;
the change is limited to accessors for the two new IsSet/Flags bits and
the narrowed RESERVED/ReservedFlags widths. win_bindings.rs cannot be
regenerated on Linux, so the identical hunks were applied to it — the
diffs of the two files match line for line, and both parse.

Verified with `cargo build --features preview-api` and `cargo test
--features preview-api` (13 passed, 0 failed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The NOTIFY_OBSERVED_ADDRESS event was only handled in the client
callback, so a server started with -observed_address_recv silently
dropped the reports a peer sent it, making the client-to-server
direction impossible to observe with the sample alone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.79592% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/connection.c 66.66% 3 Missing ⚠️
src/core/settings.c 94.11% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

EventTest no longer expects NOTIFY_OBSERVED_ADDRESS, and the
AddressDiscovery test enables the reports explicitly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@masa-koz
masa-koz marked this pull request as ready for review July 20, 2026 00:37
@masa-koz
masa-koz merged commit 2fb6c04 into seera-networks:seera-main Jul 20, 2026
477 of 487 checks passed
@masa-koz
masa-koz deleted the observed-address-frame-codepoints branch July 20, 2026 00:37
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