mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-12 11:14:40 +00:00
net/portmapper: also send UPnP SSDP query to the SSDP multicast address.
Fixes #3197
Signed-off-by: David Anderson <danderson@tailscale.com>
(cherry picked from commit 4a65b07e34
)
This commit is contained in:
parent
647486dc46
commit
d117f77094
@ -677,6 +677,7 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
|||||||
|
|
||||||
pxpAddr := netaddr.IPPortFrom(gw, c.pxpPort()).UDPAddr()
|
pxpAddr := netaddr.IPPortFrom(gw, c.pxpPort()).UDPAddr()
|
||||||
upnpAddr := netaddr.IPPortFrom(gw, c.upnpPort()).UDPAddr()
|
upnpAddr := netaddr.IPPortFrom(gw, c.upnpPort()).UDPAddr()
|
||||||
|
upnpMulticastAddr := netaddr.IPPortFrom(netaddr.IPv4(239, 255, 255, 250), c.upnpPort()).UDPAddr()
|
||||||
|
|
||||||
// Don't send probes to services that we recently learned (for
|
// Don't send probes to services that we recently learned (for
|
||||||
// the same gw/myIP) are available. See
|
// the same gw/myIP) are available. See
|
||||||
@ -694,7 +695,47 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
|||||||
if c.sawUPnPRecently() {
|
if c.sawUPnPRecently() {
|
||||||
res.UPnP = true
|
res.UPnP = true
|
||||||
} else if !DisableUPnP {
|
} else if !DisableUPnP {
|
||||||
|
// Strictly speaking, you discover UPnP services by sending an
|
||||||
|
// SSDP query (which uPnPPacket is) to udp/1900 on the SSDP
|
||||||
|
// multicast address, and then get a flood of responses back
|
||||||
|
// from everything on your network.
|
||||||
|
//
|
||||||
|
// Empirically, many home routers also respond to SSDP queries
|
||||||
|
// directed at udp/1900 on their LAN unicast IP
|
||||||
|
// (e.g. 192.168.1.1). This is handy because it means we can
|
||||||
|
// probe the router directly and likely get a reply. However,
|
||||||
|
// the specs do not _require_ UPnP devices to respond to
|
||||||
|
// unicast SSDP queries, so some conformant UPnP
|
||||||
|
// implementations only respond to multicast queries.
|
||||||
|
//
|
||||||
|
// In theory, we could send just the multicast query and get
|
||||||
|
// all compliant devices to respond. However, we instead send
|
||||||
|
// to both a unicast and a multicast addresses, for a couple
|
||||||
|
// of reasons:
|
||||||
|
//
|
||||||
|
// First, some LANs and OSes have broken multicast in one way
|
||||||
|
// or another, so it's possible for the multicast query to be
|
||||||
|
// lost while the unicast query gets through. But we still
|
||||||
|
// have to send the multicast query to also get a response
|
||||||
|
// from strict-UPnP devices on multicast-working networks.
|
||||||
|
//
|
||||||
|
// Second, SSDP's packet dynamics are a bit weird: you send
|
||||||
|
// the SSDP query from your unicast IP to the SSDP multicast
|
||||||
|
// IP, but responses are from the UPnP devices's _unicast_ IP
|
||||||
|
// to your unicast IP. This can confuse some less-intelligent
|
||||||
|
// stateful host firewalls, who might block the responses. To
|
||||||
|
// work around this, we send the unicast query first, to teach
|
||||||
|
// the firewall to expect a unicast response from the router,
|
||||||
|
// and then send our multicast query. That way, even if the
|
||||||
|
// device doesn't respond to the unicast query, we've set the
|
||||||
|
// stage for the host firewall to accept the response to the
|
||||||
|
// multicast query.
|
||||||
|
//
|
||||||
|
// See https://github.com/tailscale/tailscale/issues/3197 for
|
||||||
|
// an example of a device that strictly implements UPnP, and
|
||||||
|
// only responds to multicast queries.
|
||||||
uc.WriteTo(uPnPPacket, upnpAddr)
|
uc.WriteTo(uPnPPacket, upnpAddr)
|
||||||
|
uc.WriteTo(uPnPPacket, upnpMulticastAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 1500)
|
buf := make([]byte, 1500)
|
||||||
|
Loading…
Reference in New Issue
Block a user