Skip to content

Carry mDNS interface scope_id for link-local IPv6 sends#13

Open
vbondarevsky wants to merge 1 commit into
tom-code:mainfrom
vbondarevsky:fix/link-local-ipv6-scope
Open

Carry mDNS interface scope_id for link-local IPv6 sends#13
vbondarevsky wants to merge 1 commit into
tom-code:mainfrom
vbondarevsky:fix/link-local-ipv6-scope

Conversation

@vbondarevsky

@vbondarevsky vbondarevsky commented Jun 24, 2026

Copy link
Copy Markdown

Problem
Devices that advertise only a link-local IPv6 operational address (fe80::…) can't be reached: every UDP send fails with EINVAL (IPv4 socket) or No route to host (IPv6 socket), so CASE/commissioning never completes.

Cause
A link-local address needs the interface zone (scope_id) to be sendable — fe80::/64 exists on every interface. The address was taken from mDNS without a zone, so scope_id was 0.

Fix
Carry the scope from where it's known to where it's needed:

mdns2 records the interface each link-local AAAA reply arrived on (MdnsService::scope_for).
MatterDeviceInfo::scope_id is set from it; discover::addr_string encodes [fe80::…%idx]:port.
transport parses the zone (split_scope), stores it on Connection, and applies it as scope_id on send; inbound datagrams are matched zone-insensitively.
The scope comes from the actual receive interface, so it's correct on multi-interface hosts (no guessing). Non-link-local and IPv4 paths are unchanged.

Test
Verified end-to-end against a real accessory (Shelly Presence, Wi-Fi) on macOS: BLE commissioning → CASE → CommissioningComplete, and operational attribute reads

Matter devices commonly advertise only a link-local IPv6 operational address
(fe80::/10). Sending UDP to such an address requires the interface zone
(scope_id); without it sendto fails with EINVAL (IPv4 socket) or No route to
host (IPv6 socket), so commissioning/operation never completes.

Capture the interface index on which each device's mDNS reply was received
(ll_scope map in MdnsService), expose it as MatterDeviceInfo::scope_id, encode
it into the address as [fe80::..%idx]:port (discover::addr_string), and apply it
as the scope_id when sending (transport split_scope + Connection::scope_id).
Incoming datagrams are matched zone-insensitively (scopeless_key).
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.

1 participant