mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-06-18 08:56:34 +00:00

A new ICMPResponder interface is introduced to provide different implementations of how the ICMP flows should return to the QUIC connection muxer. Improves usages of netip.AddrPort to leverage the embedded zone field for IPv6 addresses. Closes TUN-8640
140 lines
3.2 KiB
Go
140 lines
3.2 KiB
Go
//go:build darwin || linux
|
|
|
|
package ingress
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fortytw2/leaktest"
|
|
"github.com/google/gopacket/layers"
|
|
"github.com/rs/zerolog"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/net/icmp"
|
|
"golang.org/x/net/ipv4"
|
|
|
|
"github.com/cloudflare/cloudflared/packet"
|
|
)
|
|
|
|
func TestFunnelIdleTimeout(t *testing.T) {
|
|
defer leaktest.Check(t)()
|
|
|
|
const (
|
|
idleTimeout = time.Second
|
|
echoID = 42573
|
|
startSeq = 8129
|
|
)
|
|
logger := zerolog.New(os.Stderr)
|
|
proxy, err := newICMPProxy(localhostIP, &logger, idleTimeout)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
proxyDone := make(chan struct{})
|
|
go func() {
|
|
proxy.Serve(ctx)
|
|
close(proxyDone)
|
|
}()
|
|
|
|
// Send a packet to register the flow
|
|
pk := packet.ICMP{
|
|
IP: &packet.IP{
|
|
Src: localhostIP,
|
|
Dst: localhostIP,
|
|
Protocol: layers.IPProtocolICMPv4,
|
|
},
|
|
Message: &icmp.Message{
|
|
Type: ipv4.ICMPTypeEcho,
|
|
Code: 0,
|
|
Body: &icmp.Echo{
|
|
ID: echoID,
|
|
Seq: startSeq,
|
|
Data: []byte(t.Name()),
|
|
},
|
|
},
|
|
}
|
|
muxer := newMockMuxer(0)
|
|
responder := newPacketResponder(muxer, 0, packet.NewEncoder())
|
|
require.NoError(t, proxy.Request(ctx, &pk, responder))
|
|
validateEchoFlow(t, <-muxer.cfdToEdge, &pk)
|
|
|
|
// Send second request, should reuse the funnel
|
|
require.NoError(t, proxy.Request(ctx, &pk, responder))
|
|
validateEchoFlow(t, <-muxer.cfdToEdge, &pk)
|
|
|
|
// New muxer on a different connection should use a new flow
|
|
time.Sleep(idleTimeout * 2)
|
|
newMuxer := newMockMuxer(0)
|
|
newResponder := newPacketResponder(newMuxer, 1, packet.NewEncoder())
|
|
require.NoError(t, proxy.Request(ctx, &pk, newResponder))
|
|
validateEchoFlow(t, <-newMuxer.cfdToEdge, &pk)
|
|
|
|
time.Sleep(idleTimeout * 2)
|
|
cancel()
|
|
<-proxyDone
|
|
}
|
|
|
|
func TestReuseFunnel(t *testing.T) {
|
|
defer leaktest.Check(t)()
|
|
|
|
const (
|
|
idleTimeout = time.Millisecond * 100
|
|
echoID = 42573
|
|
startSeq = 8129
|
|
)
|
|
logger := zerolog.New(os.Stderr)
|
|
proxy, err := newICMPProxy(localhostIP, &logger, idleTimeout)
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
proxyDone := make(chan struct{})
|
|
go func() {
|
|
proxy.Serve(ctx)
|
|
close(proxyDone)
|
|
}()
|
|
|
|
// Send a packet to register the flow
|
|
pk := packet.ICMP{
|
|
IP: &packet.IP{
|
|
Src: localhostIP,
|
|
Dst: localhostIP,
|
|
Protocol: layers.IPProtocolICMPv4,
|
|
},
|
|
Message: &icmp.Message{
|
|
Type: ipv4.ICMPTypeEcho,
|
|
Code: 0,
|
|
Body: &icmp.Echo{
|
|
ID: echoID,
|
|
Seq: startSeq,
|
|
Data: []byte(t.Name()),
|
|
},
|
|
},
|
|
}
|
|
tuple := flow3Tuple{
|
|
srcIP: pk.Src,
|
|
dstIP: pk.Dst,
|
|
originalEchoID: echoID,
|
|
}
|
|
muxer := newMockMuxer(0)
|
|
responder := newPacketResponder(muxer, 0, packet.NewEncoder())
|
|
require.NoError(t, proxy.Request(ctx, &pk, responder))
|
|
validateEchoFlow(t, <-muxer.cfdToEdge, &pk)
|
|
funnel1, found := getFunnel(t, proxy, tuple)
|
|
require.True(t, found)
|
|
|
|
// Send second request, should reuse the funnel
|
|
require.NoError(t, proxy.Request(ctx, &pk, responder))
|
|
validateEchoFlow(t, <-muxer.cfdToEdge, &pk)
|
|
funnel2, found := getFunnel(t, proxy, tuple)
|
|
require.True(t, found)
|
|
require.Equal(t, funnel1, funnel2)
|
|
|
|
time.Sleep(idleTimeout * 2)
|
|
|
|
cancel()
|
|
<-proxyDone
|
|
}
|