From 87701339fee7324c90c975b947fef2a5dafa0b0c Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Fri, 27 May 2016 06:28:46 +0000 Subject: [PATCH] lib/nat, lib/connections: Fix a few issues with NAT traversal 1. For the same internal port we ask for the same external port on all devices. This can be a problem if one device speaks over two protocols. 2. Always add a nil address even if we managed to get external address of the gateway, just because the gateway might be in DMZ behind another gateway. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3196 --- lib/connections/tcp_listen.go | 9 +++++++++ lib/nat/service.go | 15 +++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/connections/tcp_listen.go b/lib/connections/tcp_listen.go index ba78a76a..58febccc 100644 --- a/lib/connections/tcp_listen.go +++ b/lib/connections/tcp_listen.go @@ -137,6 +137,15 @@ func (t *tcpListener) WANAddresses() []*url.URL { // Does net.JoinHostPort internally uri.Host = addr.String() uris = append(uris, &uri) + + // For every address with a specified IP, add one without an IP, + // just in case the specified IP is still internal (router behind DMZ). + if len(addr.IP) != 0 && !addr.IP.IsUnspecified() { + uri = *t.uri + addr.IP = nil + uri.Host = addr.String() + uris = append(uris, &uri) + } } } t.mut.RUnlock() diff --git a/lib/nat/service.go b/lib/nat/service.go index d26a61a3..ad14e294 100644 --- a/lib/nat/service.go +++ b/lib/nat/service.go @@ -8,6 +8,7 @@ package nat import ( "fmt" + "hash/fnv" "math/rand" "net" stdsync "sync" @@ -284,10 +285,10 @@ func (s *Service) tryNATDevice(natd Device, intPort, extPort int, leaseTime time var err error var port int - // Generate a predictable random which is based on device ID + local port - // number so that the ports we'd try to acquire for the mapping would always - // be the same. - predictableRand := rand.New(rand.NewSource(int64(s.id.Short()) + int64(intPort))) + // Generate a predictable random which is based on device ID + local port + hash of the device ID + // number so that the ports we'd try to acquire for the mapping would always be the same for the + // same device trying to get the same internal port. + predictableRand := rand.New(rand.NewSource(int64(s.id.Short()) + int64(intPort) + hash(natd.ID()))) if extPort != 0 { // First try renewing our existing mapping, if we have one. @@ -325,3 +326,9 @@ findIP: Port: extPort, }, nil } + +func hash(input string) int64 { + h := fnv.New64a() + h.Write([]byte(input)) + return int64(h.Sum64()) +}