Skip to content

feat(general): drop irrelevant mDNS packets before decode#3995

Merged
mergify[bot] merged 2 commits into
mainfrom
feat/mdns-relevance-prefilter
Jun 27, 2026
Merged

feat(general): drop irrelevant mDNS packets before decode#3995
mergify[bot] merged 2 commits into
mainfrom
feat/mdns-relevance-prefilter

Conversation

@Apollon77

Copy link
Copy Markdown
Collaborator

Problem

On a busy LAN, mDNS is a firehose — printers, AirPlay, Chromecast, Spotify, etc. A real high-CPU capture from a Home Assistant host showed ~97% of mDNS frames are irrelevant to Matter/Thread, yet every packet was fully decoded (DnsCodec.decode) before any relevance gate ran. That decode + GC churn per packet is what hurts under bursts, especially on constrained devices.

Change

A pre-decode relevance filter at the single decode chokepoint (MdnsSocket.#handleMessage). Consumers register the DNS names they care about; each inbound packet is scanned for a footprint before a full decode.

  • MdnsRelevanceFilter (new, self-contained class): a refcounted incremental registry (O(1) per discovered/expired name) plus a names-only partial-parse matcher — walks the DNS name structure only, skips RDATA via rdlength, follows compression pointers (backward-only, hop-bounded), folds ASCII case (RFC 4343), FNV-1a hashes each label, tests Map membership. A footprint is a name's rightmost non-stoplist label (_tcp/_udp/local/_sub skipped), so service families collapse to one label while hostnames stay distinct.
  • Conservative superset: any structural uncertainty or hash collision keeps the packet (it falls through to the real gates) — a false drop never occurs.
  • Wiring: DnssdNames.filters.add(filter, names) now requires the names a filter accepts (or "all", which disables the filter socket-wide — documented). Tracked SRV-target hostnames register incrementally, so operational A/AAAA answers that carry no service text still pass. MdnsServer, MdnsService and matterjs-server's BorderRouterDiscovery (separate change) declare their service types.

Why names-only partial parse (measured)

Benchmarked the real DnsCodec.decode vs candidate matchers (Node 24):

matcher ~400 B packet scales with footprint count N?
DnsCodec.decode (full) ~3.9 µs
per-footprint substring scan 1.2 µs (N=5) → 47 µs (N=200) yes — loses by N≈50
names-only partial parse ~1.2 µs, flat in N no

Operational hostnames don't fold (one footprint per tracked peer, 50–200 on a controller), so any per-footprint scan loses at scale. The partial parse is ~3× faster than decode and flat in N. The registry is incremental specifically so a discovery burst doesn't reintroduce an O(N)-per-packet cost (an event-loop concern).

Tests

  • MdnsRelevanceFilterTest — 32 unit tests: activation, derivation, case-insensitivity, incremental add/remove + reference counting, multi-owner, replace/"all" transitions, packet-structure walking, compression-pointer following, malformed→keep.
  • MdnsSocketTest — end-to-end gate behavior through the socket.
  • Existing MdnsServer / DnssdNames / scanner suites green: general 1202, protocol 1242. Build, format, lint clean.

Follow-ups (not in this PR)

  • matterjs-server BorderRouterDiscovery must pass [MESHCOP_TYPE_QNAME, TREL_TYPE_QNAME] to filters.add (now a required arg).
  • A separate, benchmarked pass for post-gate allocation micro-opts (responder key building, RFC 6762 known-answer suppression) and a possible small-N substring hybrid.

🤖 Generated with Claude Code

Busy LANs flood mDNS, almost all of it irrelevant to us, yet every packet was
fully decoded before any relevance gate. Add a pre-decode relevance filter on
the mDNS socket: consumers register the DNS names they care about, and each
inbound packet is scanned (names only, RDATA skipped, compression pointers
followed) for a distinctive-label footprint before paying for a full decode.

- MdnsRelevanceFilter: self-contained class holding a refcounted incremental
  registry (O(1) per tracked name) and a names-only partial-parse matcher.
  Conservative superset - any uncertainty keeps the packet, so a false drop
  never occurs. ~3x cheaper than decode and flat in the number of footprints.
- DnssdNames.filters.add now requires the names a filter accepts (or "all",
  which disables the filter socket-wide); SRV-target hostnames are registered
  incrementally as they are discovered/expired.
- MdnsServer, MdnsService and CommissionableMdnsScanner declare their names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 27, 2026 10:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a pre-decode mDNS relevance gate in @matter/general so busy LAN traffic can be dropped before paying the cost of full DnsCodec.decode, and wires DNS-SD consumers to declare (and incrementally track) the names they care about to drive that gate.

Changes:

  • Added MdnsRelevanceFilter and integrated it into MdnsSocket to drop irrelevant packets before decoding.
  • Updated DNS-SD name filtering APIs to require/propagate accepted names (service types / tracked hostnames) into the socket-level relevance registry.
  • Added/expanded tests for the relevance pre-filter and its edge cases (compression pointers, malformed packets, incremental refcount behavior).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/protocol/src/mdns/MdnsService.ts Declares Matter service suffixes once and provides filterNames to drive the socket pre-filter.
packages/protocol/src/mdns/MdnsServer.ts Registers responder-owned names with the socket so responder-relevant queries/answers survive pre-filtering.
packages/protocol/src/mdns/CommissionableMdnsScanner.ts Updates filters.add usage to provide accepted names for pre-filtering.
packages/general/test/net/dns-sd/MdnsSocketTest.ts Adds end-to-end coverage proving irrelevant packets are filtered before decode and relevant ones pass.
packages/general/test/net/dns-sd/MdnsRelevanceFilterTest.ts Adds unit tests for relevance derivation, refcounting, pointer-following, and malformed-packet keep behavior.
packages/general/test/net/dns-sd/DnssdNamesTest.ts Updates tests for the new filters.add(filter, names) API shape.
packages/general/src/net/dns-sd/ServiceDiscovery.ts Provides accepted type name(s) when registering a discovery filter.
packages/general/src/net/dns-sd/MdnsSocket.ts Wires the pre-decode relevance check into the receive path and exposes registration APIs.
packages/general/src/net/dns-sd/MdnsRelevanceFilter.ts Implements names-only packet scanning and owner/refcount tracking to decide pre-decode relevance.
packages/general/src/net/dns-sd/index.ts Exports MdnsRelevanceFilter.
packages/general/src/net/dns-sd/DnssdNames.ts Tracks filter-declared names and discovered hostnames, publishing both to the socket relevance registry.
CHANGELOG.md Documents the new optimization and related protocol-side wiring.

Comment thread packages/general/src/net/dns-sd/MdnsRelevanceFilter.ts
Comment thread packages/general/src/net/dns-sd/MdnsRelevanceFilter.ts
Comment thread packages/general/src/net/dns-sd/MdnsRelevanceFilter.ts
Comment thread CHANGELOG.md Outdated
…vance scan

Address PR review: the names-only scan must defer to the decoder (keep) on any
malformed structure, never drop. Add the missing guards: reserved label types
(top bits 0b01/0b10), compression pointers into the header, a question name
truncated before QTYPE/QCLASS, and an RDLENGTH running past the packet end.
All are strictly more conservative (more keeps, never fewer). Fix CHANGELOG grammar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@Apollon77 Apollon77 added the automerge Set this label if the PR is ready to automatically merged after approval label Jun 27, 2026
@mergify

mergify Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@mergify mergify Bot merged commit e936355 into main Jun 27, 2026
44 checks passed
@mergify mergify Bot deleted the feat/mdns-relevance-prefilter branch June 27, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge Set this label if the PR is ready to automatically merged after approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants