Skip to content

Commit 92e0f5f

Browse files
committed
TUN-8688: Correct UDP bind for IPv6 edge connectivity on macOS
For macOS, we want to set the DF bit for the UDP packets used by the QUIC connection; to achieve this, you need to explicitly set the network to either "udp4" or "udp6". When determining which network type to pick we need to use the edge IP address chosen to align with what the local IP family interface we will use. This means we want cloudflared to bind to local interfaces for a random port, so we provide a zero IP and 0 port number (ex. 0.0.0.0:0). However, instead of providing the zero IP, we can leave the value as nil and let the kernel decide which interface and pick a random port as defined by the target edge IP family. This was previously broken for IPv6-only edge connectivity on macOS and all other operating systems should be unaffected because the network type was left as default "udp" which will rely on the provided local or remote IP for selection. Closes TUN-8688
1 parent d608a64 commit 92e0f5f

File tree

9 files changed

+904
-21
lines changed

9 files changed

+904
-21
lines changed

connection/quic.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ type QUICConnection struct {
9090
func NewQUICConnection(
9191
ctx context.Context,
9292
quicConfig *quic.Config,
93-
edgeAddr net.Addr,
93+
edgeAddr netip.AddrPort,
9494
localAddr net.IP,
9595
connIndex uint8,
9696
tlsConfig *tls.Config,
@@ -103,12 +103,12 @@ func NewQUICConnection(
103103
streamWriteTimeout time.Duration,
104104
gracePeriod time.Duration,
105105
) (*QUICConnection, error) {
106-
udpConn, err := createUDPConnForConnIndex(connIndex, localAddr, logger)
106+
udpConn, err := createUDPConnForConnIndex(connIndex, localAddr, edgeAddr, logger)
107107
if err != nil {
108108
return nil, err
109109
}
110110

111-
session, err := quic.Dial(ctx, udpConn, edgeAddr, tlsConfig, quicConfig)
111+
session, err := quic.Dial(ctx, udpConn, net.UDPAddrFromAddrPort(edgeAddr), tlsConfig, quicConfig)
112112
if err != nil {
113113
// close the udp server socket in case of error connecting to the edge
114114
udpConn.Close()
@@ -641,18 +641,15 @@ func (rp *muxerWrapper) Close() error {
641641
return nil
642642
}
643643

644-
func createUDPConnForConnIndex(connIndex uint8, localIP net.IP, logger *zerolog.Logger) (*net.UDPConn, error) {
644+
func createUDPConnForConnIndex(connIndex uint8, localIP net.IP, edgeIP netip.AddrPort, logger *zerolog.Logger) (*net.UDPConn, error) {
645645
portMapMutex.Lock()
646646
defer portMapMutex.Unlock()
647647

648-
if localIP == nil {
649-
localIP = net.IPv4zero
650-
}
651-
652648
listenNetwork := "udp"
653-
// https://github.com/quic-go/quic-go/issues/3793 DF bit cannot be set for dual stack listener on OSX
649+
// https://github.com/quic-go/quic-go/issues/3793 DF bit cannot be set for dual stack listener ("udp") on macOS,
650+
// to set the DF bit properly, the network string needs to be specific to the IP family.
654651
if runtime.GOOS == "darwin" {
655-
if localIP.To4() != nil {
652+
if edgeIP.Addr().Is4() {
656653
listenNetwork = "udp4"
657654
} else {
658655
listenNetwork = "udp6"

connection/quic_test.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"math/big"
1414
"net"
1515
"net/http"
16+
"net/netip"
1617
"net/url"
1718
"os"
1819
"strings"
@@ -26,6 +27,7 @@ import (
2627
"github.com/rs/zerolog"
2728
"github.com/stretchr/testify/assert"
2829
"github.com/stretchr/testify/require"
30+
"golang.org/x/net/nettest"
2931

3032
"github.com/cloudflare/cloudflared/datagramsession"
3133
cfdquic "github.com/cloudflare/cloudflared/quic"
@@ -162,7 +164,7 @@ func TestQUICServer(t *testing.T) {
162164
close(serverDone)
163165
}()
164166

165-
qc := testQUICConnection(udpListener.LocalAddr(), t, uint8(i))
167+
qc := testQUICConnection(netip.MustParseAddrPort(udpListener.LocalAddr().String()), t, uint8(i))
166168

167169
connDone := make(chan struct{})
168170
go func() {
@@ -632,7 +634,6 @@ func TestServeUDPSession(t *testing.T) {
632634
defer udpListener.Close()
633635

634636
ctx, cancel := context.WithCancel(context.Background())
635-
val := udpListener.LocalAddr()
636637

637638
// Establish QUIC connection with edge
638639
edgeQUICSessionChan := make(chan quic.Connection)
@@ -646,7 +647,7 @@ func TestServeUDPSession(t *testing.T) {
646647
}()
647648

648649
// Random index to avoid reusing port
649-
qc := testQUICConnection(val, t, 28)
650+
qc := testQUICConnection(netip.MustParseAddrPort(udpListener.LocalAddr().String()), t, 28)
650651
go qc.Serve(ctx)
651652

652653
edgeQUICSession := <-edgeQUICSessionChan
@@ -695,8 +696,20 @@ func TestNopCloserReadWriterCloseAfterEOF(t *testing.T) {
695696
}
696697

697698
func TestCreateUDPConnReuseSourcePort(t *testing.T) {
699+
edgeIPv4 := netip.MustParseAddrPort("0.0.0.0:0")
700+
edgeIPv6 := netip.MustParseAddrPort("[::]:0")
701+
702+
// We assume the test environment has access to an IPv4 interface
703+
testCreateUDPConnReuseSourcePortForEdgeIP(t, edgeIPv4)
704+
705+
if nettest.SupportsIPv6() {
706+
testCreateUDPConnReuseSourcePortForEdgeIP(t, edgeIPv6)
707+
}
708+
}
709+
710+
func testCreateUDPConnReuseSourcePortForEdgeIP(t *testing.T, edgeIP netip.AddrPort) {
698711
logger := zerolog.Nop()
699-
conn, err := createUDPConnForConnIndex(0, nil, &logger)
712+
conn, err := createUDPConnForConnIndex(0, nil, edgeIP, &logger)
700713
require.NoError(t, err)
701714

702715
getPortFunc := func(conn *net.UDPConn) int {
@@ -710,17 +723,17 @@ func TestCreateUDPConnReuseSourcePort(t *testing.T) {
710723
conn.Close()
711724

712725
// should get the same port as before.
713-
conn, err = createUDPConnForConnIndex(0, nil, &logger)
726+
conn, err = createUDPConnForConnIndex(0, nil, edgeIP, &logger)
714727
require.NoError(t, err)
715728
require.Equal(t, initialPort, getPortFunc(conn))
716729

717730
// new index, should get a different port
718-
conn1, err := createUDPConnForConnIndex(1, nil, &logger)
731+
conn1, err := createUDPConnForConnIndex(1, nil, edgeIP, &logger)
719732
require.NoError(t, err)
720733
require.NotEqual(t, initialPort, getPortFunc(conn1))
721734

722735
// not closing the conn and trying to obtain a new conn for same index should give a different random port
723-
conn, err = createUDPConnForConnIndex(0, nil, &logger)
736+
conn, err = createUDPConnForConnIndex(0, nil, edgeIP, &logger)
724737
require.NoError(t, err)
725738
require.NotEqual(t, initialPort, getPortFunc(conn))
726739
}
@@ -832,7 +845,7 @@ func (s mockSessionRPCServer) UnregisterUdpSession(ctx context.Context, sessionI
832845
return nil
833846
}
834847

835-
func testQUICConnection(udpListenerAddr net.Addr, t *testing.T, index uint8) *QUICConnection {
848+
func testQUICConnection(udpListenerAddr netip.AddrPort, t *testing.T, index uint8) *QUICConnection {
836849
tlsClientConfig := &tls.Config{
837850
InsecureSkipVerify: true,
838851
NextProtos: []string{"argotunnel"},

supervisor/tunnel.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/tls"
66
"fmt"
77
"net"
8+
"net/netip"
89
"runtime/debug"
910
"strings"
1011
"sync"
@@ -465,7 +466,7 @@ func (e *EdgeTunnelServer) serveConnection(
465466
case connection.QUIC:
466467
connOptions := e.config.connectionOptions(addr.UDP.String(), uint8(backoff.Retries()))
467468
return e.serveQUIC(ctx,
468-
addr.UDP,
469+
addr.UDP.AddrPort(),
469470
connLog,
470471
connOptions,
471472
controlStream,
@@ -548,7 +549,7 @@ func (e *EdgeTunnelServer) serveHTTP2(
548549

549550
func (e *EdgeTunnelServer) serveQUIC(
550551
ctx context.Context,
551-
edgeAddr *net.UDPAddr,
552+
edgeAddr netip.AddrPort,
552553
connLogger *ConnAwareLogger,
553554
connOptions *pogs.ConnectionOptions,
554555
controlStreamHandler connection.ControlStreamHandler,
@@ -571,7 +572,7 @@ func (e *EdgeTunnelServer) serveQUIC(
571572
// quic-go 0.44 increases the initial packet size to 1280 by default. That breaks anyone running tunnel through WARP
572573
// because WARP MTU is 1280.
573574
var initialPacketSize uint16 = 1252
574-
if edgeAddr.IP.To4() == nil {
575+
if edgeAddr.Addr().Is4() {
575576
initialPacketSize = 1232
576577
}
577578

0 commit comments

Comments
 (0)