origin_icmp_proxy.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package ingress
  2. import (
  3. "context"
  4. "fmt"
  5. "net/netip"
  6. "time"
  7. "github.com/rs/zerolog"
  8. "go.opentelemetry.io/otel/attribute"
  9. "go.opentelemetry.io/otel/trace"
  10. "golang.org/x/net/icmp"
  11. "golang.org/x/net/ipv4"
  12. "golang.org/x/net/ipv6"
  13. "github.com/cloudflare/cloudflared/packet"
  14. "github.com/cloudflare/cloudflared/tracing"
  15. )
  16. const (
  17. mtu = 1500
  18. // icmpRequestTimeoutMs controls how long to wait for a reply
  19. icmpRequestTimeoutMs = 1000
  20. )
  21. var (
  22. errPacketNil = fmt.Errorf("packet is nil")
  23. )
  24. // ICMPRouterServer is a parent interface over-top of ICMPRouter that allows for the operation of the proxy origin listeners.
  25. type ICMPRouterServer interface {
  26. ICMPRouter
  27. // Serve runs the ICMPRouter proxy origin listeners for any of the IPv4 or IPv6 interfaces configured.
  28. Serve(ctx context.Context) error
  29. }
  30. // ICMPRouter manages out-going ICMP requests towards the origin.
  31. type ICMPRouter interface {
  32. // Request will send an ICMP packet towards the origin with an ICMPResponder to attach to the ICMP flow for the
  33. // response to utilize.
  34. Request(ctx context.Context, pk *packet.ICMP, responder ICMPResponder) error
  35. // ConvertToTTLExceeded will take an ICMP packet and create a ICMP TTL Exceeded response origininating from the
  36. // ICMPRouter's IP interface.
  37. ConvertToTTLExceeded(pk *packet.ICMP, rawPacket packet.RawPacket) *packet.ICMP
  38. }
  39. // ICMPResponder manages how to handle incoming ICMP messages coming from the origin to the edge.
  40. type ICMPResponder interface {
  41. ConnectionID() uint8
  42. ReturnPacket(pk *packet.ICMP) error
  43. AddTraceContext(tracedCtx *tracing.TracedContext, serializedIdentity []byte)
  44. RequestSpan(ctx context.Context, pk *packet.ICMP) (context.Context, trace.Span)
  45. ReplySpan(ctx context.Context, logger *zerolog.Logger) (context.Context, trace.Span)
  46. ExportSpan()
  47. }
  48. type icmpRouter struct {
  49. ipv4Proxy *icmpProxy
  50. ipv4Src netip.Addr
  51. ipv6Proxy *icmpProxy
  52. ipv6Src netip.Addr
  53. }
  54. // NewICMPRouter doesn't return an error if either ipv4 proxy or ipv6 proxy can be created. The machine might only
  55. // support one of them.
  56. // funnelIdleTimeout controls how long to wait to close a funnel without send/return
  57. func NewICMPRouter(ipv4Addr, ipv6Addr netip.Addr, logger *zerolog.Logger, funnelIdleTimeout time.Duration) (ICMPRouterServer, error) {
  58. ipv4Proxy, ipv4Err := newICMPProxy(ipv4Addr, logger, funnelIdleTimeout)
  59. ipv6Proxy, ipv6Err := newICMPProxy(ipv6Addr, logger, funnelIdleTimeout)
  60. if ipv4Err != nil && ipv6Err != nil {
  61. err := fmt.Errorf("cannot create ICMPv4 proxy: %v nor ICMPv6 proxy: %v", ipv4Err, ipv6Err)
  62. logger.Debug().Err(err).Msg("ICMP proxy feature is disabled")
  63. return nil, err
  64. }
  65. if ipv4Err != nil {
  66. logger.Debug().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created")
  67. ipv4Proxy = nil
  68. }
  69. if ipv6Err != nil {
  70. logger.Debug().Err(ipv6Err).Msg("failed to create ICMPv6 proxy, only ICMPv4 proxy is created")
  71. ipv6Proxy = nil
  72. }
  73. return &icmpRouter{
  74. ipv4Proxy: ipv4Proxy,
  75. ipv4Src: ipv4Addr,
  76. ipv6Proxy: ipv6Proxy,
  77. ipv6Src: ipv6Addr,
  78. }, nil
  79. }
  80. func (ir *icmpRouter) Serve(ctx context.Context) error {
  81. if ir.ipv4Proxy != nil && ir.ipv6Proxy != nil {
  82. errC := make(chan error, 2)
  83. go func() {
  84. errC <- ir.ipv4Proxy.Serve(ctx)
  85. }()
  86. go func() {
  87. errC <- ir.ipv6Proxy.Serve(ctx)
  88. }()
  89. return <-errC
  90. }
  91. if ir.ipv4Proxy != nil {
  92. return ir.ipv4Proxy.Serve(ctx)
  93. }
  94. if ir.ipv6Proxy != nil {
  95. return ir.ipv6Proxy.Serve(ctx)
  96. }
  97. return fmt.Errorf("ICMPv4 proxy and ICMPv6 proxy are both nil")
  98. }
  99. func (ir *icmpRouter) Request(ctx context.Context, pk *packet.ICMP, responder ICMPResponder) error {
  100. if pk == nil {
  101. return errPacketNil
  102. }
  103. if pk.Dst.Is4() {
  104. if ir.ipv4Proxy != nil {
  105. return ir.ipv4Proxy.Request(ctx, pk, responder)
  106. }
  107. return fmt.Errorf("ICMPv4 proxy was not instantiated")
  108. }
  109. if ir.ipv6Proxy != nil {
  110. return ir.ipv6Proxy.Request(ctx, pk, responder)
  111. }
  112. return fmt.Errorf("ICMPv6 proxy was not instantiated")
  113. }
  114. func (ir *icmpRouter) ConvertToTTLExceeded(pk *packet.ICMP, rawPacket packet.RawPacket) *packet.ICMP {
  115. var srcIP netip.Addr
  116. if pk.Dst.Is4() {
  117. srcIP = ir.ipv4Src
  118. } else {
  119. srcIP = ir.ipv6Src
  120. }
  121. return packet.NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP)
  122. }
  123. func getICMPEcho(msg *icmp.Message) (*icmp.Echo, error) {
  124. echo, ok := msg.Body.(*icmp.Echo)
  125. if !ok {
  126. return nil, fmt.Errorf("expect ICMP echo, got %s", msg.Type)
  127. }
  128. return echo, nil
  129. }
  130. func isEchoReply(msg *icmp.Message) bool {
  131. return msg.Type == ipv4.ICMPTypeEchoReply || msg.Type == ipv6.ICMPTypeEchoReply
  132. }
  133. func observeICMPRequest(logger *zerolog.Logger, span trace.Span, src string, dst string, echoID int, seq int) {
  134. incrementICMPRequest()
  135. logger.Debug().
  136. Str("src", src).
  137. Str("dst", dst).
  138. Int("originalEchoID", echoID).
  139. Int("originalEchoSeq", seq).
  140. Msg("Received ICMP request")
  141. span.SetAttributes(
  142. attribute.Int("originalEchoID", echoID),
  143. attribute.Int("seq", seq),
  144. )
  145. }
  146. func observeICMPReply(logger *zerolog.Logger, span trace.Span, dst string, echoID int, seq int) {
  147. incrementICMPReply()
  148. logger.Debug().Str("dst", dst).Int("echoID", echoID).Int("seq", seq).Msg("Sent ICMP reply to edge")
  149. span.SetAttributes(
  150. attribute.String("dst", dst),
  151. attribute.Int("echoID", echoID),
  152. attribute.Int("seq", seq),
  153. )
  154. }