github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/cmd/agent/daemon/state/events_pipeline.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "net/netip" 8 9 castpb "github.com/castai/kvisor/api/v1/runtime" 10 "github.com/castai/kvisor/cmd/agent/daemon/enrichment" 11 ebpftypes "github.com/castai/kvisor/pkg/ebpftracer/types" 12 "github.com/castai/kvisor/pkg/metrics" 13 "github.com/cespare/xxhash/v2" 14 "github.com/elastic/go-freelru" 15 ) 16 17 func (c *Controller) runEventsPipeline(ctx context.Context) error { 18 c.log.Info("running events pipeline") 19 defer c.log.Info("events pipeline done") 20 21 for { 22 select { 23 case <-ctx.Done(): 24 return ctx.Err() 25 case e := <-c.tracer.Events(): 26 pbEvent := c.toProtoEvent(e) 27 if c.enrichmentService.Enqueue(&enrichment.EnrichRequest{ 28 Event: pbEvent, 29 EbpfEvent: e, 30 }) { 31 continue 32 } 33 for _, exporter := range c.exporters.Events { 34 exporter.Enqueue(pbEvent) 35 } 36 case e := <-c.signatureEngine.Events(): 37 for _, exporter := range c.exporters.Events { 38 exporter.Enqueue(e) 39 } 40 case e := <-c.enrichmentService.Events(): 41 for _, exporter := range c.exporters.Events { 42 exporter.Enqueue(e) 43 } 44 } 45 } 46 } 47 48 func (c *Controller) toProtoEvent(e *ebpftypes.Event) *castpb.Event { 49 event := &castpb.Event{ 50 EventType: 0, 51 Timestamp: e.Context.Ts, 52 ProcessName: string(bytes.TrimRight(e.Context.Comm[:], "\x00")), 53 Namespace: e.Container.PodNamespace, 54 PodUid: e.Container.PodUID, 55 PodName: e.Container.PodName, 56 ContainerName: e.Container.Name, 57 ContainerId: e.Container.ID, 58 CgroupId: e.Container.CgroupID, 59 HostPid: e.Context.HostPid, 60 } 61 62 if podInfo, found := c.getPodInfo(event.PodUid); found { 63 event.WorkloadKind = podInfo.WorkloadKind 64 event.WorkloadName = podInfo.WorkloadName 65 event.WorkloadUid = podInfo.WorkloadKind 66 } 67 68 switch args := e.Args.(type) { 69 case ebpftypes.NetPacketDNSBaseArgs: 70 metrics.AgentDNSPacketsTotal.Inc() 71 event.EventType = castpb.EventType_EVENT_DNS 72 73 dnsEvent := args.Payload 74 dnsEvent.FlowDirection = convertFlowDirection(e.Context.GetFlowDirection()) 75 event.Data = &castpb.Event_Dns{ 76 Dns: dnsEvent, 77 } 78 79 // Add dns cache. 80 c.cacheDNS(event, dnsEvent) 81 82 case ebpftypes.SockSetStateArgs: 83 tpl := args.Tuple 84 event.EventType = findTCPEventType(ebpftypes.TCPSocketState(args.OldState), ebpftypes.TCPSocketState(args.NewState)) 85 pbTuple := &castpb.Tuple{ 86 SrcIp: tpl.Src.Addr().String(), 87 DstIp: tpl.Dst.Addr().String(), 88 SrcPort: uint32(tpl.Src.Port()), 89 DstPort: uint32(tpl.Dst.Port()), 90 DnsQuestion: c.getAddrDnsQuestion(e.Context.CgroupID, tpl.Dst.Addr()), 91 } 92 event.Data = &castpb.Event_Tuple{ 93 Tuple: pbTuple, 94 } 95 case ebpftypes.SchedProcessExecArgs: 96 event.EventType = castpb.EventType_EVENT_EXEC 97 event.Data = &castpb.Event_Exec{ 98 Exec: &castpb.Exec{ 99 Path: args.Pathname, 100 Args: args.Argv, 101 }, 102 } 103 case ebpftypes.FileModificationArgs: 104 event.EventType = castpb.EventType_EVENT_FILE_CHANGE 105 event.Data = &castpb.Event_File{ 106 File: &castpb.File{ 107 Path: args.FilePath, 108 }, 109 } 110 case ebpftypes.ProcessOomKilledArgs: 111 event.EventType = castpb.EventType_EVENT_PROCESS_OOM 112 // Nothing to add, sinces we do not have a payload 113 114 case ebpftypes.TestEventArgs: 115 // nothing to do here, as this event is solely used by testing 116 117 case ebpftypes.MagicWriteArgs: 118 event.EventType = castpb.EventType_EVENT_MAGIC_WRITE 119 event.Data = &castpb.Event_File{ 120 File: &castpb.File{ 121 Path: args.Pathname, 122 }, 123 } 124 default: 125 data, _ := json.Marshal(args) //nolint:errchkjson 126 event.EventType = castpb.EventType_EVENT_ANY 127 event.Data = &castpb.Event_Any{ 128 Any: &castpb.Any{ 129 EventId: uint32(e.Context.EventID), 130 Syscall: uint32(e.Context.Syscall), 131 Data: data, 132 }, 133 } 134 } 135 return event 136 } 137 138 func (c *Controller) getAddrDnsQuestion(cgroupID uint64, addr netip.Addr) string { 139 if cache, found := c.dnsCache.Get(cgroupID); found { 140 if dnsQuestion, found := cache.Get(addr.Unmap()); found { 141 return dnsQuestion 142 } 143 } 144 return "" 145 } 146 147 func (c *Controller) cacheDNS(e *castpb.Event, dnsEvent *ebpftypes.ProtoDNS) { 148 cacheVal, found := c.dnsCache.Get(e.CgroupId) 149 if !found { 150 var err error 151 cacheVal, err = freelru.NewSynced[netip.Addr, string](1024, func(k netip.Addr) uint32 { 152 return uint32(xxhash.Sum64(k.AsSlice())) 153 }) 154 if err != nil { 155 c.log.Errorf("creating dns cache: %v", err) 156 return 157 } 158 c.dnsCache.Add(e.CgroupId, cacheVal) 159 } 160 161 for _, answ := range dnsEvent.Answers { 162 if len(answ.Ip) == 0 { 163 continue 164 } 165 addr, ok := netip.AddrFromSlice(answ.Ip) 166 if !ok { 167 continue 168 } 169 170 cacheVal.Add(addr, answ.Name) 171 } 172 } 173 174 func convertFlowDirection(flowDir ebpftypes.FlowDirection) castpb.FlowDirection { 175 switch flowDir { 176 case ebpftypes.FlowDirectionIngress: 177 return castpb.FlowDirection_FLOW_INGRESS 178 case ebpftypes.FlowDirectionEgress: 179 return castpb.FlowDirection_FLOW_EGRESS 180 default: 181 return castpb.FlowDirection_FLOW_UNKNOWN 182 } 183 } 184 185 func findTCPEventType(oldState, newState ebpftypes.TCPSocketState) castpb.EventType { 186 if oldState == ebpftypes.TCP_STATE_CLOSE && newState == ebpftypes.TCP_STATE_LISTEN { 187 return castpb.EventType_EVENT_TCP_LISTEN 188 } 189 190 if oldState == ebpftypes.TCP_STATE_SYN_SENT { 191 if newState == ebpftypes.TCP_STATE_ESTABLISHED { 192 return castpb.EventType_EVENT_TCP_CONNECT 193 } 194 if newState == ebpftypes.TCP_STATE_CLOSE { 195 return castpb.EventType_EVENT_TCP_CONNECT_ERROR 196 } 197 } 198 199 return castpb.EventType_UNKNOWN 200 }