mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:49:57 +00:00
TUN-6654: Support ICMPv6 on Linux and Darwin
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
@@ -12,13 +14,15 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
|
||||
"github.com/cloudflare/cloudflared/packet"
|
||||
)
|
||||
|
||||
var (
|
||||
noopLogger = zerolog.Nop()
|
||||
localhostIP = netip.MustParseAddr("127.0.0.1")
|
||||
noopLogger = zerolog.Nop()
|
||||
localhostIP = netip.MustParseAddr("127.0.0.1")
|
||||
localhostIPv6 = netip.MustParseAddr("::1")
|
||||
)
|
||||
|
||||
// TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the
|
||||
@@ -28,12 +32,20 @@ var (
|
||||
// is allowed in ping_group_range. See the following gist for how to do that:
|
||||
// https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md
|
||||
func TestICMPProxyEcho(t *testing.T) {
|
||||
testICMPProxyEcho(t, true)
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("TODO: TUN-6743: test ICMPv6 on Windows")
|
||||
}
|
||||
testICMPProxyEcho(t, false)
|
||||
}
|
||||
|
||||
func testICMPProxyEcho(t *testing.T, sendIPv4 bool) {
|
||||
const (
|
||||
echoID = 36571
|
||||
endSeq = 100
|
||||
endSeq = 20
|
||||
)
|
||||
|
||||
proxy, err := NewICMPProxy(localhostIP, &noopLogger)
|
||||
proxy, err := NewICMPProxy(&noopLogger)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxyDone := make(chan struct{})
|
||||
@@ -48,37 +60,30 @@ func TestICMPProxyEcho(t *testing.T) {
|
||||
respChan: make(chan []byte, 1),
|
||||
}
|
||||
|
||||
ips := []packet.IP{
|
||||
{
|
||||
Src: localhostIP,
|
||||
Dst: localhostIP,
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
},
|
||||
protocol := layers.IPProtocolICMPv6
|
||||
if sendIPv4 {
|
||||
protocol = layers.IPProtocolICMPv4
|
||||
}
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
require.NoError(t, err)
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||
ip := ipnet.IP
|
||||
if !ipnet.IP.IsLoopback() && ip.IsPrivate() && ip.To4() != nil {
|
||||
localIP := netip.MustParseAddr(ipnet.IP.String())
|
||||
ips = append(ips, packet.IP{
|
||||
Src: localIP,
|
||||
Dst: localIP,
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
})
|
||||
break
|
||||
}
|
||||
localIPs := getLocalIPs(t, sendIPv4)
|
||||
ips := make([]*packet.IP, len(localIPs))
|
||||
for i, localIP := range localIPs {
|
||||
ips[i] = &packet.IP{
|
||||
Src: localIP,
|
||||
Dst: localIP,
|
||||
Protocol: protocol,
|
||||
}
|
||||
}
|
||||
|
||||
var icmpType icmp.Type = ipv6.ICMPTypeEchoRequest
|
||||
if sendIPv4 {
|
||||
icmpType = ipv4.ICMPTypeEcho
|
||||
}
|
||||
for seq := 0; seq < endSeq; seq++ {
|
||||
for i, ip := range ips {
|
||||
pk := packet.ICMP{
|
||||
IP: &ip,
|
||||
IP: ip,
|
||||
Message: &icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Type: icmpType,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: echoID + i,
|
||||
@@ -121,19 +126,52 @@ func TestICMPProxyRejectNotEcho(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
proxy, err := NewICMPProxy(localhostIP, &noopLogger)
|
||||
testICMPProxyRejectNotEcho(t, localhostIP, msgs)
|
||||
msgsV6 := []icmp.Message{
|
||||
{
|
||||
Type: ipv6.ICMPTypeDestinationUnreachable,
|
||||
Code: 3,
|
||||
Body: &icmp.DstUnreach{
|
||||
Data: []byte("original packet"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ipv6.ICMPTypeTimeExceeded,
|
||||
Code: 0,
|
||||
Body: &icmp.TimeExceeded{
|
||||
Data: []byte("original packet"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ipv6.ICMPTypePacketTooBig,
|
||||
Code: 0,
|
||||
Body: &icmp.PacketTooBig{
|
||||
MTU: 1280,
|
||||
Data: []byte("original packet"),
|
||||
},
|
||||
},
|
||||
}
|
||||
testICMPProxyRejectNotEcho(t, localhostIPv6, msgsV6)
|
||||
}
|
||||
|
||||
func testICMPProxyRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.Message) {
|
||||
proxy, err := NewICMPProxy(&noopLogger)
|
||||
require.NoError(t, err)
|
||||
|
||||
responder := echoFlowResponder{
|
||||
decoder: packet.NewICMPDecoder(),
|
||||
respChan: make(chan []byte),
|
||||
}
|
||||
protocol := layers.IPProtocolICMPv4
|
||||
if srcDstIP.Is6() {
|
||||
protocol = layers.IPProtocolICMPv6
|
||||
}
|
||||
for _, m := range msgs {
|
||||
pk := packet.ICMP{
|
||||
IP: &packet.IP{
|
||||
Src: localhostIP,
|
||||
Dst: localhostIP,
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
Src: srcDstIP,
|
||||
Dst: srcDstIP,
|
||||
Protocol: protocol,
|
||||
},
|
||||
Message: &m,
|
||||
}
|
||||
@@ -166,8 +204,40 @@ func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) {
|
||||
require.Equal(t, decoded.Dst, echoReq.Src)
|
||||
require.Equal(t, echoReq.Protocol, decoded.Protocol)
|
||||
|
||||
require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type)
|
||||
if echoReq.Type == ipv4.ICMPTypeEcho {
|
||||
require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type)
|
||||
} else {
|
||||
require.Equal(t, ipv6.ICMPTypeEchoReply, decoded.Type)
|
||||
}
|
||||
require.Equal(t, 0, decoded.Code)
|
||||
require.NotZero(t, decoded.Checksum)
|
||||
if echoReq.Type == ipv4.ICMPTypeEcho {
|
||||
require.NotZero(t, decoded.Checksum)
|
||||
} else {
|
||||
// For ICMPv6, the kernel will compute the checksum during transmission unless pseudo header is not nil
|
||||
require.Zero(t, decoded.Checksum)
|
||||
}
|
||||
|
||||
require.Equal(t, echoReq.Body, decoded.Body)
|
||||
}
|
||||
|
||||
func getLocalIPs(t *testing.T, ipv4 bool) []netip.Addr {
|
||||
interfaces, err := net.Interfaces()
|
||||
require.NoError(t, err)
|
||||
localIPs := []netip.Addr{}
|
||||
for _, i := range interfaces {
|
||||
// Skip TUN devices
|
||||
if strings.Contains(i.Name, "tun") {
|
||||
continue
|
||||
}
|
||||
addrs, err := i.Addrs()
|
||||
require.NoError(t, err)
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && (ipnet.IP.IsPrivate() || ipnet.IP.IsLoopback()) {
|
||||
if (ipv4 && ipnet.IP.To4() != nil) || (!ipv4 && ipnet.IP.To4() == nil) {
|
||||
localIPs = append(localIPs, netip.MustParseAddr(ipnet.IP.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return localIPs
|
||||
}
|
||||
|
Reference in New Issue
Block a user