TUN-6856: Refactor to lay foundation for tracing ICMP

Remove send and return methods from Funnel interface. Users of Funnel can provide their own send and return methods without wrapper to comply with the interface.
Move packet router to ingress package to avoid circular dependency
This commit is contained in:
cthuang 2022-10-13 11:01:25 +01:00
parent 225c344ceb
commit 495f9fb8bd
15 changed files with 323 additions and 337 deletions

View File

@ -22,7 +22,6 @@ import (
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/edgediscovery/allregions"
"github.com/cloudflare/cloudflared/packet"
"github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/config"
"github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/connection"
@ -463,7 +462,7 @@ func parseConfigIPVersion(version string) (v allregions.ConfigIPVersion, err err
return return
} }
func newPacketConfig(c *cli.Context, logger *zerolog.Logger) (*packet.GlobalRouterConfig, error) { func newPacketConfig(c *cli.Context, logger *zerolog.Logger) (*ingress.GlobalRouterConfig, error) {
ipv4Src, err := determineICMPv4Src(c.String("icmpv4-src"), logger) ipv4Src, err := determineICMPv4Src(c.String("icmpv4-src"), logger)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to determine IPv4 source address for ICMP proxy") return nil, errors.Wrap(err, "failed to determine IPv4 source address for ICMP proxy")
@ -484,7 +483,7 @@ func newPacketConfig(c *cli.Context, logger *zerolog.Logger) (*packet.GlobalRout
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &packet.GlobalRouterConfig{ return &ingress.GlobalRouterConfig{
ICMPRouter: icmpRouter, ICMPRouter: icmpRouter,
IPv4Src: ipv4Src, IPv4Src: ipv4Src,
IPv6Src: ipv6Src, IPv6Src: ipv6Src,

View File

@ -57,7 +57,7 @@ type QUICConnection struct {
sessionManager datagramsession.Manager sessionManager datagramsession.Manager
// datagramMuxer mux/demux datagrams from quic connection // datagramMuxer mux/demux datagrams from quic connection
datagramMuxer *quicpogs.DatagramMuxerV2 datagramMuxer *quicpogs.DatagramMuxerV2
packetRouter *packet.Router packetRouter *ingress.PacketRouter
controlStreamHandler ControlStreamHandler controlStreamHandler ControlStreamHandler
connOptions *tunnelpogs.ConnectionOptions connOptions *tunnelpogs.ConnectionOptions
} }
@ -72,7 +72,7 @@ func NewQUICConnection(
connOptions *tunnelpogs.ConnectionOptions, connOptions *tunnelpogs.ConnectionOptions,
controlStreamHandler ControlStreamHandler, controlStreamHandler ControlStreamHandler,
logger *zerolog.Logger, logger *zerolog.Logger,
packetRouterConfig *packet.GlobalRouterConfig, packetRouterConfig *ingress.GlobalRouterConfig,
) (*QUICConnection, error) { ) (*QUICConnection, error) {
udpConn, err := createUDPConnForConnIndex(connIndex, logger) udpConn, err := createUDPConnForConnIndex(connIndex, logger)
if err != nil { if err != nil {
@ -93,8 +93,7 @@ func NewQUICConnection(
sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity)
datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan) datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan)
sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan) sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan)
muxer := muxerWrapper{muxer: datagramMuxer} packetRouter := ingress.NewPacketRouter(packetRouterConfig, datagramMuxer, logger, orchestrator.WarpRoutingEnabled)
packetRouter := packet.NewRouter(packetRouterConfig, &muxer, &muxer, logger, orchestrator.WarpRoutingEnabled)
return &QUICConnection{ return &QUICConnection{
session: session, session: session,

View File

@ -11,7 +11,6 @@ import (
"context" "context"
"fmt" "fmt"
"math" "math"
"net"
"net/netip" "net/netip"
"strconv" "strconv"
"sync" "sync"
@ -129,10 +128,7 @@ func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idle
}, nil }, nil
} }
func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { func (ip *icmpProxy) Request(ctx context.Context, pk *packet.ICMP, responder *packetResponder) error {
if pk == nil {
return errPacketNil
}
originalEcho, err := getICMPEcho(pk.Message) originalEcho, err := getICMPEcho(pk.Message)
if err != nil { if err != nil {
return err return err
@ -152,13 +148,11 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er
if err != nil { if err != nil {
return nil, err return nil, err
} }
originSender := originSender{ closeCallback := func() error {
conn: ip.conn, ip.echoIDTracker.release(echoIDTrackerKey, assignedEchoID)
echoIDTracker: ip.echoIDTracker, return nil
echoIDTrackerKey: echoIDTrackerKey,
assignedEchoID: assignedEchoID,
} }
icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, int(assignedEchoID), originalEcho.ID, ip.encoder) icmpFlow := newICMPEchoFlow(pk.Src, closeCallback, ip.conn, responder, int(assignedEchoID), originalEcho.ID, ip.encoder)
return icmpFlow, nil return icmpFlow, nil
} }
funnelID := echoFunnelID(assignedEchoID) funnelID := echoFunnelID(assignedEchoID)
@ -250,23 +244,3 @@ func (ip *icmpProxy) sendReply(reply *echoReply) error {
} }
return icmpFlow.returnToSrc(reply) return icmpFlow.returnToSrc(reply)
} }
// originSender wraps icmp.PacketConn to implement packet.FunnelUniPipe interface
type originSender struct {
conn *icmp.PacketConn
echoIDTracker *echoIDTracker
echoIDTrackerKey flow3Tuple
assignedEchoID uint16
}
func (os *originSender) SendPacket(dst netip.Addr, pk packet.RawPacket) error {
_, err := os.conn.WriteTo(pk.Data, &net.UDPAddr{
IP: dst.AsSlice(),
})
return err
}
func (os *originSender) Close() error {
os.echoIDTracker.release(os.echoIDTrackerKey, os.assignedEchoID)
return nil
}

View File

@ -18,7 +18,7 @@ var errICMPProxyNotImplemented = fmt.Errorf("ICMP proxy is not implemented on %s
type icmpProxy struct{} type icmpProxy struct{}
func (ip icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { func (ip icmpProxy) Request(ctx context.Context, pk *packet.ICMP, responder *packetResponder) error {
return errICMPProxyNotImplemented return errICMPProxyNotImplemented
} }

View File

@ -97,10 +97,7 @@ func checkInPingGroup() error {
return fmt.Errorf("did not find group range in %s", pingGroupPath) return fmt.Errorf("did not find group range in %s", pingGroupPath)
} }
func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { func (ip *icmpProxy) Request(ctx context.Context, pk *packet.ICMP, responder *packetResponder) error {
if pk == nil {
return errPacketNil
}
originalEcho, err := getICMPEcho(pk.Message) originalEcho, err := getICMPEcho(pk.Message)
if err != nil { if err != nil {
return err return err
@ -113,13 +110,15 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er
} }
ip.logger.Debug().Msgf("Opened ICMP socket listen on %s", conn.LocalAddr()) ip.logger.Debug().Msgf("Opened ICMP socket listen on %s", conn.LocalAddr())
newConnChan <- conn newConnChan <- conn
closeCallback := func() error {
return conn.Close()
}
localUDPAddr, ok := conn.LocalAddr().(*net.UDPAddr) localUDPAddr, ok := conn.LocalAddr().(*net.UDPAddr)
if !ok { if !ok {
return nil, fmt.Errorf("ICMP listener address %s is not net.UDPAddr", conn.LocalAddr()) return nil, fmt.Errorf("ICMP listener address %s is not net.UDPAddr", conn.LocalAddr())
} }
originSender := originSender{conn: conn}
echoID := localUDPAddr.Port echoID := localUDPAddr.Port
icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, echoID, originalEcho.ID, packet.NewEncoder()) icmpFlow := newICMPEchoFlow(pk.Src, closeCallback, conn, responder, echoID, originalEcho.ID, packet.NewEncoder())
return icmpFlow, nil return icmpFlow, nil
} }
funnelID := flow3Tuple{ funnelID := flow3Tuple{
@ -187,22 +186,6 @@ func (ip *icmpProxy) listenResponse(flow *icmpEchoFlow, conn *icmp.PacketConn) e
} }
} }
// originSender wraps icmp.PacketConn to implement packet.FunnelUniPipe interface
type originSender struct {
conn *icmp.PacketConn
}
func (os *originSender) SendPacket(dst netip.Addr, pk packet.RawPacket) error {
_, err := os.conn.WriteTo(pk.Data, &net.UDPAddr{
IP: dst.AsSlice(),
})
return err
}
func (os *originSender) Close() error {
return os.conn.Close()
}
// Only linux uses flow3Tuple as FunnelID // Only linux uses flow3Tuple as FunnelID
func (ft flow3Tuple) Type() string { func (ft flow3Tuple) Type() string {
return "srcIP_dstIP_echoID" return "srcIP_dstIP_echoID"

View File

@ -43,24 +43,54 @@ type flow3Tuple struct {
// icmpEchoFlow implements the packet.Funnel interface. // icmpEchoFlow implements the packet.Funnel interface.
type icmpEchoFlow struct { type icmpEchoFlow struct {
*packet.RawPacketFunnel *packet.ActivityTracker
closeCallback func() error
src netip.Addr
originConn *icmp.PacketConn
responder *packetResponder
assignedEchoID int assignedEchoID int
originalEchoID int originalEchoID int
// it's up to the user to ensure respEncoder is not used concurrently // it's up to the user to ensure respEncoder is not used concurrently
respEncoder *packet.Encoder respEncoder *packet.Encoder
} }
func newICMPEchoFlow(src netip.Addr, sendPipe, returnPipe packet.FunnelUniPipe, assignedEchoID, originalEchoID int, respEncoder *packet.Encoder) *icmpEchoFlow { func newICMPEchoFlow(src netip.Addr, closeCallback func() error, originConn *icmp.PacketConn, responder *packetResponder, assignedEchoID, originalEchoID int, respEncoder *packet.Encoder) *icmpEchoFlow {
return &icmpEchoFlow{ return &icmpEchoFlow{
RawPacketFunnel: packet.NewRawPacketFunnel(src, sendPipe, returnPipe), ActivityTracker: packet.NewActivityTracker(),
closeCallback: closeCallback,
src: src,
originConn: originConn,
responder: responder,
assignedEchoID: assignedEchoID, assignedEchoID: assignedEchoID,
originalEchoID: originalEchoID, originalEchoID: originalEchoID,
respEncoder: respEncoder, respEncoder: respEncoder,
} }
} }
func (ief *icmpEchoFlow) Equal(other packet.Funnel) bool {
otherICMPFlow, ok := other.(*icmpEchoFlow)
if !ok {
return false
}
if otherICMPFlow.src != ief.src {
return false
}
if otherICMPFlow.originalEchoID != ief.originalEchoID {
return false
}
if otherICMPFlow.assignedEchoID != ief.assignedEchoID {
return false
}
return true
}
func (ief *icmpEchoFlow) Close() error {
return ief.closeCallback()
}
// sendToDst rewrites the echo ID to the one assigned to this flow // sendToDst rewrites the echo ID to the one assigned to this flow
func (ief *icmpEchoFlow) sendToDst(dst netip.Addr, msg *icmp.Message) error { func (ief *icmpEchoFlow) sendToDst(dst netip.Addr, msg *icmp.Message) error {
ief.UpdateLastActive()
originalEcho, err := getICMPEcho(msg) originalEcho, err := getICMPEcho(msg)
if err != nil { if err != nil {
return err return err
@ -80,17 +110,21 @@ func (ief *icmpEchoFlow) sendToDst(dst netip.Addr, msg *icmp.Message) error {
if err != nil { if err != nil {
return err return err
} }
return ief.SendToDst(dst, packet.RawPacket{Data: serializedPacket}) _, err = ief.originConn.WriteTo(serializedPacket, &net.UDPAddr{
IP: dst.AsSlice(),
})
return err
} }
// returnToSrc rewrites the echo ID to the original echo ID from the eyeball // returnToSrc rewrites the echo ID to the original echo ID from the eyeball
func (ief *icmpEchoFlow) returnToSrc(reply *echoReply) error { func (ief *icmpEchoFlow) returnToSrc(reply *echoReply) error {
ief.UpdateLastActive()
reply.echo.ID = ief.originalEchoID reply.echo.ID = ief.originalEchoID
reply.msg.Body = reply.echo reply.msg.Body = reply.echo
pk := packet.ICMP{ pk := packet.ICMP{
IP: &packet.IP{ IP: &packet.IP{
Src: reply.from, Src: reply.from,
Dst: ief.Src, Dst: ief.src,
Protocol: layers.IPProtocol(reply.msg.Type.Protocol()), Protocol: layers.IPProtocol(reply.msg.Type.Protocol()),
TTL: packet.DefaultTTL, TTL: packet.DefaultTTL,
}, },
@ -100,7 +134,7 @@ func (ief *icmpEchoFlow) returnToSrc(reply *echoReply) error {
if err != nil { if err != nil {
return err return err
} }
return ief.ReturnToSrc(serializedPacket) return ief.responder.returnPacket(serializedPacket)
} }
type echoReply struct { type echoReply struct {

View File

@ -52,24 +52,26 @@ func TestFunnelIdleTimeout(t *testing.T) {
}, },
}, },
} }
responder := echoFlowResponder{ muxer := newMockMuxer(0)
decoder: packet.NewICMPDecoder(), responder := packetResponder{
respChan: make(chan []byte), datagramMuxer: muxer,
} }
require.NoError(t, proxy.Request(&pk, &responder)) require.NoError(t, proxy.Request(ctx, &pk, &responder))
responder.validate(t, &pk) validateEchoFlow(t, muxer, &pk)
// Send second request, should reuse the funnel // Send second request, should reuse the funnel
require.NoError(t, proxy.Request(&pk, nil)) require.NoError(t, proxy.Request(ctx, &pk, &packetResponder{
responder.validate(t, &pk) datagramMuxer: nil,
}))
validateEchoFlow(t, muxer, &pk)
time.Sleep(idleTimeout * 2) time.Sleep(idleTimeout * 2)
newResponder := echoFlowResponder{ newMuxer := newMockMuxer(0)
decoder: packet.NewICMPDecoder(), newResponder := packetResponder{
respChan: make(chan []byte), datagramMuxer: newMuxer,
} }
require.NoError(t, proxy.Request(&pk, &newResponder)) require.NoError(t, proxy.Request(ctx, &pk, &newResponder))
newResponder.validate(t, &pk) validateEchoFlow(t, newMuxer, &pk)
cancel() cancel()
<-proxyDone <-proxyDone

View File

@ -265,7 +265,7 @@ func (ip *icmpProxy) Serve(ctx context.Context) error {
// Request sends an ICMP echo request and wait for a reply or timeout. // Request sends an ICMP echo request and wait for a reply or timeout.
// The async version of Win32 APIs take a callback whose memory is not garbage collected, so we use the synchronous version. // The async version of Win32 APIs take a callback whose memory is not garbage collected, so we use the synchronous version.
// It's possible that a slow request will block other requests, so we set the timeout to only 1s. // It's possible that a slow request will block other requests, so we set the timeout to only 1s.
func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { func (ip *icmpProxy) Request(ctx context.Context, pk *packet.ICMP, responder *packetResponder) error {
if pk == nil { if pk == nil {
return errPacketNil return errPacketNil
} }
@ -292,7 +292,7 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er
return nil return nil
} }
func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, data []byte, responder packet.FunnelUniPipe) error { func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, data []byte, responder *packetResponder) error {
var replyType icmp.Type var replyType icmp.Type
if request.Dst.Is4() { if request.Dst.Is4() {
replyType = ipv4.ICMPTypeEchoReply replyType = ipv4.ICMPTypeEchoReply
@ -331,7 +331,7 @@ func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, d
if err != nil { if err != nil {
return err return err
} }
return responder.SendPacket(request.Src, serializedPacket) return responder.returnPacket(serializedPacket)
} }
func (ip *icmpProxy) icmpEchoRoundtrip(dst netip.Addr, echo *icmp.Echo) ([]byte, error) { func (ip *icmpProxy) icmpEchoRoundtrip(dst netip.Addr, echo *icmp.Echo) ([]byte, error) {

View File

@ -37,7 +37,9 @@ func NewICMPRouter(ipv4Addr, ipv6Addr netip.Addr, ipv6Zone string, logger *zerol
ipv4Proxy, ipv4Err := newICMPProxy(ipv4Addr, "", logger, funnelIdleTimeout) ipv4Proxy, ipv4Err := newICMPProxy(ipv4Addr, "", logger, funnelIdleTimeout)
ipv6Proxy, ipv6Err := newICMPProxy(ipv6Addr, ipv6Zone, logger, funnelIdleTimeout) ipv6Proxy, ipv6Err := newICMPProxy(ipv6Addr, ipv6Zone, logger, funnelIdleTimeout)
if ipv4Err != nil && ipv6Err != nil { if ipv4Err != nil && ipv6Err != nil {
return nil, fmt.Errorf("cannot create ICMPv4 proxy: %v nor ICMPv6 proxy: %v", ipv4Err, ipv6Err) err := fmt.Errorf("cannot create ICMPv4 proxy: %v nor ICMPv6 proxy: %v", ipv4Err, ipv6Err)
logger.Debug().Err(err).Msg("ICMP proxy feature is disabled")
return nil, err
} }
if ipv4Err != nil { if ipv4Err != nil {
logger.Debug().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created") logger.Debug().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created")
@ -73,15 +75,18 @@ func (ir *icmpRouter) Serve(ctx context.Context) error {
return fmt.Errorf("ICMPv4 proxy and ICMPv6 proxy are both nil") return fmt.Errorf("ICMPv4 proxy and ICMPv6 proxy are both nil")
} }
func (ir *icmpRouter) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { func (ir *icmpRouter) Request(ctx context.Context, pk *packet.ICMP, responder *packetResponder) error {
if pk == nil {
return errPacketNil
}
if pk.Dst.Is4() { if pk.Dst.Is4() {
if ir.ipv4Proxy != nil { if ir.ipv4Proxy != nil {
return ir.ipv4Proxy.Request(pk, responder) return ir.ipv4Proxy.Request(ctx, pk, responder)
} }
return fmt.Errorf("ICMPv4 proxy was not instantiated") return fmt.Errorf("ICMPv4 proxy was not instantiated")
} }
if ir.ipv6Proxy != nil { if ir.ipv6Proxy != nil {
return ir.ipv6Proxy.Request(pk, responder) return ir.ipv6Proxy.Request(ctx, pk, responder)
} }
return fmt.Errorf("ICMPv6 proxy was not instantiated") return fmt.Errorf("ICMPv6 proxy was not instantiated")
} }

View File

@ -52,9 +52,9 @@ func testICMPRouterEcho(t *testing.T, sendIPv4 bool) {
close(proxyDone) close(proxyDone)
}() }()
responder := echoFlowResponder{ muxer := newMockMuxer(1)
decoder: packet.NewICMPDecoder(), responder := packetResponder{
respChan: make(chan []byte, 1), datagramMuxer: muxer,
} }
protocol := layers.IPProtocolICMPv6 protocol := layers.IPProtocolICMPv6
@ -90,8 +90,8 @@ func testICMPRouterEcho(t *testing.T, sendIPv4 bool) {
}, },
}, },
} }
require.NoError(t, router.Request(&pk, &responder)) require.NoError(t, router.Request(ctx, &pk, &responder))
responder.validate(t, &pk) validateEchoFlow(t, muxer, &pk)
} }
} }
cancel() cancel()
@ -123,9 +123,10 @@ func TestConcurrentRequestsToSameDst(t *testing.T) {
echoID := 38451 + i echoID := 38451 + i
go func() { go func() {
defer wg.Done() defer wg.Done()
responder := echoFlowResponder{
decoder: packet.NewICMPDecoder(), muxer := newMockMuxer(1)
respChan: make(chan []byte, 1), responder := packetResponder{
datagramMuxer: muxer,
} }
for seq := 0; seq < endSeq; seq++ { for seq := 0; seq < endSeq; seq++ {
pk := &packet.ICMP{ pk := &packet.ICMP{
@ -145,15 +146,15 @@ func TestConcurrentRequestsToSameDst(t *testing.T) {
}, },
}, },
} }
require.NoError(t, router.Request(pk, &responder)) require.NoError(t, router.Request(ctx, pk, &responder))
responder.validate(t, pk) validateEchoFlow(t, muxer, pk)
} }
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
responder := echoFlowResponder{ muxer := newMockMuxer(1)
decoder: packet.NewICMPDecoder(), responder := packetResponder{
respChan: make(chan []byte, 1), datagramMuxer: muxer,
} }
for seq := 0; seq < endSeq; seq++ { for seq := 0; seq < endSeq; seq++ {
pk := &packet.ICMP{ pk := &packet.ICMP{
@ -173,8 +174,8 @@ func TestConcurrentRequestsToSameDst(t *testing.T) {
}, },
}, },
} }
require.NoError(t, router.Request(pk, &responder)) require.NoError(t, router.Request(ctx, pk, &responder))
responder.validate(t, pk) validateEchoFlow(t, muxer, pk)
} }
}() }()
} }
@ -241,9 +242,9 @@ func testICMPRouterRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.
router, err := NewICMPRouter(localhostIP, localhostIPv6, "", &noopLogger) router, err := NewICMPRouter(localhostIP, localhostIPv6, "", &noopLogger)
require.NoError(t, err) require.NoError(t, err)
responder := echoFlowResponder{ muxer := newMockMuxer(1)
decoder: packet.NewICMPDecoder(), responder := packetResponder{
respChan: make(chan []byte), datagramMuxer: muxer,
} }
protocol := layers.IPProtocolICMPv4 protocol := layers.IPProtocolICMPv4
if srcDstIP.Is6() { if srcDstIP.Is6() {
@ -259,7 +260,7 @@ func testICMPRouterRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.
}, },
Message: &m, Message: &m,
} }
require.Error(t, router.Request(&pk, &responder)) require.Error(t, router.Request(context.Background(), &pk, &responder))
} }
} }
@ -285,9 +286,10 @@ func (efr *echoFlowResponder) Close() error {
return nil return nil
} }
func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { func validateEchoFlow(t *testing.T, muxer *mockMuxer, echoReq *packet.ICMP) {
pk := <-efr.respChan pk := <-muxer.cfdToEdge
decoded, err := efr.decoder.Decode(packet.RawPacket{Data: pk}) decoder := packet.NewICMPDecoder()
decoded, err := decoder.Decode(packet.RawPacket{Data: pk.Payload()})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, decoded.Src, echoReq.Dst) require.Equal(t, decoded.Src, echoReq.Dst)
require.Equal(t, decoded.Dst, echoReq.Src) require.Equal(t, decoded.Dst, echoReq.Src)

134
ingress/packet_router.go Normal file
View File

@ -0,0 +1,134 @@
package ingress
import (
"context"
"fmt"
"net/netip"
"github.com/rs/zerolog"
"github.com/cloudflare/cloudflared/packet"
quicpogs "github.com/cloudflare/cloudflared/quic"
)
// Upstream of raw packets
type muxer interface {
SendPacket(pk quicpogs.Packet) error
// ReceivePacket waits for the next raw packet from upstream
ReceivePacket(ctx context.Context) (quicpogs.Packet, error)
}
// PacketRouter routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets
type PacketRouter struct {
globalConfig *GlobalRouterConfig
muxer muxer
logger *zerolog.Logger
checkRouterEnabledFunc func() bool
icmpDecoder *packet.ICMPDecoder
encoder *packet.Encoder
}
// GlobalRouterConfig is the configuration shared by all instance of Router.
type GlobalRouterConfig struct {
ICMPRouter *icmpRouter
IPv4Src netip.Addr
IPv6Src netip.Addr
Zone string
}
// NewPacketRouter creates a PacketRouter that handles ICMP packets. Packets are read from muxer but dropped if globalConfig is nil.
func NewPacketRouter(globalConfig *GlobalRouterConfig, muxer muxer, logger *zerolog.Logger, checkRouterEnabledFunc func() bool) *PacketRouter {
return &PacketRouter{
globalConfig: globalConfig,
muxer: muxer,
logger: logger,
checkRouterEnabledFunc: checkRouterEnabledFunc,
icmpDecoder: packet.NewICMPDecoder(),
encoder: packet.NewEncoder(),
}
}
func (r *PacketRouter) Serve(ctx context.Context) error {
for {
rawPacket, responder, err := r.nextPacket(ctx)
if err != nil {
return err
}
r.handlePacket(ctx, rawPacket, responder)
}
}
func (r *PacketRouter) nextPacket(ctx context.Context) (packet.RawPacket, *packetResponder, error) {
pk, err := r.muxer.ReceivePacket(ctx)
if err != nil {
return packet.RawPacket{}, nil, err
}
responder := &packetResponder{
datagramMuxer: r.muxer,
}
switch pk.Type() {
case quicpogs.DatagramTypeIP:
return packet.RawPacket{Data: pk.Payload()}, responder, nil
case quicpogs.DatagramTypeIPWithTrace:
return packet.RawPacket{}, responder, fmt.Errorf("TODO: TUN-6604 Handle IP packet with trace")
default:
return packet.RawPacket{}, nil, fmt.Errorf("unexpected datagram type %d", pk.Type())
}
}
func (r *PacketRouter) handlePacket(ctx context.Context, rawPacket packet.RawPacket, responder *packetResponder) {
// ICMP Proxy feature is disabled, drop packets
if r.globalConfig == nil {
return
}
if enabled := r.checkRouterEnabledFunc(); !enabled {
return
}
icmpPacket, err := r.icmpDecoder.Decode(rawPacket)
if err != nil {
r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram")
return
}
if icmpPacket.TTL <= 1 {
if err := r.sendTTLExceedMsg(ctx, icmpPacket, rawPacket, r.encoder); err != nil {
r.logger.Err(err).Msg("Failed to return ICMP TTL exceed error")
}
return
}
icmpPacket.TTL--
if err := r.globalConfig.ICMPRouter.Request(ctx, icmpPacket, responder); err != nil {
r.logger.Err(err).
Str("src", icmpPacket.Src.String()).
Str("dst", icmpPacket.Dst.String()).
Interface("type", icmpPacket.Type).
Msg("Failed to send ICMP packet")
}
}
func (r *PacketRouter) sendTTLExceedMsg(ctx context.Context, pk *packet.ICMP, rawPacket packet.RawPacket, encoder *packet.Encoder) error {
var srcIP netip.Addr
if pk.Dst.Is4() {
srcIP = r.globalConfig.IPv4Src
} else {
srcIP = r.globalConfig.IPv6Src
}
ttlExceedPacket := packet.NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP)
encodedTTLExceed, err := encoder.Encode(ttlExceedPacket)
if err != nil {
return err
}
return r.muxer.SendPacket(quicpogs.RawPacket(encodedTTLExceed))
}
type packetResponder struct {
datagramMuxer muxer
}
func (pr *packetResponder) returnPacket(rawPacket packet.RawPacket) error {
return pr.datagramMuxer.SendPacket(quicpogs.RawPacket(rawPacket))
}

View File

@ -1,4 +1,4 @@
package packet package ingress
import ( import (
"bytes" "bytes"
@ -10,32 +10,28 @@ import (
"time" "time"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"github.com/cloudflare/cloudflared/packet"
quicpogs "github.com/cloudflare/cloudflared/quic"
) )
var ( var (
noopLogger = zerolog.Nop()
packetConfig = &GlobalRouterConfig{ packetConfig = &GlobalRouterConfig{
ICMPRouter: &mockICMPRouter{}, ICMPRouter: nil,
IPv4Src: netip.MustParseAddr("172.16.0.1"), IPv4Src: netip.MustParseAddr("172.16.0.1"),
IPv6Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), IPv6Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
} }
) )
func TestRouterReturnTTLExceed(t *testing.T) { func TestRouterReturnTTLExceed(t *testing.T) {
upstream := &mockUpstream{ muxer := newMockMuxer(0)
source: make(chan RawPacket),
}
returnPipe := &mockFunnelUniPipe{
uniPipe: make(chan RawPacket),
}
routerEnabled := &routerEnabledChecker{} routerEnabled := &routerEnabledChecker{}
routerEnabled.set(true) routerEnabled.set(true)
router := NewRouter(packetConfig, upstream, returnPipe, &noopLogger, routerEnabled.isEnabled) router := NewPacketRouter(packetConfig, muxer, &noopLogger, routerEnabled.isEnabled)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
routerStopped := make(chan struct{}) routerStopped := make(chan struct{})
go func() { go func() {
@ -43,8 +39,8 @@ func TestRouterReturnTTLExceed(t *testing.T) {
close(routerStopped) close(routerStopped)
}() }()
pk := ICMP{ pk := packet.ICMP{
IP: &IP{ IP: &packet.IP{
Src: netip.MustParseAddr("192.168.1.1"), Src: netip.MustParseAddr("192.168.1.1"),
Dst: netip.MustParseAddr("10.0.0.1"), Dst: netip.MustParseAddr("10.0.0.1"),
Protocol: layers.IPProtocolICMPv4, Protocol: layers.IPProtocolICMPv4,
@ -60,9 +56,9 @@ func TestRouterReturnTTLExceed(t *testing.T) {
}, },
}, },
} }
assertTTLExceed(t, &pk, router.globalConfig.IPv4Src, upstream, returnPipe) assertTTLExceed(t, &pk, router.globalConfig.IPv4Src, muxer)
pk = ICMP{ pk = packet.ICMP{
IP: &IP{ IP: &packet.IP{
Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"), Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"),
Protocol: layers.IPProtocolICMPv6, Protocol: layers.IPProtocolICMPv6,
@ -78,21 +74,16 @@ func TestRouterReturnTTLExceed(t *testing.T) {
}, },
}, },
} }
assertTTLExceed(t, &pk, router.globalConfig.IPv6Src, upstream, returnPipe) assertTTLExceed(t, &pk, router.globalConfig.IPv6Src, muxer)
cancel() cancel()
<-routerStopped <-routerStopped
} }
func TestRouterCheckEnabled(t *testing.T) { func TestRouterCheckEnabled(t *testing.T) {
upstream := &mockUpstream{ muxer := newMockMuxer(0)
source: make(chan RawPacket),
}
returnPipe := &mockFunnelUniPipe{
uniPipe: make(chan RawPacket),
}
routerEnabled := &routerEnabledChecker{} routerEnabled := &routerEnabledChecker{}
router := NewRouter(packetConfig, upstream, returnPipe, &noopLogger, routerEnabled.isEnabled) router := NewPacketRouter(packetConfig, muxer, &noopLogger, routerEnabled.isEnabled)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
routerStopped := make(chan struct{}) routerStopped := make(chan struct{})
go func() { go func() {
@ -100,8 +91,8 @@ func TestRouterCheckEnabled(t *testing.T) {
close(routerStopped) close(routerStopped)
}() }()
pk := ICMP{ pk := packet.ICMP{
IP: &IP{ IP: &packet.IP{
Src: netip.MustParseAddr("192.168.1.1"), Src: netip.MustParseAddr("192.168.1.1"),
Dst: netip.MustParseAddr("10.0.0.1"), Dst: netip.MustParseAddr("10.0.0.1"),
Protocol: layers.IPProtocolICMPv4, Protocol: layers.IPProtocolICMPv4,
@ -119,23 +110,28 @@ func TestRouterCheckEnabled(t *testing.T) {
} }
// router is disabled // router is disabled
require.NoError(t, upstream.send(&pk)) encoder := packet.NewEncoder()
encodedPacket, err := encoder.Encode(&pk)
require.NoError(t, err)
sendPacket := quicpogs.RawPacket(encodedPacket)
muxer.edgeToCfd <- sendPacket
select { select {
case <-time.After(time.Millisecond * 10): case <-time.After(time.Millisecond * 10):
case <-returnPipe.uniPipe: case <-muxer.cfdToEdge:
t.Error("Unexpected reply when router is disabled") t.Error("Unexpected reply when router is disabled")
} }
routerEnabled.set(true) routerEnabled.set(true)
// router is enabled, expects reply // router is enabled, expects reply
require.NoError(t, upstream.send(&pk)) muxer.edgeToCfd <- sendPacket
<-returnPipe.uniPipe <-muxer.cfdToEdge
routerEnabled.set(false) routerEnabled.set(false)
// router is disabled // router is disabled
require.NoError(t, upstream.send(&pk)) muxer.edgeToCfd <- sendPacket
select { select {
case <-time.After(time.Millisecond * 10): case <-time.After(time.Millisecond * 10):
case <-returnPipe.uniPipe: case <-muxer.cfdToEdge:
t.Error("Unexpected reply when router is disabled") t.Error("Unexpected reply when router is disabled")
} }
@ -143,66 +139,83 @@ func TestRouterCheckEnabled(t *testing.T) {
<-routerStopped <-routerStopped
} }
func assertTTLExceed(t *testing.T, originalPacket *ICMP, expectedSrc netip.Addr, upstream *mockUpstream, returnPipe *mockFunnelUniPipe) { func assertTTLExceed(t *testing.T, originalPacket *packet.ICMP, expectedSrc netip.Addr, muxer *mockMuxer) {
encoder := NewEncoder() encoder := packet.NewEncoder()
rawPacket, err := encoder.Encode(originalPacket) rawPacket, err := encoder.Encode(originalPacket)
require.NoError(t, err) require.NoError(t, err)
upstream.source <- rawPacket muxer.edgeToCfd <- quicpogs.RawPacket(rawPacket)
resp := <-returnPipe.uniPipe resp := <-muxer.cfdToEdge
decoder := NewICMPDecoder() decoder := packet.NewICMPDecoder()
decoded, err := decoder.Decode(resp) decoded, err := decoder.Decode(packet.RawPacket(resp.(quicpogs.RawPacket)))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expectedSrc, decoded.Src) require.Equal(t, expectedSrc, decoded.Src)
require.Equal(t, originalPacket.Src, decoded.Dst) require.Equal(t, originalPacket.Src, decoded.Dst)
require.Equal(t, originalPacket.Protocol, decoded.Protocol) require.Equal(t, originalPacket.Protocol, decoded.Protocol)
require.Equal(t, DefaultTTL, decoded.TTL) require.Equal(t, packet.DefaultTTL, decoded.TTL)
if originalPacket.Dst.Is4() { if originalPacket.Dst.Is4() {
require.Equal(t, ipv4.ICMPTypeTimeExceeded, decoded.Type) require.Equal(t, ipv4.ICMPTypeTimeExceeded, decoded.Type)
} else { } else {
require.Equal(t, ipv6.ICMPTypeTimeExceeded, decoded.Type) require.Equal(t, ipv6.ICMPTypeTimeExceeded, decoded.Type)
} }
require.Equal(t, 0, decoded.Code) require.Equal(t, 0, decoded.Code)
assertICMPChecksum(t, decoded)
timeExceed, ok := decoded.Body.(*icmp.TimeExceeded) timeExceed, ok := decoded.Body.(*icmp.TimeExceeded)
require.True(t, ok) require.True(t, ok)
require.True(t, bytes.Equal(rawPacket.Data, timeExceed.Data)) require.True(t, bytes.Equal(rawPacket.Data, timeExceed.Data))
} }
type mockUpstream struct { type mockMuxer struct {
source chan RawPacket cfdToEdge chan quicpogs.Packet
edgeToCfd chan quicpogs.Packet
} }
func (ms *mockUpstream) send(pk Packet) error { func newMockMuxer(capacity int) *mockMuxer {
encoder := NewEncoder() return &mockMuxer{
rawPacket, err := encoder.Encode(pk) cfdToEdge: make(chan quicpogs.Packet, capacity),
if err != nil { edgeToCfd: make(chan quicpogs.Packet, capacity),
return err
} }
ms.source <- rawPacket }
// Copy packet, because icmpProxy expects the encoder buffer to be reusable after the packet is sent
func (mm *mockMuxer) SendPacket(pk quicpogs.Packet) error {
payload := pk.Payload()
copiedPayload := make([]byte, len(payload))
copy(copiedPayload, payload)
metadata := pk.Metadata()
copiedMetadata := make([]byte, len(metadata))
copy(copiedMetadata, metadata)
var copiedPacket quicpogs.Packet
switch pk.Type() {
case quicpogs.DatagramTypeIP:
copiedPacket = quicpogs.RawPacket(packet.RawPacket{
Data: copiedPayload,
})
case quicpogs.DatagramTypeIPWithTrace:
copiedPacket = &quicpogs.TracedPacket{
Packet: packet.RawPacket{
Data: copiedPayload,
},
TracingIdentity: copiedMetadata,
}
default:
return fmt.Errorf("unexpected metadata type %d", pk.Type())
}
mm.cfdToEdge <- copiedPacket
return nil return nil
} }
func (ms *mockUpstream) ReceivePacket(ctx context.Context) (RawPacket, error) { func (mm *mockMuxer) ReceivePacket(ctx context.Context) (quicpogs.Packet, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return RawPacket{}, ctx.Err() return nil, ctx.Err()
case pk := <-ms.source: case pk := <-mm.edgeToCfd:
return pk, nil return pk, nil
} }
} }
type mockICMPRouter struct{}
func (mir mockICMPRouter) Serve(ctx context.Context) error {
return fmt.Errorf("Serve not implemented by mockICMPRouter")
}
func (mir mockICMPRouter) Request(pk *ICMP, responder FunnelUniPipe) error {
return fmt.Errorf("Request not implemented by mockICMPRouter")
}
type routerEnabledChecker struct { type routerEnabledChecker struct {
enabled uint32 enabled uint32
} }

View File

@ -16,10 +16,6 @@ var (
// Funnel is an abstraction to pipe from 1 src to 1 or more destinations // Funnel is an abstraction to pipe from 1 src to 1 or more destinations
type Funnel interface { type Funnel interface {
// SendToDst sends a raw packet to a destination
SendToDst(dst netip.Addr, pk RawPacket) error
// ReturnToSrc returns a raw packet to the source
ReturnToSrc(pk RawPacket) error
// LastActive returns the last time SendToDst or ReturnToSrc is called // LastActive returns the last time SendToDst or ReturnToSrc is called
LastActive() time.Time LastActive() time.Time
// Close closes the funnel. Further call to SendToDst or ReturnToSrc should return an error // Close closes the funnel. Further call to SendToDst or ReturnToSrc should return an error
@ -36,73 +32,26 @@ type FunnelUniPipe interface {
Close() error Close() error
} }
// RawPacketFunnel is an implementation of Funnel that sends raw packets. It can be embedded in other structs to type ActivityTracker struct {
// satisfy the Funnel interface.
type RawPacketFunnel struct {
Src netip.Addr
// last active unix time. Unit is seconds // last active unix time. Unit is seconds
lastActive int64 lastActive int64
sendPipe FunnelUniPipe
returnPipe FunnelUniPipe
} }
func NewRawPacketFunnel(src netip.Addr, sendPipe, returnPipe FunnelUniPipe) *RawPacketFunnel { func NewActivityTracker() *ActivityTracker {
return &RawPacketFunnel{ return &ActivityTracker{
Src: src,
lastActive: time.Now().Unix(), lastActive: time.Now().Unix(),
sendPipe: sendPipe,
returnPipe: returnPipe,
} }
} }
func (rpf *RawPacketFunnel) SendToDst(dst netip.Addr, pk RawPacket) error { func (at *ActivityTracker) UpdateLastActive() {
rpf.updateLastActive() atomic.StoreInt64(&at.lastActive, time.Now().Unix())
return rpf.sendPipe.SendPacket(dst, pk)
} }
func (rpf *RawPacketFunnel) ReturnToSrc(pk RawPacket) error { func (at *ActivityTracker) LastActive() time.Time {
rpf.updateLastActive() lastActive := atomic.LoadInt64(&at.lastActive)
return rpf.returnPipe.SendPacket(rpf.Src, pk)
}
func (rpf *RawPacketFunnel) updateLastActive() {
atomic.StoreInt64(&rpf.lastActive, time.Now().Unix())
}
func (rpf *RawPacketFunnel) LastActive() time.Time {
lastActive := atomic.LoadInt64(&rpf.lastActive)
return time.Unix(lastActive, 0) return time.Unix(lastActive, 0)
} }
func (rpf *RawPacketFunnel) Close() error {
sendPipeErr := rpf.sendPipe.Close()
returnPipeErr := rpf.returnPipe.Close()
if sendPipeErr != nil {
return sendPipeErr
}
if returnPipeErr != nil {
return returnPipeErr
}
return nil
}
func (rpf *RawPacketFunnel) Equal(other Funnel) bool {
otherRawFunnel, ok := other.(*RawPacketFunnel)
if !ok {
return false
}
if rpf.Src != otherRawFunnel.Src {
return false
}
if rpf.sendPipe != otherRawFunnel.sendPipe {
return false
}
if rpf.returnPipe != otherRawFunnel.returnPipe {
return false
}
return true
}
// FunnelID represents a key type that can be used by FunnelTracker // FunnelID represents a key type that can be used by FunnelTracker
type FunnelID interface { type FunnelID interface {
// Type returns the name of the type that implements the FunnelID // Type returns the name of the type that implements the FunnelID

View File

@ -1,108 +0,0 @@
package packet
import (
"context"
"net/netip"
"github.com/rs/zerolog"
)
// ICMPRouter sends ICMP messages and listens for their responses
type ICMPRouter interface {
// Serve starts listening for responses to the requests until context is done
Serve(ctx context.Context) error
// Request sends an ICMP message. Implementations should not modify pk after the function returns.
Request(pk *ICMP, responder FunnelUniPipe) error
}
// Upstream of raw packets
type Upstream interface {
// ReceivePacket waits for the next raw packet from upstream
ReceivePacket(ctx context.Context) (RawPacket, error)
}
// Router routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets
type Router struct {
upstream Upstream
returnPipe FunnelUniPipe
globalConfig *GlobalRouterConfig
logger *zerolog.Logger
checkRouterEnabledFunc func() bool
}
// GlobalRouterConfig is the configuration shared by all instance of Router.
type GlobalRouterConfig struct {
ICMPRouter ICMPRouter
IPv4Src netip.Addr
IPv6Src netip.Addr
Zone string
}
func NewRouter(globalConfig *GlobalRouterConfig, upstream Upstream, returnPipe FunnelUniPipe, logger *zerolog.Logger, checkRouterEnabledFunc func() bool) *Router {
return &Router{
upstream: upstream,
returnPipe: returnPipe,
globalConfig: globalConfig,
logger: logger,
checkRouterEnabledFunc: checkRouterEnabledFunc,
}
}
func (r *Router) Serve(ctx context.Context) error {
icmpDecoder := NewICMPDecoder()
encoder := NewEncoder()
for {
rawPacket, err := r.upstream.ReceivePacket(ctx)
if err != nil {
return err
}
// Drop packets if ICMPRouter wasn't created
if r.globalConfig == nil {
continue
}
if enabled := r.checkRouterEnabledFunc(); !enabled {
continue
}
icmpPacket, err := icmpDecoder.Decode(rawPacket)
if err != nil {
r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram")
continue
}
if icmpPacket.TTL <= 1 {
if err := r.sendTTLExceedMsg(icmpPacket, rawPacket, encoder); err != nil {
r.logger.Err(err).Msg("Failed to return ICMP TTL exceed error")
}
continue
}
icmpPacket.TTL--
if err := r.globalConfig.ICMPRouter.Request(icmpPacket, r.returnPipe); err != nil {
r.logger.Err(err).
Str("src", icmpPacket.Src.String()).
Str("dst", icmpPacket.Dst.String()).
Interface("type", icmpPacket.Type).
Msg("Failed to send ICMP packet")
continue
}
}
}
func (r *Router) sendTTLExceedMsg(pk *ICMP, rawPacket RawPacket, encoder *Encoder) error {
var srcIP netip.Addr
if pk.Dst.Is4() {
srcIP = r.globalConfig.IPv4Src
} else {
srcIP = r.globalConfig.IPv6Src
}
ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP)
encodedTTLExceed, err := encoder.Encode(ttlExceedPacket)
if err != nil {
return err
}
return r.returnPipe.SendPacket(pk.Src, encodedTTLExceed)
}

View File

@ -20,8 +20,8 @@ import (
"github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/edgediscovery"
"github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/edgediscovery/allregions"
"github.com/cloudflare/cloudflared/h2mux" "github.com/cloudflare/cloudflared/h2mux"
"github.com/cloudflare/cloudflared/ingress"
"github.com/cloudflare/cloudflared/orchestration" "github.com/cloudflare/cloudflared/orchestration"
"github.com/cloudflare/cloudflared/packet"
quicpogs "github.com/cloudflare/cloudflared/quic" quicpogs "github.com/cloudflare/cloudflared/quic"
"github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/retry"
"github.com/cloudflare/cloudflared/signal" "github.com/cloudflare/cloudflared/signal"
@ -70,7 +70,7 @@ type TunnelConfig struct {
MuxerConfig *connection.MuxerConfig MuxerConfig *connection.MuxerConfig
ProtocolSelector connection.ProtocolSelector ProtocolSelector connection.ProtocolSelector
EdgeTLSConfigs map[connection.Protocol]*tls.Config EdgeTLSConfigs map[connection.Protocol]*tls.Config
PacketConfig *packet.GlobalRouterConfig PacketConfig *ingress.GlobalRouterConfig
} }
func (c *TunnelConfig) registrationOptions(connectionID uint8, OriginLocalIP string, uuid uuid.UUID) *tunnelpogs.RegistrationOptions { func (c *TunnelConfig) registrationOptions(connectionID uint8, OriginLocalIP string, uuid uuid.UUID) *tunnelpogs.RegistrationOptions {