You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix OBSERVED_ADDRESS frame codepoints and make address discovery opt-in (#43)
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
Copy file name to clipboardExpand all lines: docs/Settings.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -70,6 +70,8 @@ The following settings are available via registry as well as via [QUIC_SETTINGS]
70
70
| Server Initiated Migration | uint8_t | ServerMigrationEnabled | 0 (FALSE) | Enable Server Initiated Migration. |
71
71
| ADD_ADDRESS handling mode | uint8_t | AddAddressMode | 0 (AUTO) | Control handling of ADD_ADDRESS Frame |
72
72
| IgnoreUnreachable | uint8_t | IgnoreUnreachable | 0 (FALSE) | Ignore Unreachable during the Handshake |
73
+
| SendObservedAddressReports | uint8_t | SendObservedAddressReports | 0 (FALSE) | Report the peer's observed address to it via OBSERVED_ADDRESS frames. Frames are only sent if the peer also asked to receive them. |
74
+
| ReceiveObservedAddressReports | uint8_t | ReceiveObservedAddressReports | 0 (FALSE) | Ask the peer to report this endpoint's observed address. Reports arrive as `QUIC_CONNECTION_EVENT_NOTIFY_OBSERVED_ADDRESS`. |
0 commit comments