cloudflared/ingress/origin_icmp_proxy.go
cthuang 495f9fb8bd 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
2022-10-17 19:48:35 +01:00

105 lines
2.7 KiB
Go

package ingress
import (
"context"
"fmt"
"net/netip"
"time"
"github.com/rs/zerolog"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"github.com/cloudflare/cloudflared/packet"
)
const (
// funnelIdleTimeout controls how long to wait to close a funnel without send/return
funnelIdleTimeout = time.Second * 10
mtu = 1500
// icmpRequestTimeoutMs controls how long to wait for a reply
icmpRequestTimeoutMs = 1000
)
var (
errPacketNil = fmt.Errorf("packet is nil")
)
type icmpRouter struct {
ipv4Proxy *icmpProxy
ipv6Proxy *icmpProxy
}
// NewICMPRouter doesn't return an error if either ipv4 proxy or ipv6 proxy can be created. The machine might only
// support one of them
func NewICMPRouter(ipv4Addr, ipv6Addr netip.Addr, ipv6Zone string, logger *zerolog.Logger) (*icmpRouter, error) {
ipv4Proxy, ipv4Err := newICMPProxy(ipv4Addr, "", logger, funnelIdleTimeout)
ipv6Proxy, ipv6Err := newICMPProxy(ipv6Addr, ipv6Zone, logger, funnelIdleTimeout)
if ipv4Err != nil && ipv6Err != nil {
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 {
logger.Debug().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created")
ipv4Proxy = nil
}
if ipv6Err != nil {
logger.Debug().Err(ipv6Err).Msg("failed to create ICMPv6 proxy, only ICMPv4 proxy is created")
ipv6Proxy = nil
}
return &icmpRouter{
ipv4Proxy: ipv4Proxy,
ipv6Proxy: ipv6Proxy,
}, nil
}
func (ir *icmpRouter) Serve(ctx context.Context) error {
if ir.ipv4Proxy != nil && ir.ipv6Proxy != nil {
errC := make(chan error, 2)
go func() {
errC <- ir.ipv4Proxy.Serve(ctx)
}()
go func() {
errC <- ir.ipv6Proxy.Serve(ctx)
}()
return <-errC
}
if ir.ipv4Proxy != nil {
return ir.ipv4Proxy.Serve(ctx)
}
if ir.ipv6Proxy != nil {
return ir.ipv6Proxy.Serve(ctx)
}
return fmt.Errorf("ICMPv4 proxy and ICMPv6 proxy are both nil")
}
func (ir *icmpRouter) Request(ctx context.Context, pk *packet.ICMP, responder *packetResponder) error {
if pk == nil {
return errPacketNil
}
if pk.Dst.Is4() {
if ir.ipv4Proxy != nil {
return ir.ipv4Proxy.Request(ctx, pk, responder)
}
return fmt.Errorf("ICMPv4 proxy was not instantiated")
}
if ir.ipv6Proxy != nil {
return ir.ipv6Proxy.Request(ctx, pk, responder)
}
return fmt.Errorf("ICMPv6 proxy was not instantiated")
}
func getICMPEcho(msg *icmp.Message) (*icmp.Echo, error) {
echo, ok := msg.Body.(*icmp.Echo)
if !ok {
return nil, fmt.Errorf("expect ICMP echo, got %s", msg.Type)
}
return echo, nil
}
func isEchoReply(msg *icmp.Message) bool {
return msg.Type == ipv4.ICMPTypeEchoReply || msg.Type == ipv6.ICMPTypeEchoReply
}