github.com/cilium/cilium@v1.16.2/pkg/hubble/dropeventemitter/dropeventemitter.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package dropeventemitter
     5  
     6  import (
     7  	"context"
     8  	"slices"
     9  	"strconv"
    10  	"strings"
    11  
    12  	v1 "k8s.io/api/core/v1"
    13  	typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
    14  	"k8s.io/client-go/tools/record"
    15  
    16  	flowpb "github.com/cilium/cilium/api/v1/flow"
    17  	"github.com/cilium/cilium/pkg/identity"
    18  	client "github.com/cilium/cilium/pkg/k8s/client"
    19  	slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    20  	metaslimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    21  	slimscheme "github.com/cilium/cilium/pkg/k8s/slim/k8s/client/clientset/versioned/scheme"
    22  	"github.com/cilium/cilium/pkg/k8s/watchers"
    23  	"github.com/cilium/cilium/pkg/time"
    24  )
    25  
    26  type DropEventEmitter struct {
    27  	reasons    []string
    28  	recorder   record.EventRecorder
    29  	k8sWatcher watchers.CacheAccessK8SWatcher
    30  }
    31  
    32  func NewDropEventEmitter(interval time.Duration, reasons []string, k8s client.Clientset, watcher watchers.CacheAccessK8SWatcher) *DropEventEmitter {
    33  	broadcaster := record.NewBroadcasterWithCorrelatorOptions(record.CorrelatorOptions{
    34  		BurstSize:            1,
    35  		QPS:                  1 / float32(interval.Seconds()),
    36  		MaxEvents:            1,
    37  		MaxIntervalInSeconds: int(interval.Seconds()),
    38  		MessageFunc:          func(event *v1.Event) string { return event.Message },
    39  	})
    40  	broadcaster.StartRecordingToSink(&typedv1.EventSinkImpl{Interface: k8s.CoreV1().Events("")})
    41  
    42  	return &DropEventEmitter{
    43  		reasons:    reasons,
    44  		recorder:   broadcaster.NewRecorder(slimscheme.Scheme, v1.EventSource{Component: "cilium"}),
    45  		k8sWatcher: watcher,
    46  	}
    47  }
    48  
    49  func (e *DropEventEmitter) ProcessFlow(ctx context.Context, flow *flowpb.Flow) error {
    50  	reason := strings.ToLower(flow.DropReasonDesc.String())
    51  
    52  	// Only handle packet drops due to policy related to a Pod
    53  	if flow.Verdict != flowpb.Verdict_DROPPED ||
    54  		!slices.Contains(e.reasons, reason) ||
    55  		(flow.TrafficDirection == flowpb.TrafficDirection_INGRESS &&
    56  			flow.Destination.PodName == "") ||
    57  		(flow.TrafficDirection == flowpb.TrafficDirection_EGRESS &&
    58  			flow.Source.PodName == "") {
    59  		return nil
    60  	}
    61  
    62  	if flow.TrafficDirection == flowpb.TrafficDirection_INGRESS {
    63  		message := "Incoming packet dropped (" + reason + ") from " +
    64  			e.endpointToString(flow.IP.Source, flow.Source) + " " +
    65  			e.l4protocolToString(flow.L4)
    66  		e.recorder.Event(&slimv1.Pod{
    67  			TypeMeta: metaslimv1.TypeMeta{
    68  				Kind:       "Pod",
    69  				APIVersion: "v1",
    70  			},
    71  			ObjectMeta: metaslimv1.ObjectMeta{
    72  				Name:      flow.Destination.PodName,
    73  				Namespace: flow.Destination.Namespace,
    74  			},
    75  		}, v1.EventTypeWarning, "PacketDrop", message)
    76  	} else {
    77  		message := "Outgoing packet dropped (" + reason + ") to " +
    78  			e.endpointToString(flow.IP.Destination, flow.Destination) + " " +
    79  			e.l4protocolToString(flow.L4)
    80  
    81  		objMeta := metaslimv1.ObjectMeta{
    82  			Name:      flow.Source.PodName,
    83  			Namespace: flow.Source.Namespace,
    84  		}
    85  		if e.k8sWatcher != nil {
    86  			pod, err := e.k8sWatcher.GetCachedPod(flow.Source.Namespace, flow.Source.PodName)
    87  			if err == nil {
    88  				objMeta.UID = pod.UID
    89  			}
    90  		}
    91  		podObj := slimv1.Pod{
    92  			TypeMeta: metaslimv1.TypeMeta{
    93  				Kind:       "Pod",
    94  				APIVersion: "v1",
    95  			},
    96  			ObjectMeta: objMeta,
    97  		}
    98  		e.recorder.Event(&podObj, v1.EventTypeWarning, "PacketDrop", message)
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  func (e *DropEventEmitter) endpointToString(ip string, endpoint *flowpb.Endpoint) string {
   105  	if endpoint.PodName != "" {
   106  		return endpoint.Namespace + "/" + endpoint.PodName + " (" + ip + ")"
   107  	} else if identity.NumericIdentity(endpoint.Identity).IsReservedIdentity() {
   108  		return identity.NumericIdentity(endpoint.Identity).String() + " (" + ip + ")"
   109  	} else {
   110  		return ip
   111  	}
   112  }
   113  
   114  func (e *DropEventEmitter) l4protocolToString(l4 *flowpb.Layer4) string {
   115  	switch l4.Protocol.(type) {
   116  	case *flowpb.Layer4_TCP:
   117  		return "TCP/" + strconv.Itoa(int(l4.GetTCP().DestinationPort))
   118  	case *flowpb.Layer4_UDP:
   119  		return "UDP/" + strconv.Itoa(int(l4.GetUDP().DestinationPort))
   120  	case *flowpb.Layer4_ICMPv4:
   121  		return "ICMPv4"
   122  	case *flowpb.Layer4_ICMPv6:
   123  		return "ICMPv6"
   124  	case *flowpb.Layer4_SCTP:
   125  		return "SCTP"
   126  	}
   127  	return ""
   128  }