Skip to content

Commit 305b425

Browse files
Antonio Ojeaaojea
authored andcommitted
use happy-eyeballs for port-forwarding
golang has enabled RFC 6555 Fast Fallback (aka HappyEyeballs) by default in 1.12. It means that if a host resolves to both IPv6 and IPv4, it will try to connect to any of those addresses and use the working connection. However, the implementation uses go routines to start both connections in parallel, and this has limitations when running inside a namespace, so we try to the connections serially, trying IPv4 first for keeping the same behaviour. xref golang/go#44922 Signed-off-by: Antonio Ojea <[email protected]>
1 parent a5d17eb commit 305b425

1 file changed

Lines changed: 16 additions & 4 deletions

File tree

pkg/cri/server/sandbox_portforward_linux.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,24 @@ func (c *criService) portForward(ctx context.Context, id string, port int32, str
6262
log.G(ctx).Infof("Executing port forwarding in network namespace %q", netNSPath)
6363
err = netNSDo(func(_ ns.NetNS) error {
6464
defer stream.Close()
65-
// TODO: hardcoded to tcp4 because localhost resolves to ::1 by default if the system has IPv6 enabled.
66-
// Theoretically happy eyeballs will try IPv6 first and fallback to IPv4
67-
// but resolving localhost doesn't seem to return and IPv4 address, thus failing the connection.
65+
// localhost can resolve to both IPv4 and IPv6 addresses in dual-stack systems
66+
// but the application can be listening in one of the IP families only.
67+
// golang has enabled RFC 6555 Fast Fallback (aka HappyEyeballs) by default in 1.12
68+
// It means that if a host resolves to both IPv6 and IPv4, it will try to connect to any
69+
// of those addresses and use the working connection.
70+
// However, the implementation uses go routines to start both connections in parallel,
71+
// and this cases that the connection is done outside the namespace, so we try to connect
72+
// serially.
73+
// We try IPv4 first to keep current behavior and we fallback to IPv6 if the connection fails.
74+
// xref https://github.com/golang/go/issues/44922
75+
var conn net.Conn
6876
conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
6977
if err != nil {
70-
return errors.Wrapf(err, "failed to dial %d", port)
78+
var errV6 error
79+
conn, errV6 = net.Dial("tcp6", fmt.Sprintf("localhost:%d", port))
80+
if errV6 != nil {
81+
return fmt.Errorf("failed to connect to localhost:%d inside namespace %q, IPv4: %v IPv6 %v ", port, id, err, errV6)
82+
}
7183
}
7284
defer conn.Close()
7385

0 commit comments

Comments
 (0)