Fix OBSERVED_ADDRESS frame codepoints and make address discovery opt-in - #43
Merged
masa-koz merged 5 commits intoJul 20, 2026
Conversation
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>
| if (!Settings->IsSet.SendObservedAddressReports) { | ||
| Value = QUIC_DEFAULT_SEND_OBSERVED_ADDRESS_REPORTS; | ||
| ValueLen = sizeof(Value); | ||
| CxPlatStorageReadValue( |
| if (!Settings->IsSet.ReceiveObservedAddressReports) { | ||
| Value = QUIC_DEFAULT_RECEIVE_OBSERVED_ADDRESS_REPORTS; | ||
| ValueLen = sizeof(Value); | ||
| CxPlatStorageReadValue( |
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 Report❌ Patch coverage is
📢 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two commits against the address discovery extension.
1. Use draft codepoints for OBSERVED_ADDRESS frame types
QUIC_FRAME_OBSERVED_ADDRESS_V4/_V6insrc/core/frame.hwere0x9f81/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:src/plugins/dbg/quictypes.halready carried the correct draft values, so the two enums were out of sync.src/core/unittest/SpinFrame.cppwidensFrameTypetouint32_tso thecaselabels stay within the switch operand's range (-Werror=switch-outside-rangeotherwise 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 (theTODO - Pass value?incrypto_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:
SendObservedAddressReportsFALSEReceiveObservedAddressReportsFALSEFALSE.SEND_ONLY(0) /RECEIVE_ONLY(1) /BOTH(2), per the draft.QUIC_TRANSPORT_PARAMETERS::ObservedAddressRoleand honored: frames are sent only when this endpoint enabled sending and the peer asked to receive.PROTOCOL_VIOLATIONinstead of being silently accepted.quicsamplegains-observed_address_send/-observed_address_recvon both client and server.Plumbed through the usual places:
msquic.h(IsSet + Flags bits),msquic.hppsetters,settings.h/settings.c(defaults, copy, apply, registry load, both dumps, to/from-internal),quicdef.hdefaults and registry names,docs/Settings.md,SettingsTestSET+GET entries. Clog headers andclog.sidecarwere regenerated with the clog tool fromsubmodules/clog.Verification
Loopback against
noq-mp-sample(unmodified; noq enables both directions), with the rebuiltquicsample:Frame codepoints — before commit 1:
quicsample -client-> noq serverShut down by transport, 0x5quicsample -client -multipath-> noq serverquicsample -serverAfter both commits:
-observed_address_recv-observed_address_send-multipath -observed_address_recvmsquic-to-msquic behaves the same: a server started with
-observed_address_sendreports 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.ObservedAddresswas extended to round-trip all three role values.Notes
SpinFramecan no longer generate them, so its OBSERVED_ADDRESS cases become unreachable — the same situation as the existingADD_ADDRESS/PUNCH_ME_NOWcases. 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.src/rs/settings.rsgains the two setters, andlinux_bindings.rswas regenerated withcargo build --features overwrite.win_bindings.rscannot 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 withcargo test --features preview-api(13 passed).MultipathEnabled.🤖 Generated with Claude Code