Multi-NIC discovery
How --interface and --all-interfaces work under the hood
Multi-NIC discovery
A laptop with Wi-Fi + Ethernet both up has more than one path to broadcast on. The kernel's default-route NIC wins by default — every other interface stays silent unless you explicitly tell go-udap otherwise. This page explains the wire-level details.
The problem
Default go-udap discover binds 0.0.0.0:17784 and writes to
255.255.255.255:17784. The kernel chooses which interface to emit
the packet on based on its routing table — typically the
default-route NIC.
If the device is on the other NIC's subnet (say, a Squeezebox on your wired LAN while your Wi-Fi default route points at a tethered phone), the broadcast never reaches it.
--interface NAME
Forces a specific egress interface. The mechanism is platform-specific:
- macOS:
IP_BOUND_IFsocket option (IPPROTO_IP option 25). Takes the interface index fromEnumerateInterfaces(). The kernel routes all output through that NIC regardless of route-table preference. - Linux:
SO_BINDTODEVICEsocket option (SOL_SOCKET). Takes the interface name string. Same effect asIP_BOUND_IF. RequiresCAP_NET_RAWon most distros — usually granted to the binary or the user. - Windows: Not yet supported.
IP_UNICAST_IFexists on Windows but its broadcast semantics are undocumented; investigation is tracked as Task #29. Until then, the flag is hidden from--helpon Windows.
The local socket is bound to 0.0.0.0:17784 regardless of the egress
interface — not to the interface's specific IP. Binding to the
specific IP would prevent the socket from receiving limited-broadcast
replies (those only reach 0.0.0.0-bound sockets). This was a real
bug caught by Phase 6 hardware testing.
--all-interfaces
Fans out across every usable interface. Implementation: one
UDPTransport per interface, composed into a MultiTransport.
The catch: multiple sockets all binding to 0.0.0.0:17784
simultaneously requires SO_REUSEPORT (in addition to
SO_REUSEADDR), set before bind(). go-udap uses
net.ListenConfig.Control to set both options pre-bind via a
platform-specific helper (setReusePortPreBind).
On Windows, SO_REUSEPORT doesn't exist; --all-interfaces is
therefore not supported there.
What you'd see in tcpdump
./go-udap --all-interfaces discover on a Mac with en0 (Wi-Fi,
192.168.1.118) and en7 (Ethernet, 192.168.1.226) both on the
same subnet:
16:58:38.746037 IP 192.168.1.118.17784 > 255.255.255.255.17784: UDP, length 27
16:58:38.746183 IP 192.168.1.226.17784 > 255.255.255.255.17784: UDP, length 27
16:58:38.746539 IP 0.0.0.0.17784 > 255.255.255.255.17784: UDP, length 61Two source IPs on the outbound — confirming fan-out really did go out both NICs. The third line is the device reply.
Why not just set IP_MULTICAST_IF?
UDAP discovery uses broadcast, not multicast. The
IP_MULTICAST_IF socket option doesn't affect broadcast routing on
any of the three target platforms.
See also
- How UDAP discovery works — the broader discovery story
- How to discover on a multi-NIC laptop — the practical recipe
- Global flags reference — flag documentation