mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-23 17:31:43 +00:00
cmd/natc: attempt to match IP version between upstream and downstream
As IPv4 and IPv6 end up with different MSS and different congestion control strategies, proxying between them can really amplify TCP meltdown style conditions in many real world network conditions, such as with higher latency, some loss, etc. Attempt to match up the protocols, otherwise pick a destination address arbitrarily. Also shuffle the target address to spread load across upstream load balancers. Updates #15367 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
parent
7f5932e8f4
commit
8e1aa86bdb
@ -13,6 +13,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -438,7 +439,7 @@ func (c *connector) handleTCPFlow(src, dst netip.AddrPort) (handler func(net.Con
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return func(conn net.Conn) {
|
return func(conn net.Conn) {
|
||||||
proxyTCPConn(conn, domain)
|
proxyTCPConn(conn, domain, c)
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,16 +457,34 @@ func (c *connector) ignoreDestination(dstAddrs []netip.Addr) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxyTCPConn(c net.Conn, dest string) {
|
func proxyTCPConn(c net.Conn, dest string, ctor *connector) {
|
||||||
if c.RemoteAddr() == nil {
|
if c.RemoteAddr() == nil {
|
||||||
log.Printf("proxyTCPConn: nil RemoteAddr")
|
log.Printf("proxyTCPConn: nil RemoteAddr")
|
||||||
c.Close()
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addrPortStr := c.LocalAddr().String()
|
laddr, err := netip.ParseAddrPort(c.LocalAddr().String())
|
||||||
_, port, err := net.SplitHostPort(addrPortStr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("tcpRoundRobinHandler.Handle: bogus addrPort %q", addrPortStr)
|
log.Printf("proxyTCPConn: ParseAddrPort failed: %v", err)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
daddrs, err := ctor.resolver.LookupNetIP(context.TODO(), "ip", dest)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("proxyTCPConn: LookupNetIP failed: %v", err)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(daddrs) == 0 {
|
||||||
|
log.Printf("proxyTCPConn: no IP addresses found for %s", dest)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctor.ignoreDestination(daddrs) {
|
||||||
|
log.Printf("proxyTCPConn: closing connection to ignored destination %s (%v)", dest, daddrs)
|
||||||
c.Close()
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -475,10 +494,37 @@ func proxyTCPConn(c net.Conn, dest string) {
|
|||||||
return netutil.NewOneConnListener(c, nil), nil
|
return netutil.NewOneConnListener(c, nil), nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// XXX(raggi): if the connection here resolves to an ignored destination,
|
|
||||||
// the connection should be closed/failed.
|
// TODO(raggi): more code could avoid this shuffle, but avoiding allocations
|
||||||
p.AddRoute(addrPortStr, &tcpproxy.DialProxy{
|
// for now most of the time daddrs will be short.
|
||||||
Addr: fmt.Sprintf("%s:%s", dest, port),
|
rand.Shuffle(len(daddrs), func(i, j int) {
|
||||||
|
daddrs[i], daddrs[j] = daddrs[j], daddrs[i]
|
||||||
})
|
})
|
||||||
|
daddr := daddrs[0]
|
||||||
|
|
||||||
|
// Try to match the upstream and downstream protocols (v4/v6)
|
||||||
|
if laddr.Addr().Is6() {
|
||||||
|
for _, addr := range daddrs {
|
||||||
|
if addr.Is6() {
|
||||||
|
daddr = addr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, addr := range daddrs {
|
||||||
|
if addr.Is4() {
|
||||||
|
daddr = addr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(raggi): drop this library, it ends up being allocation and
|
||||||
|
// indirection heavy and really doesn't help us here.
|
||||||
|
dsockaddrs := netip.AddrPortFrom(daddr, laddr.Port()).String()
|
||||||
|
p.AddRoute(dsockaddrs, &tcpproxy.DialProxy{
|
||||||
|
Addr: dsockaddrs,
|
||||||
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user