reflector: bridge mDNS/SSDP/Wake-on-LAN across VLANs, including the DIAL proxy that makes casting work
▲ 38 r/mikrotik+3 crossposts

reflector: bridge mDNS/SSDP/Wake-on-LAN across VLANs, including the DIAL proxy that makes casting work

Disclosure: I wrote this — open source, Apache-2.0.

TL;DR: mDNS reflectors fix discovery across VLANs but not casting. reflector adds the SSDP + DIAL bits so Chromecast/AirPlay/YouTube/Netflix casting actually works between segments.

Segmented networks (separate VLANs for IoT/guest/trusted) break service discovery: mDNS and SSDP ride multicast that routers don't forward, so Chromecast, AirPlay, DLNA, printers, and Sonos stop appearing across segments.

The usual fix — an mDNS reflector (Avahi, the UniFi/pfSense/OPNsense toggle, RouterOS's built-in repeater) — restores discovery but not casting. A DIAL device (the YouTube/Netflix "cast" target) serves its device description and REST control endpoints only to its own subnet, so a client on another VLAN sees the TV but can't drive it.

reflector adds the parts the mDNS-only options miss: SSDP M-SEARCH relay and a DIAL proxy, so the cast actually launches and stops across VLANs. It also does mDNS and WoL, so it can replace your reflector rather than sit next to it.

What it does:

  • mDNS reflection — Bonjour/Avahi discovery (AirPlay, printers, Chromecast) across the two segments.
  • SSDP reflection with active discovery, not just passive NOTIFY: relays the client's M-SEARCH and proxies the device's unicast 200 OK back.
  • DIAL proxy (opt-in) — a terminating HTTP reverse proxy that rewrites the device's LOCATION/description URLs and connects upstream bound to the device's subnet, so it sees an on-subnet client. This is what makes cast launch/stop work.
  • Wake-on-LAN across segments.
  • Per-device MAC filter — expose one device, or omit it to mirror the whole segment.

Config is interface-based, no IPs:

[tv]
source_if = "vlan-trusted"
target_if = "vlan-iot"
mac  = "B0:37:95:C5:60:BE"   # optional; omit to mirror the whole segment
wol  = true
mdns = true
ssdp = true
dial = true                  # requires ssdp; IPv4 only

Every option also has an env-var form, which is easier with containers:

REFLECTOR_TV_SOURCE_IF=vlan-trusted
REFLECTOR_TV_TARGET_IF=vlan-iot
REFLECTOR_TV_MAC=B0:37:95:C5:60:BE
REFLECTOR_TV_WOL=true
REFLECTOR_TV_MDNS=true
REFLECTOR_TV_SSDP=true
REFLECTOR_TV_DIAL=true

Deploys as a single static binary or a tiny container — prebuilt multi-arch images (amd64/arm64/armv7/armv5), uses under 10 MB RAM.

Security: it doesn't merge VLANs. It relays only mDNS/SSDP/WoL on the interface pair you name, and the MAC filter scopes the exposed direction to one device. The DIAL proxy does expose that device's HTTP control endpoint to the source side, and the daemon parses untrusted mDNS/SSDP/HTTP. Tested with ASan+UBSan, Valgrind memcheck, and Docker e2e over real veth/feth pairs.

Single C++23 binary, Linux + macOS. Linux runs unprivileged with CAP_NET_RAW; macOS needs root or the ChmodBPF helper. mDNS, SSDP, and WoL run on both IPv4 and IPv6 (IPv6 best-effort by default, or required per entry). DIAL is IPv4-only because the DIAL spec ties the device's authority to an IPv4 address.

https://github.com/sbogomolov/reflector — Apache-2.0

Feedback welcome, especially on the SSDP/DIAL handling.

u/s3rgb — 6 days ago