mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:19:57 +00:00
TUN-6592: Decrement TTL and return ICMP time exceed if it's 0
This commit is contained in:
@@ -16,8 +16,8 @@ func FindProtocol(p []byte) (layers.IPProtocol, error) {
|
||||
}
|
||||
switch version {
|
||||
case 4:
|
||||
if len(p) < ipv4HeaderLen {
|
||||
return 0, fmt.Errorf("IPv4 packet should have at least %d bytes, got %d bytes", ipv4HeaderLen, len(p))
|
||||
if len(p) < ipv4MinHeaderLen {
|
||||
return 0, fmt.Errorf("IPv4 packet should have at least %d bytes, got %d bytes", ipv4MinHeaderLen, len(p))
|
||||
}
|
||||
// Protocol is in the 10th byte of IPv4 header
|
||||
return layers.IPProtocol(p[9]), nil
|
||||
|
@@ -61,11 +61,13 @@ func TestDecodeICMP(t *testing.T) {
|
||||
Src: netip.MustParseAddr("172.16.0.1"),
|
||||
Dst: netip.MustParseAddr("10.0.0.1"),
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
TTL: DefaultTTL,
|
||||
}
|
||||
ipv6Packet = IP{
|
||||
Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
|
||||
Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"),
|
||||
Protocol: layers.IPProtocolICMPv6,
|
||||
TTL: DefaultTTL,
|
||||
}
|
||||
icmpID = 100
|
||||
icmpSeq = 52819
|
||||
@@ -171,7 +173,7 @@ func TestDecodeBadPackets(t *testing.T) {
|
||||
SrcIP: srcIPv4,
|
||||
DstIP: dstIPv4,
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
TTL: defaultTTL,
|
||||
TTL: DefaultTTL,
|
||||
}
|
||||
icmpLayer := layers.ICMPv4{
|
||||
TypeCode: layers.CreateICMPv4TypeCode(uint8(ipv4.ICMPTypeEcho), 0),
|
||||
@@ -231,6 +233,7 @@ func assertIPLayer(t *testing.T, expected, actual *IP) {
|
||||
require.Equal(t, expected.Src, actual.Src)
|
||||
require.Equal(t, expected.Dst, actual.Dst)
|
||||
require.Equal(t, expected.Protocol, actual.Protocol)
|
||||
require.Equal(t, expected.TTL, actual.TTL)
|
||||
}
|
||||
|
||||
type UDP struct {
|
||||
|
16
packet/funnel_test.go
Normal file
16
packet/funnel_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package packet
|
||||
|
||||
import "net/netip"
|
||||
|
||||
type mockFunnelUniPipe struct {
|
||||
uniPipe chan RawPacket
|
||||
}
|
||||
|
||||
func (mfui *mockFunnelUniPipe) SendPacket(dst netip.Addr, pk RawPacket) error {
|
||||
mfui.uniPipe <- pk
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfui *mockFunnelUniPipe) Close() error {
|
||||
return nil
|
||||
}
|
@@ -7,12 +7,20 @@ import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTTL uint8 = 64
|
||||
ipv4HeaderLen = 20
|
||||
ipv6HeaderLen = 40
|
||||
ipv4MinHeaderLen = 20
|
||||
ipv6HeaderLen = 40
|
||||
ipv4MinMTU = 576
|
||||
ipv6MinMTU = 1280
|
||||
icmpHeaderLen = 8
|
||||
// https://www.rfc-editor.org/rfc/rfc792 and https://datatracker.ietf.org/doc/html/rfc4443#section-3.3 define 2 codes.
|
||||
// 0 = ttl exceed in transit, 1 = fragment reassembly time exceeded
|
||||
icmpTTLExceedInTransitCode = 0
|
||||
DefaultTTL uint8 = 255
|
||||
)
|
||||
|
||||
// Packet represents an IP packet or a packet that is encapsulated by IP
|
||||
@@ -28,6 +36,7 @@ type IP struct {
|
||||
Src netip.Addr
|
||||
Dst netip.Addr
|
||||
Protocol layers.IPProtocol
|
||||
TTL uint8
|
||||
}
|
||||
|
||||
func newIPv4(ipLayer *layers.IPv4) (*IP, error) {
|
||||
@@ -43,6 +52,7 @@ func newIPv4(ipLayer *layers.IPv4) (*IP, error) {
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Protocol: ipLayer.Protocol,
|
||||
TTL: ipLayer.TTL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -59,6 +69,7 @@ func newIPv6(ipLayer *layers.IPv6) (*IP, error) {
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Protocol: ipLayer.NextHeader,
|
||||
TTL: ipLayer.HopLimit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -78,7 +89,7 @@ func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
|
||||
SrcIP: ip.Src.AsSlice(),
|
||||
DstIP: ip.Dst.AsSlice(),
|
||||
Protocol: layers.IPProtocol(ip.Protocol),
|
||||
TTL: defaultTTL,
|
||||
TTL: ip.TTL,
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
@@ -88,7 +99,7 @@ func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
|
||||
SrcIP: ip.Src.AsSlice(),
|
||||
DstIP: ip.Dst.AsSlice(),
|
||||
NextHeader: layers.IPProtocol(ip.Protocol),
|
||||
HopLimit: defaultTTL,
|
||||
HopLimit: ip.TTL,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -113,3 +124,51 @@ func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
|
||||
icmpLayer := gopacket.Payload(msg)
|
||||
return append(ipLayers, icmpLayer), nil
|
||||
}
|
||||
|
||||
func NewICMPTTLExceedPacket(originalIP *IP, originalPacket RawPacket, routerIP netip.Addr) *ICMP {
|
||||
var (
|
||||
protocol layers.IPProtocol
|
||||
icmpType icmp.Type
|
||||
)
|
||||
if originalIP.Dst.Is4() {
|
||||
protocol = layers.IPProtocolICMPv4
|
||||
icmpType = ipv4.ICMPTypeTimeExceeded
|
||||
} else {
|
||||
protocol = layers.IPProtocolICMPv6
|
||||
icmpType = ipv6.ICMPTypeTimeExceeded
|
||||
}
|
||||
return &ICMP{
|
||||
IP: &IP{
|
||||
Src: routerIP,
|
||||
Dst: originalIP.Src,
|
||||
Protocol: protocol,
|
||||
TTL: DefaultTTL,
|
||||
},
|
||||
Message: &icmp.Message{
|
||||
Type: icmpType,
|
||||
Code: icmpTTLExceedInTransitCode,
|
||||
Body: &icmp.TimeExceeded{
|
||||
Data: originalDatagram(originalPacket, originalIP.Dst.Is4()),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// originalDatagram returns a slice of the original datagram for ICMP error messages
|
||||
// https://www.rfc-editor.org/rfc/rfc1812#section-4.3.2.3 suggests to copy as much without exceeding 576 bytes.
|
||||
// https://datatracker.ietf.org/doc/html/rfc4443#section-3.3 suggests to copy as much without exceeding 1280 bytes
|
||||
func originalDatagram(originalPacket RawPacket, isIPv4 bool) []byte {
|
||||
var upperBound int
|
||||
if isIPv4 {
|
||||
upperBound = ipv4MinMTU - ipv4MinHeaderLen - icmpHeaderLen
|
||||
if upperBound > len(originalPacket.Data) {
|
||||
upperBound = len(originalPacket.Data)
|
||||
}
|
||||
} else {
|
||||
upperBound = ipv6MinMTU - ipv6HeaderLen - icmpHeaderLen
|
||||
if upperBound > len(originalPacket.Data) {
|
||||
upperBound = len(originalPacket.Data)
|
||||
}
|
||||
}
|
||||
return originalPacket.Data[:upperBound]
|
||||
}
|
||||
|
104
packet/packet_test.go
Normal file
104
packet/packet_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
func TestNewICMPTTLExceedPacket(t *testing.T) {
|
||||
ipv4Packet := IP{
|
||||
Src: netip.MustParseAddr("192.168.1.1"),
|
||||
Dst: netip.MustParseAddr("10.0.0.1"),
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
TTL: 0,
|
||||
}
|
||||
icmpV4Packet := ICMP{
|
||||
IP: &ipv4Packet,
|
||||
Message: &icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: 25821,
|
||||
Seq: 58129,
|
||||
Data: []byte("test ttl=0"),
|
||||
},
|
||||
},
|
||||
}
|
||||
assertTTLExceedPacket(t, &icmpV4Packet)
|
||||
icmpV4Packet.Body = &icmp.Echo{
|
||||
ID: 3487,
|
||||
Seq: 19183,
|
||||
Data: make([]byte, ipv4MinMTU),
|
||||
}
|
||||
assertTTLExceedPacket(t, &icmpV4Packet)
|
||||
ipv6Packet := IP{
|
||||
Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
|
||||
Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"),
|
||||
Protocol: layers.IPProtocolICMPv6,
|
||||
TTL: 0,
|
||||
}
|
||||
icmpV6Packet := ICMP{
|
||||
IP: &ipv6Packet,
|
||||
Message: &icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: 25821,
|
||||
Seq: 58129,
|
||||
Data: []byte("test ttl=0"),
|
||||
},
|
||||
},
|
||||
}
|
||||
assertTTLExceedPacket(t, &icmpV6Packet)
|
||||
icmpV6Packet.Body = &icmp.Echo{
|
||||
ID: 1497,
|
||||
Seq: 39284,
|
||||
Data: make([]byte, ipv6MinMTU),
|
||||
}
|
||||
assertTTLExceedPacket(t, &icmpV6Packet)
|
||||
}
|
||||
|
||||
func assertTTLExceedPacket(t *testing.T, pk *ICMP) {
|
||||
encoder := NewEncoder()
|
||||
rawPacket, err := encoder.Encode(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
minMTU := ipv4MinMTU
|
||||
headerLen := ipv4MinHeaderLen
|
||||
routerIP := netip.MustParseAddr("172.16.0.3")
|
||||
if pk.Dst.Is6() {
|
||||
minMTU = ipv6MinMTU
|
||||
headerLen = ipv6HeaderLen
|
||||
routerIP = netip.MustParseAddr("fd51:2391:697:f4ee::3")
|
||||
}
|
||||
|
||||
ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, routerIP)
|
||||
require.Equal(t, routerIP, ttlExceedPacket.Src)
|
||||
require.Equal(t, pk.Src, ttlExceedPacket.Dst)
|
||||
require.Equal(t, pk.Protocol, ttlExceedPacket.Protocol)
|
||||
require.Equal(t, DefaultTTL, ttlExceedPacket.TTL)
|
||||
|
||||
timeExceed, ok := ttlExceedPacket.Body.(*icmp.TimeExceeded)
|
||||
require.True(t, ok)
|
||||
if len(rawPacket.Data) > minMTU {
|
||||
require.True(t, bytes.Equal(timeExceed.Data, rawPacket.Data[:minMTU-headerLen-icmpHeaderLen]))
|
||||
} else {
|
||||
require.True(t, bytes.Equal(timeExceed.Data, rawPacket.Data))
|
||||
}
|
||||
|
||||
rawTTLExceedPacket, err := encoder.Encode(ttlExceedPacket)
|
||||
require.NoError(t, err)
|
||||
if len(rawPacket.Data) > minMTU {
|
||||
require.Len(t, rawTTLExceedPacket.Data, minMTU)
|
||||
} else {
|
||||
require.Len(t, rawTTLExceedPacket.Data, headerLen+icmpHeaderLen+len(rawPacket.Data))
|
||||
require.True(t, bytes.Equal(rawPacket.Data, rawTTLExceedPacket.Data[headerLen+icmpHeaderLen:]))
|
||||
}
|
||||
}
|
126
packet/router.go
Normal file
126
packet/router.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var (
|
||||
// Source IP in documentation range to return ICMP error messages if we can't determine the IP of this machine
|
||||
icmpv4ErrFallbackSrc = netip.MustParseAddr("192.0.2.30")
|
||||
icmpv6ErrFallbackSrc = netip.MustParseAddr("2001:db8::")
|
||||
)
|
||||
|
||||
// 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
|
||||
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)
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
upstream Upstream
|
||||
returnPipe FunnelUniPipe
|
||||
icmpProxy ICMPRouter
|
||||
ipv4Src netip.Addr
|
||||
ipv6Src netip.Addr
|
||||
logger *zerolog.Logger
|
||||
}
|
||||
|
||||
func NewRouter(upstream Upstream, returnPipe FunnelUniPipe, icmpProxy ICMPRouter, logger *zerolog.Logger) *Router {
|
||||
ipv4Src, err := findLocalAddr(net.ParseIP("1.1.1.1"), 53)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msgf("Failed to determine the IPv4 for this machine. It will use %s as source IP for error messages such as ICMP TTL exceed", icmpv4ErrFallbackSrc)
|
||||
ipv4Src = icmpv4ErrFallbackSrc
|
||||
}
|
||||
ipv6Src, err := findLocalAddr(net.ParseIP("2606:4700:4700::1111"), 53)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msgf("Failed to determine the IPv6 for this machine. It will use %s as source IP for error messages such as ICMP TTL exceed", icmpv6ErrFallbackSrc)
|
||||
ipv6Src = icmpv6ErrFallbackSrc
|
||||
}
|
||||
return &Router{
|
||||
upstream: upstream,
|
||||
returnPipe: returnPipe,
|
||||
icmpProxy: icmpProxy,
|
||||
ipv4Src: ipv4Src,
|
||||
ipv6Src: ipv6Src,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) Serve(ctx context.Context) error {
|
||||
icmpDecoder := NewICMPDecoder()
|
||||
encoder := NewEncoder()
|
||||
for {
|
||||
rawPacket, err := r.upstream.ReceivePacket(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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.icmpProxy.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.ipv4Src
|
||||
} else {
|
||||
srcIP = r.ipv6Src
|
||||
}
|
||||
ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP)
|
||||
|
||||
encodedTTLExceed, err := encoder.Encode(ttlExceedPacket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.returnPipe.SendPacket(pk.Src, encodedTTLExceed)
|
||||
}
|
||||
|
||||
// findLocalAddr tries to dial UDP and returns the local address picked by the OS
|
||||
func findLocalAddr(dst net.IP, port int) (netip.Addr, error) {
|
||||
udpConn, err := net.DialUDP("udp", nil, &net.UDPAddr{
|
||||
IP: dst,
|
||||
Port: port,
|
||||
})
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
defer udpConn.Close()
|
||||
localAddrPort, err := netip.ParseAddrPort(udpConn.LocalAddr().String())
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
localAddr := localAddrPort.Addr()
|
||||
return localAddr, nil
|
||||
}
|
125
packet/router_test.go
Normal file
125
packet/router_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var (
|
||||
noopLogger = zerolog.Nop()
|
||||
)
|
||||
|
||||
func TestRouterReturnTTLExceed(t *testing.T) {
|
||||
upstream := &mockUpstream{
|
||||
source: make(chan RawPacket),
|
||||
}
|
||||
returnPipe := &mockFunnelUniPipe{
|
||||
uniPipe: make(chan RawPacket),
|
||||
}
|
||||
router := NewRouter(upstream, returnPipe, &mockICMPRouter{}, &noopLogger)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
routerStopped := make(chan struct{})
|
||||
go func() {
|
||||
router.Serve(ctx)
|
||||
close(routerStopped)
|
||||
}()
|
||||
|
||||
pk := ICMP{
|
||||
IP: &IP{
|
||||
Src: netip.MustParseAddr("192.168.1.1"),
|
||||
Dst: netip.MustParseAddr("10.0.0.1"),
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
TTL: 1,
|
||||
},
|
||||
Message: &icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: 12481,
|
||||
Seq: 8036,
|
||||
Data: []byte("TTL exceed"),
|
||||
},
|
||||
},
|
||||
}
|
||||
assertTTLExceed(t, &pk, router.ipv4Src, upstream, returnPipe)
|
||||
pk = ICMP{
|
||||
IP: &IP{
|
||||
Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
|
||||
Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"),
|
||||
Protocol: layers.IPProtocolICMPv6,
|
||||
TTL: 1,
|
||||
},
|
||||
Message: &icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: 42583,
|
||||
Seq: 7039,
|
||||
Data: []byte("TTL exceed"),
|
||||
},
|
||||
},
|
||||
}
|
||||
assertTTLExceed(t, &pk, router.ipv6Src, upstream, returnPipe)
|
||||
|
||||
cancel()
|
||||
<-routerStopped
|
||||
}
|
||||
|
||||
func assertTTLExceed(t *testing.T, originalPacket *ICMP, expectedSrc netip.Addr, upstream *mockUpstream, returnPipe *mockFunnelUniPipe) {
|
||||
encoder := NewEncoder()
|
||||
rawPacket, err := encoder.Encode(originalPacket)
|
||||
require.NoError(t, err)
|
||||
|
||||
upstream.source <- rawPacket
|
||||
resp := <-returnPipe.uniPipe
|
||||
decoder := NewICMPDecoder()
|
||||
decoded, err := decoder.Decode(resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expectedSrc, decoded.Src)
|
||||
require.Equal(t, originalPacket.Src, decoded.Dst)
|
||||
require.Equal(t, originalPacket.Protocol, decoded.Protocol)
|
||||
require.Equal(t, DefaultTTL, decoded.TTL)
|
||||
if originalPacket.Dst.Is4() {
|
||||
require.Equal(t, ipv4.ICMPTypeTimeExceeded, decoded.Type)
|
||||
} else {
|
||||
require.Equal(t, ipv6.ICMPTypeTimeExceeded, decoded.Type)
|
||||
}
|
||||
require.Equal(t, 0, decoded.Code)
|
||||
timeExceed, ok := decoded.Body.(*icmp.TimeExceeded)
|
||||
require.True(t, ok)
|
||||
require.True(t, bytes.Equal(rawPacket.Data, timeExceed.Data))
|
||||
}
|
||||
|
||||
type mockUpstream struct {
|
||||
source chan RawPacket
|
||||
}
|
||||
|
||||
func (ms *mockUpstream) ReceivePacket(ctx context.Context) (RawPacket, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return RawPacket{}, ctx.Err()
|
||||
case pk := <-ms.source:
|
||||
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")
|
||||
}
|
Reference in New Issue
Block a user