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
|
|
}
|