2022-10-13 10:01:25 +00:00
|
|
|
package ingress
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/rs/zerolog"
|
2022-10-13 10:01:25 +00:00
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
2022-10-13 10:01:25 +00:00
|
|
|
|
|
|
|
"github.com/cloudflare/cloudflared/packet"
|
|
|
|
quicpogs "github.com/cloudflare/cloudflared/quic"
|
2022-10-13 10:01:25 +00:00
|
|
|
"github.com/cloudflare/cloudflared/tracing"
|
2022-10-13 10:01:25 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Upstream of raw packets
|
|
|
|
type muxer interface {
|
|
|
|
SendPacket(pk quicpogs.Packet) error
|
|
|
|
// ReceivePacket waits for the next raw packet from upstream
|
|
|
|
ReceivePacket(ctx context.Context) (quicpogs.Packet, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PacketRouter routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets
|
|
|
|
type PacketRouter struct {
|
2024-11-19 20:44:24 +00:00
|
|
|
icmpRouter ICMPRouter
|
|
|
|
muxer muxer
|
|
|
|
connID uint8
|
|
|
|
logger *zerolog.Logger
|
|
|
|
encoder *packet.Encoder
|
|
|
|
decoder *packet.ICMPDecoder
|
2022-10-13 10:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewPacketRouter creates a PacketRouter that handles ICMP packets. Packets are read from muxer but dropped if globalConfig is nil.
|
2024-11-19 20:44:24 +00:00
|
|
|
func NewPacketRouter(icmpRouter ICMPRouter, muxer muxer, connIndex uint8, logger *zerolog.Logger) *PacketRouter {
|
2022-10-13 10:01:25 +00:00
|
|
|
return &PacketRouter{
|
2024-11-19 20:44:24 +00:00
|
|
|
icmpRouter: icmpRouter,
|
|
|
|
muxer: muxer,
|
|
|
|
connID: connIndex,
|
|
|
|
logger: logger,
|
|
|
|
encoder: packet.NewEncoder(),
|
|
|
|
decoder: packet.NewICMPDecoder(),
|
2022-10-13 10:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *PacketRouter) Serve(ctx context.Context) error {
|
|
|
|
for {
|
|
|
|
rawPacket, responder, err := r.nextPacket(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.handlePacket(ctx, rawPacket, responder)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (r *PacketRouter) nextPacket(ctx context.Context) (packet.RawPacket, ICMPResponder, error) {
|
2022-10-13 10:01:25 +00:00
|
|
|
pk, err := r.muxer.ReceivePacket(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return packet.RawPacket{}, nil, err
|
|
|
|
}
|
2024-11-19 20:44:24 +00:00
|
|
|
responder := newPacketResponder(r.muxer, r.connID, packet.NewEncoder())
|
|
|
|
|
2022-10-13 10:01:25 +00:00
|
|
|
switch pk.Type() {
|
|
|
|
case quicpogs.DatagramTypeIP:
|
|
|
|
return packet.RawPacket{Data: pk.Payload()}, responder, nil
|
|
|
|
case quicpogs.DatagramTypeIPWithTrace:
|
2022-10-13 10:01:25 +00:00
|
|
|
var identity tracing.Identity
|
|
|
|
if err := identity.UnmarshalBinary(pk.Metadata()); err != nil {
|
|
|
|
r.logger.Err(err).Bytes("tracingIdentity", pk.Metadata()).Msg("Failed to unmarshal tracing identity")
|
|
|
|
} else {
|
2024-11-19 20:44:24 +00:00
|
|
|
tracedCtx := tracing.NewTracedContext(ctx, identity.String(), r.logger)
|
|
|
|
responder.AddTraceContext(tracedCtx, pk.Metadata())
|
2022-10-13 10:01:25 +00:00
|
|
|
}
|
|
|
|
return packet.RawPacket{Data: pk.Payload()}, responder, nil
|
2022-10-13 10:01:25 +00:00
|
|
|
default:
|
|
|
|
return packet.RawPacket{}, nil, fmt.Errorf("unexpected datagram type %d", pk.Type())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (r *PacketRouter) handlePacket(ctx context.Context, rawPacket packet.RawPacket, responder ICMPResponder) {
|
2022-10-13 10:01:25 +00:00
|
|
|
// ICMP Proxy feature is disabled, drop packets
|
2024-11-19 20:44:24 +00:00
|
|
|
if r.icmpRouter == nil {
|
2022-10-13 10:01:25 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
icmpPacket, err := r.decoder.Decode(rawPacket)
|
2022-10-13 10:01:25 +00:00
|
|
|
if err != nil {
|
|
|
|
r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if icmpPacket.TTL <= 1 {
|
2024-11-19 20:44:24 +00:00
|
|
|
if err := r.sendTTLExceedMsg(icmpPacket, rawPacket); err != nil {
|
2022-10-13 10:01:25 +00:00
|
|
|
r.logger.Err(err).Msg("Failed to return ICMP TTL exceed error")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
icmpPacket.TTL--
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
if err := r.icmpRouter.Request(ctx, icmpPacket, responder); err != nil {
|
2022-10-13 10:01:25 +00:00
|
|
|
r.logger.Err(err).
|
|
|
|
Str("src", icmpPacket.Src.String()).
|
|
|
|
Str("dst", icmpPacket.Dst.String()).
|
|
|
|
Interface("type", icmpPacket.Type).
|
|
|
|
Msg("Failed to send ICMP packet")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (r *PacketRouter) sendTTLExceedMsg(pk *packet.ICMP, rawPacket packet.RawPacket) error {
|
|
|
|
icmpTTLPacket := r.icmpRouter.ConvertToTTLExceeded(pk, rawPacket)
|
|
|
|
encodedTTLExceed, err := r.encoder.Encode(icmpTTLPacket)
|
2022-10-13 10:01:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return r.muxer.SendPacket(quicpogs.RawPacket(encodedTTLExceed))
|
|
|
|
}
|
|
|
|
|
2022-10-14 13:44:17 +00:00
|
|
|
// packetResponder should not be used concurrently. This assumption is upheld because reply packets are ready one-by-one
|
2022-10-13 10:01:25 +00:00
|
|
|
type packetResponder struct {
|
2022-10-13 10:01:25 +00:00
|
|
|
datagramMuxer muxer
|
2024-11-19 20:44:24 +00:00
|
|
|
connID uint8
|
|
|
|
encoder *packet.Encoder
|
2022-10-13 10:01:25 +00:00
|
|
|
tracedCtx *tracing.TracedContext
|
|
|
|
serializedIdentity []byte
|
2022-10-14 13:44:17 +00:00
|
|
|
// hadReply tracks if there has been any reply for this flow
|
|
|
|
hadReply bool
|
2022-10-13 10:01:25 +00:00
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func newPacketResponder(datagramMuxer muxer, connID uint8, encoder *packet.Encoder) ICMPResponder {
|
|
|
|
return &packetResponder{
|
|
|
|
datagramMuxer: datagramMuxer,
|
|
|
|
connID: connID,
|
|
|
|
encoder: encoder,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-13 10:01:25 +00:00
|
|
|
func (pr *packetResponder) tracingEnabled() bool {
|
|
|
|
return pr.tracedCtx != nil
|
2022-10-13 10:01:25 +00:00
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (pr *packetResponder) ConnectionID() uint8 {
|
|
|
|
return pr.connID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pr *packetResponder) ReturnPacket(pk *packet.ICMP) error {
|
|
|
|
rawPacket, err := pr.encoder.Encode(pk)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-10-14 13:44:17 +00:00
|
|
|
pr.hadReply = true
|
2022-10-13 10:01:25 +00:00
|
|
|
return pr.datagramMuxer.SendPacket(quicpogs.RawPacket(rawPacket))
|
|
|
|
}
|
2022-10-13 10:01:25 +00:00
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (pr *packetResponder) AddTraceContext(tracedCtx *tracing.TracedContext, serializedIdentity []byte) {
|
|
|
|
pr.tracedCtx = tracedCtx
|
|
|
|
pr.serializedIdentity = serializedIdentity
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pr *packetResponder) RequestSpan(ctx context.Context, pk *packet.ICMP) (context.Context, trace.Span) {
|
2022-10-13 10:01:25 +00:00
|
|
|
if !pr.tracingEnabled() {
|
|
|
|
return ctx, tracing.NewNoopSpan()
|
|
|
|
}
|
|
|
|
return pr.tracedCtx.Tracer().Start(pr.tracedCtx, "icmp-echo-request", trace.WithAttributes(
|
|
|
|
attribute.String("src", pk.Src.String()),
|
|
|
|
attribute.String("dst", pk.Dst.String()),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (pr *packetResponder) ReplySpan(ctx context.Context, logger *zerolog.Logger) (context.Context, trace.Span) {
|
2022-10-14 13:44:17 +00:00
|
|
|
if !pr.tracingEnabled() || pr.hadReply {
|
|
|
|
return ctx, tracing.NewNoopSpan()
|
|
|
|
}
|
|
|
|
return pr.tracedCtx.Tracer().Start(pr.tracedCtx, "icmp-echo-reply")
|
|
|
|
}
|
|
|
|
|
2024-11-19 20:44:24 +00:00
|
|
|
func (pr *packetResponder) ExportSpan() {
|
2022-10-13 10:01:25 +00:00
|
|
|
if !pr.tracingEnabled() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
spans := pr.tracedCtx.GetProtoSpans()
|
|
|
|
if len(spans) > 0 {
|
|
|
|
pr.datagramMuxer.SendPacket(&quicpogs.TracingSpanPacket{
|
|
|
|
Spans: spans,
|
|
|
|
TracingIdentity: pr.serializedIdentity,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|