x-pack/auditbeat/module/system/socket/events.go (901 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. //go:build (linux && 386) || (linux && amd64) package socket import ( "bytes" "encoding/binary" "fmt" "net" "os" "path/filepath" "strings" "syscall" "golang.org/x/sys/unix" "github.com/elastic/beats/v7/auditbeat/tracing" ) const ( // This compensates the size argument of udp_sendmsg which is only // UDP payload. 28 is the size of an IPv4 header (no options) + UDP header. minIPv4UdpPacketSize = 28 // Same for udpv6_sendmsg. // 40 is the size of an IPv6 header (no extensions) + UDP header. minIPv6UdpPacketSize = 48 ) // event is the interface that all the deserialized events from the ring-buffer // have to conform to in order to be processed by state. type event interface { fmt.Stringer Update(*state) error } type tcpIPv4ConnectCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"addr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"port"` } // String returns a representation of the event. func (e *tcpIPv4ConnectCall) String() string { var buf [4]byte tracing.MachineEndian.PutUint32(buf[:], e.LAddr) laddr := net.IPv4(buf[0], buf[1], buf[2], buf[3]) tracing.MachineEndian.PutUint16(buf[:], e.LPort) lport := binary.BigEndian.Uint16(buf[:]) tracing.MachineEndian.PutUint32(buf[:], e.RAddr) raddr := net.IPv4(buf[0], buf[1], buf[2], buf[3]) tracing.MachineEndian.PutUint16(buf[:], e.RPort) rport := binary.BigEndian.Uint16(buf[:]) return fmt.Sprintf( "%s connect(sock=0x%x, %s:%d -> %s:%d)", header(e.Meta), e.Sock, laddr.String(), lport, raddr.String(), rport) } // Update the state with the contents of this event. func (e *tcpIPv4ConnectCall) Update(s *state) error { return s.ThreadEnter(e.Meta.TID, e) } type tcpIPv6ConnectCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` LAddrA uint64 `kprobe:"laddra"` LAddrB uint64 `kprobe:"laddrb"` RAddrA uint64 `kprobe:"addra"` RAddrB uint64 `kprobe:"addrb"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"port"` } // String returns a representation of the event. func (e *tcpIPv6ConnectCall) String() string { var buf [16]byte tracing.MachineEndian.PutUint64(buf[:], e.LAddrA) tracing.MachineEndian.PutUint64(buf[8:], e.LAddrB) laddr := net.IP(buf[:]).String() tracing.MachineEndian.PutUint64(buf[:], e.RAddrA) tracing.MachineEndian.PutUint64(buf[8:], e.RAddrB) raddr := net.IP(buf[:]).String() tracing.MachineEndian.PutUint16(buf[:], e.LPort) lport := binary.BigEndian.Uint16(buf[:]) tracing.MachineEndian.PutUint16(buf[:], e.RPort) rport := binary.BigEndian.Uint16(buf[:]) return fmt.Sprintf( "%s connect6(sock=0x%x, %s:%d -> %s:%d)", header(e.Meta), e.Sock, laddr, lport, raddr, rport) } // Update the state with the contents of this event. func (e *tcpIPv6ConnectCall) Update(s *state) error { return s.ThreadEnter(e.Meta.TID, e) } type tcpConnectResult struct { Meta tracing.Metadata `kprobe:"metadata"` Retval int32 `kprobe:"retval"` } // String returns a representation of the event. func (e *tcpConnectResult) String() string { return fmt.Sprintf("%s <- connect %s", header(e.Meta), kernErrorDesc(e.Retval)) } // Update the state with the contents of this event. func (e *tcpConnectResult) Update(s *state) error { ev, found := s.ThreadLeave(e.Meta.TID) if !found || e.Retval != 0 { return nil } switch call := ev.(type) { case *tcpIPv4ConnectCall: return s.UpdateFlow(flow{ sock: call.Sock, pid: e.Meta.PID, inetType: inetTypeIPv4, proto: protoTCP, dir: directionEgress, complete: true, lastSeen: kernelTime(call.Meta.Timestamp), local: newEndpointIPv4(call.LAddr, call.LPort, 0, 0), remote: newEndpointIPv4(call.RAddr, call.RPort, 0, 0), }) case *tcpIPv6ConnectCall: return s.UpdateFlow(flow{ sock: call.Sock, pid: e.Meta.PID, inetType: inetTypeIPv6, proto: protoTCP, dir: directionEgress, complete: true, lastSeen: kernelTime(call.Meta.Timestamp), local: newEndpointIPv6(call.LAddrA, call.LAddrB, call.LPort, 0, 0), remote: newEndpointIPv6(call.RAddrA, call.RAddrB, call.RPort, 0, 0), }) } return fmt.Errorf("stored thread event has unexpected type %T", ev) } type tcpAcceptResult struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` LAddr6a uint64 `kprobe:"laddr6a"` LAddr6b uint64 `kprobe:"laddr6b"` RAddr6a uint64 `kprobe:"raddr6a"` RAddr6b uint64 `kprobe:"raddr6b"` Af uint16 `kprobe:"family"` } func (e *tcpAcceptResult) asFlow() flow { evTime := kernelTime(e.Meta.Timestamp) f := flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetType(e.Af), proto: protoTCP, dir: directionIngress, complete: true, lastSeen: evTime, created: evTime, } if e.Af == unix.AF_INET { f.local = newEndpointIPv4(e.LAddr, e.LPort, 0, 0) f.remote = newEndpointIPv4(e.RAddr, e.RPort, 0, 0) } else { f.local = newEndpointIPv6(e.LAddr6a, e.LAddr6b, e.LPort, 0, 0) f.remote = newEndpointIPv6(e.RAddr6a, e.RAddr6b, e.RPort, 0, 0) } return f } // String returns a representation of the event. func (e *tcpAcceptResult) String() string { f := e.asFlow() return fmt.Sprintf("%s <- accept(sock=0x%x, af=%s, %s <- %s)", header(e.Meta), e.Sock, inetType(e.Af), f.local.String(), f.remote.String()) } // Update the state with the contents of this event. func (e *tcpAcceptResult) Update(s *state) error { if e.Sock != 0 { return s.CreateSocket(e.asFlow()) } return nil } type tcpAcceptResult4 struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` Af uint16 `kprobe:"family"` } func (e *tcpAcceptResult4) asFlow() flow { evTime := kernelTime(e.Meta.Timestamp) f := flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetType(e.Af), proto: protoTCP, dir: directionIngress, complete: true, lastSeen: evTime, created: evTime, } f.local = newEndpointIPv4(e.LAddr, e.LPort, 0, 0) f.remote = newEndpointIPv4(e.RAddr, e.RPort, 0, 0) return f } // String returns a representation of the event. func (e *tcpAcceptResult4) String() string { f := e.asFlow() return fmt.Sprintf("%s <- accept(sock=0x%x, af=%s, %s <- %s)", header(e.Meta), e.Sock, inetType(e.Af), f.local.String(), f.remote.String()) } // Update the state with the contents of this event. func (e *tcpAcceptResult4) Update(s *state) error { if e.Sock != 0 { return s.CreateSocket(e.asFlow()) } return nil } type tcpSendMsgCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uintptr `kprobe:"size"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` LAddr6a uint64 `kprobe:"laddr6a"` LAddr6b uint64 `kprobe:"laddr6b"` RAddr6a uint64 `kprobe:"raddr6a"` RAddr6b uint64 `kprobe:"raddr6b"` Af uint16 `kprobe:"family"` } func (e *tcpSendMsgCall) asFlow() flow { f := flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetType(e.Af), proto: protoTCP, lastSeen: kernelTime(e.Meta.Timestamp), } if e.Af == unix.AF_INET { f.local = newEndpointIPv4(e.LAddr, e.LPort, 0, 0) f.remote = newEndpointIPv4(e.RAddr, e.RPort, 0, 0) } else { f.local = newEndpointIPv6(e.LAddr6a, e.LAddr6b, e.LPort, 0, 0) f.remote = newEndpointIPv6(e.RAddr6a, e.RAddr6b, e.RPort, 0, 0) } return f } // String returns a representation of the event. func (e *tcpSendMsgCall) String() string { flow := e.asFlow() return fmt.Sprintf( "%s tcp_sendmsg(sock=0x%x, size=%d, af=%s, %s -> %s)", header(e.Meta), flow.sock, e.Size, inetType(e.Af), flow.local.String(), flow.remote.String()) } // Update the state with the contents of this event. func (e *tcpSendMsgCall) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type tcpSendMsgCall4 struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uintptr `kprobe:"size"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` Af uint16 `kprobe:"family"` } func (e *tcpSendMsgCall4) asFlow() flow { f := flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetType(e.Af), proto: protoTCP, lastSeen: kernelTime(e.Meta.Timestamp), } f.local = newEndpointIPv4(e.LAddr, e.LPort, 0, 0) f.remote = newEndpointIPv4(e.RAddr, e.RPort, 0, 0) return f } // String returns a representation of the event. func (e *tcpSendMsgCall4) String() string { flow := e.asFlow() return fmt.Sprintf( "%s tcp_sendmsg(sock=0x%x, size=%d, af=%s, %s -> %s)", header(e.Meta), flow.sock, e.Size, inetType(e.Af), flow.local.String(), flow.remote.String()) } // Update the state with the contents of this event. func (e *tcpSendMsgCall4) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type ipLocalOutCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uint32 `kprobe:"size"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` } func (e *ipLocalOutCall) asFlow() flow { return flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv4, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv4(e.LAddr, e.LPort, 1, uint64(e.Size)), remote: newEndpointIPv4(e.RAddr, e.RPort, 0, 0), } } // String returns a representation of the event. func (e *ipLocalOutCall) String() string { f := e.asFlow() return fmt.Sprintf( "%s ip_local_out(sock=0x%x, size=%d, %s -> %s)", header(e.Meta), e.Sock, e.Size, f.local.String(), f.remote.String()) } func isNotUDP(f *flow) bool { return f.proto != protoUDP } // Update the state with the contents of this event. func (e *ipLocalOutCall) Update(s *state) error { flow := e.asFlow() if flow.remote.addr.IP == nil { // Unconnected-UDP flows have nil destination in here. return nil } // Only count non-UDP packets. // Those are already counted by udp_sendmsg, but there is no way // to discriminate UDP in ip_local_out at kprobe level. return s.UpdateFlowWithCondition(flow, isNotUDP) } type inet6CskXmitCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` LAddr6a uint64 `kprobe:"laddr6a"` LAddr6b uint64 `kprobe:"laddr6b"` RAddr6a uint64 `kprobe:"raddr6a"` RAddr6b uint64 `kprobe:"raddr6b"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` Size uint32 `kprobe:"size"` } func (e *inet6CskXmitCall) asFlow() flow { return flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv6, proto: protoTCP, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv6(e.LAddr6a, e.LAddr6b, e.LPort, 1, uint64(e.Size)), remote: newEndpointIPv6(e.RAddr6a, e.RAddr6b, e.RPort, 0, 0), } } // String returns a representation of the event. func (e *inet6CskXmitCall) String() string { f := e.asFlow() return fmt.Sprintf( "%s inet6_csk_xmit(sock=0x%x, size=%d, %s -> %s)", header(e.Meta), e.Sock, e.Size, f.local.String(), f.remote.String()) } // Update the state with the contents of this event. func (e *inet6CskXmitCall) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type tcpV4DoRcv struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uint32 `kprobe:"size"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` } func (e *tcpV4DoRcv) asFlow() flow { return flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv4, proto: protoTCP, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv4(e.LAddr, e.LPort, 0, 0), remote: newEndpointIPv4(e.RAddr, e.RPort, 1, uint64(e.Size)), } } // String returns a representation of the event. func (e *tcpV4DoRcv) String() string { f := e.asFlow() return fmt.Sprintf( "%s tcp_v4_do_rcv(sock=0x%x, size=%d, %s <- %s)", header(e.Meta), e.Sock, e.Size, f.local.String(), f.remote.String()) } // Update the state with the contents of this event. func (e *tcpV4DoRcv) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type tcpV6DoRcv struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` LAddr6a uint64 `kprobe:"laddr6a"` LAddr6b uint64 `kprobe:"laddr6b"` RAddr6a uint64 `kprobe:"raddr6a"` RAddr6b uint64 `kprobe:"raddr6b"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` Size uint32 `kprobe:"size"` } func (e *tcpV6DoRcv) asFlow() flow { return flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv6, proto: protoTCP, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv6(e.LAddr6a, e.LAddr6b, e.LPort, 0, 0), remote: newEndpointIPv6(e.RAddr6a, e.RAddr6b, e.RPort, 1, uint64(e.Size)), } } // String returns a representation of the event. func (e *tcpV6DoRcv) String() string { f := e.asFlow() return fmt.Sprintf( "%s tcp_v6_do_rcv(sock=0x%x, size=%d, %s <- %s)", header(e.Meta), e.Sock, e.Size, f.local.String(), f.remote.String()) } // Update the state with the contents of this event. func (e *tcpV6DoRcv) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type udpSendMsgCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uintptr `kprobe:"size"` LAddr uint32 `kprobe:"laddr"` RAddr uint32 `kprobe:"raddr"` AltRAddr uint32 `kprobe:"altraddr"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` AltRPort uint16 `kprobe:"altrport"` // SIPtr is the struct sockaddr_in pointer. SIPtr uintptr `kprobe:"siptr"` // SIAF is the address family in (struct sockaddr_in*)->sin_family. SIAF uint16 `kprobe:"siaf"` } func (e *udpSendMsgCall) asFlow() flow { raddr, rport := e.RAddr, e.RPort if e.SIPtr == 0 || e.SIAF != unix.AF_INET { raddr = e.AltRAddr rport = e.AltRPort } return flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv4, proto: protoUDP, dir: directionEgress, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv4(e.LAddr, e.LPort, 1, uint64(e.Size)+minIPv4UdpPacketSize), remote: newEndpointIPv4(raddr, rport, 0, 0), } } // String returns a representation of the event. func (e *udpSendMsgCall) String() string { flow := e.asFlow() return fmt.Sprintf( "%s udp_sendmsg(sock=0x%x, size=%d, %s -> %s)", header(e.Meta), flow.sock, e.Size, flow.local.String(), flow.remote.String()) } // Update the state with the contents of this event. func (e *udpSendMsgCall) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type udpv6SendMsgCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uintptr `kprobe:"size"` LAddrA uint64 `kprobe:"laddra"` LAddrB uint64 `kprobe:"laddrb"` RAddrA uint64 `kprobe:"raddra"` RAddrB uint64 `kprobe:"raddrb"` AltRAddrA uint64 `kprobe:"altraddra"` AltRAddrB uint64 `kprobe:"altraddrb"` LPort uint16 `kprobe:"lport"` RPort uint16 `kprobe:"rport"` AltRPort uint16 `kprobe:"altrport"` // SI6Ptr is the struct sockaddr_in6 pointer. SI6Ptr uintptr `kprobe:"si6ptr"` // Si6AF is the address family field ((struct sockaddr_in6*)->sin6_family) SI6AF uint16 `kprobe:"si6af"` } func (e *udpv6SendMsgCall) asFlow() flow { raddra, raddrb, rport := e.RAddrA, e.RAddrB, e.RPort if e.SI6Ptr == 0 || e.SI6AF != unix.AF_INET6 { raddra, raddrb = e.AltRAddrA, e.AltRAddrB rport = e.AltRPort } return flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv6, proto: protoUDP, dir: directionEgress, lastSeen: kernelTime(e.Meta.Timestamp), // In IPv6, udpv6_sendmsg increments local counters as there is no // corresponding ip6_local_out call. local: newEndpointIPv6(e.LAddrA, e.LAddrB, e.LPort, 1, uint64(e.Size)+minIPv6UdpPacketSize), remote: newEndpointIPv6(raddra, raddrb, rport, 0, 0), } } // String returns a representation of the event. func (e *udpv6SendMsgCall) String() string { flow := e.asFlow() return fmt.Sprintf( "%s udpv6_sendmsg(sock=0x%x, size=%d, %s -> %s)", header(e.Meta), flow.sock, e.Size, flow.local.String(), flow.remote.String()) } // Update the state with the contents of this event. func (e *udpv6SendMsgCall) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type udpQueueRcvSkb struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uint32 `kprobe:"size"` LAddr uint32 `kprobe:"laddr"` LPort uint16 `kprobe:"lport"` IPHdr uint16 `kprobe:"iphdr"` UDPHdr uint16 `kprobe:"udphdr"` Base uintptr `kprobe:"base"` Packet [skBuffDataDumpBytes]byte `kprobe:"packet,greedy"` } func validIPv4Headers(ipHdr uint16, udpHdr uint16, data []byte) bool { return ipHdr != 0 && int(ipHdr)+20 < len(data) && data[ipHdr]&0xF0 == 0x40 && udpHdr != 0 && int(udpHdr)+12 < len(data) } func validIPv6Headers(ipHdr uint16, udpHdr uint16, data []byte) bool { return ipHdr != 0 && int(ipHdr)+40 < len(data) && data[ipHdr]&0xF0 == 0x60 && udpHdr != 0 && int(udpHdr)+12 < len(data) } func (e *udpQueueRcvSkb) asFlow() flow { f := flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv4, proto: protoUDP, dir: directionIngress, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv4(e.LAddr, e.LPort, 0, 0), } if valid := validIPv4Headers(e.IPHdr, e.UDPHdr, e.Packet[:]); !valid { // Check if we're dealing with pointers // TODO: This should check for SK_BUFF_HAS_POINTERS. Instead is just // treating IPHdr/UDPHdr as the lower 16bits of a pointer which // is enough as the headers are never more than 64k bytes into the // packet. // This hacky solution will only work on little-endian archs // which is fine for now as only 386/amd64 is supported. // In the future a different set of kprobes must be used // when SK_BUFF_HAS_POINTERS so that IPHdr and UDPHdr are // the size of a pointer, not uint16. base := uint16(e.Base) if e.IPHdr > base && e.UDPHdr > base { ipOff := e.IPHdr - base udpOff := e.UDPHdr - base if valid = validIPv4Headers(ipOff, udpOff, e.Packet[:]); valid { e.IPHdr = ipOff e.UDPHdr = udpOff } } if !valid { return f } } var raddr uint32 var rport uint16 // the remote is this packet's source raddr = tracing.MachineEndian.Uint32(e.Packet[e.IPHdr+12:]) rport = tracing.MachineEndian.Uint16(e.Packet[e.UDPHdr:]) f.remote = newEndpointIPv4(raddr, rport, 1, uint64(e.Size)+minIPv4UdpPacketSize) return f } // String returns a representation of the event. func (e *udpQueueRcvSkb) String() string { flow := e.asFlow() return fmt.Sprintf( "%s udp_queue_rcv_skb(sock=0x%x, size=%d, %s <- %s)", header(e.Meta), flow.sock, e.Size, flow.local.String(), flow.remote.String()) } // Update the state with the contents of this event. func (e *udpQueueRcvSkb) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type udpv6QueueRcvSkb struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` Size uint32 `kprobe:"size"` LAddrA uint64 `kprobe:"laddra"` LAddrB uint64 `kprobe:"laddrb"` LPort uint16 `kprobe:"lport"` IPHdr uint16 `kprobe:"iphdr"` UDPHdr uint16 `kprobe:"udphdr"` Base uintptr `kprobe:"base"` Packet [skBuffDataDumpBytes]byte `kprobe:"packet,greedy"` } func (e *udpv6QueueRcvSkb) asFlow() flow { f := flow{ sock: e.Sock, pid: e.Meta.PID, inetType: inetTypeIPv6, proto: protoUDP, dir: directionIngress, lastSeen: kernelTime(e.Meta.Timestamp), local: newEndpointIPv6(e.LAddrA, e.LAddrB, e.LPort, 0, 0), } if valid := validIPv6Headers(e.IPHdr, e.UDPHdr, e.Packet[:]); !valid { // Check if we're dealing with pointers // TODO: This only works in little-endian, same as in udpQueueRcvSkb base := uint16(e.Base) if e.IPHdr > base && e.UDPHdr > base { ipOff := e.IPHdr - base udpOff := e.UDPHdr - base if valid = validIPv6Headers(ipOff, udpOff, e.Packet[:]); valid { e.IPHdr = ipOff e.UDPHdr = udpOff } } if !valid { return f } } var raddrA, raddrB uint64 var rport uint16 // the remote is this packet's source raddrA = tracing.MachineEndian.Uint64(e.Packet[e.IPHdr+8:]) raddrB = tracing.MachineEndian.Uint64(e.Packet[e.IPHdr+16:]) rport = tracing.MachineEndian.Uint16(e.Packet[e.UDPHdr:]) f.remote = newEndpointIPv6(raddrA, raddrB, rport, 1, uint64(e.Size)+minIPv6UdpPacketSize) return f } // String returns a representation of the event. func (e *udpv6QueueRcvSkb) String() string { flow := e.asFlow() return fmt.Sprintf( "%s udpv6_queue_rcv_skb(sock=0x%x, size=%d, %s <- %s)", header(e.Meta), flow.sock, e.Size, flow.local.String(), flow.remote.String()) } // Update the state with the contents of this event. func (e *udpv6QueueRcvSkb) Update(s *state) error { return s.UpdateFlow(e.asFlow()) } type sockInitData struct { Meta tracing.Metadata `kprobe:"metadata"` Socket uintptr `kprobe:"socket"` Sock uintptr `kprobe:"sock"` } // String returns a representation of the event. func (e *sockInitData) String() string { return fmt.Sprintf("%s sock_init_data(sock=0x%x)", header(e.Meta), e.Sock) } // Update the state with the contents of this event. func (e *sockInitData) Update(s *state) error { if ev, found := s.ThreadLeave(e.Meta.TID); found { // Only track socks created by inet_create / inet6_create if iCreate, ok := ev.(*inetCreate); ok { return s.CreateSocket(flow{ sock: e.Sock, pid: e.Meta.PID, proto: flowProto(iCreate.Proto), created: kernelTime(e.Meta.Timestamp), lastSeen: kernelTime(e.Meta.Timestamp), complete: true, }) } } return nil } type inetCreate struct { Meta tracing.Metadata `kprobe:"metadata"` Proto int32 `kprobe:"proto"` } // String returns a representation of the event. func (e *inetCreate) String() string { return fmt.Sprintf("%s inet_create(proto=%d)", header(e.Meta), e.Proto) } // Update the state with the contents of this event. func (e *inetCreate) Update(s *state) error { if proto := flowProto(e.Proto); proto == protoUnknown || proto == protoTCP || proto == protoUDP { return s.ThreadEnter(e.Meta.TID, e) } return nil } type inetReleaseCall struct { Meta tracing.Metadata `kprobe:"metadata"` Sock uintptr `kprobe:"sock"` } // String returns a representation of the event. func (e *inetReleaseCall) String() string { return fmt.Sprintf("%s inet_release(sock=0x%x)", header(e.Meta), e.Sock) } // Update the state with the contents of this event. func (e *inetReleaseCall) Update(s *state) error { return s.OnSockDestroyed(e.Sock, e.Meta.PID) } // Fetching data from execve is complicated as support for strings or arrays // in Kprobes appeared in recent kernels (~2018). To be compatible with older // kernels it needs to dump fixed-size arrays in 8-byte chunks. As the total // number of fetchargs available is limited, we have to dump only the first // 128 bytes of every argument. const ( maxProgArgLen = 128 maxProgArgs = 5 ) type execveCall struct { Meta tracing.Metadata `kprobe:"metadata"` Path [maxProgArgLen]byte `kprobe:"path,greedy"` // extra ptr is to detect if there are more than maxProcArgs arguments Ptrs [maxProgArgs + 1]uintptr `kprobe:"argptrs,greedy"` Param0 [maxProgArgLen]byte `kprobe:"param0,greedy"` Param1 [maxProgArgLen]byte `kprobe:"param1,greedy"` Param2 [maxProgArgLen]byte `kprobe:"param2,greedy"` Param3 [maxProgArgLen]byte `kprobe:"param3,greedy"` Param4 [maxProgArgLen]byte `kprobe:"param4,greedy"` // Extra user information for enrichment. creds *commitCreds } func (e *execveCall) getProcess() *process { p := &process{ pid: e.Meta.PID, created: kernelTime(e.Meta.Timestamp), } if idx := bytes.IndexByte(e.Path[:], 0); idx >= 0 { // Fast path if we already have the path. p.path = string(e.Path[:idx]) // Keep the basename in case we can't get the process name. p.name = filepath.Base(p.path) } else { // Attempt to get the path from the /prox/<pid>/exe symlink. var err error p.path, err = filepath.EvalSymlinks(fmt.Sprintf("/proc/%d/exe", e.Meta.PID)) if err != nil { if pe, ok := err.(*os.PathError); ok && strings.Contains(pe.Path, "(deleted)") { //nolint:errorlint // we're fetching the string body // Keep the deleted path from the PathError. p.path = pe.Path // Keep the basename in case we can't get the process name. p.name = filepath.Base(strings.TrimSuffix(p.path, " (deleted)")) } else { // Fallback to the truncated path. p.path = string(e.Path[:]) + " ..." // Don't trim the ellipsis to indicate this may be incorrect. p.name = filepath.Base(p.path) } } } // Check for truncation of arg list or arguments. params := [...][]byte{ e.Param0[:], e.Param1[:], e.Param2[:], e.Param3[:], e.Param4[:], } var ( argc int truncatedArg bool ) for argc = 0; argc < len(e.Ptrs); argc++ { if e.Ptrs[argc] == 0 { break } if argc < len(params) && bytes.IndexByte(params[argc], 0) < 0 { truncatedArg = true } } if argc > maxProgArgs || truncatedArg { // Attempt to get complete args list from /proc/<pid>/cmdline. cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", e.Meta.PID)) if err == nil { p.args = strings.Split(strings.TrimRight(string(cmdline), "\x00"), "\x00") } } if p.args == nil { // Fallback to arg list if unsuccessful or no truncation. p.args = make([]string, argc) if argc > maxProgArgs { argc = maxProgArgs p.args[argc] = "..." } for i, par := range params[:argc] { p.args[i] = readCString(par) } } // Carefully get the process name; we may have zero arguments. if len(p.args) != 0 { // Get name from first argument. p.name = filepath.Base(p.args[0]) } else { // Attempt to get name from /proc/<pid>/comm — only available since 2.6.33. comm, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", e.Meta.PID)) if err == nil { p.name = strings.TrimRight(string(comm), "\x00") if len(p.name) == 16 { // The name may have been truncated if it is TASK_COMM_LEN long. p.name += "..." } } else if p.name == "" { // This should never happen. p.name = "(unknown)" } } if e.creds != nil { p.hasCreds = true p.uid = e.creds.UID p.gid = e.creds.GID p.euid = e.creds.EUID p.egid = e.creds.EGID } return p } // String returns a representation of the event. func (e *execveCall) String() string { p := e.getProcess() list := make([]string, len(p.args)) for idx, val := range p.args { list[idx] = fmt.Sprintf("arg%d='%s'", idx, val) } return fmt.Sprintf("%s execve(name='%s', path='%s', %s)", header(e.Meta), p.name, p.path, strings.Join(list, " ")) } // Update the state with the contents of this event. func (e *execveCall) Update(s *state) error { return s.ThreadEnter(e.Meta.TID, e) } type execveRet struct { Meta tracing.Metadata `kprobe:"metadata"` Retval int32 `kprobe:"retval"` } // String returns a representation of the event. func (e *execveRet) String() string { return fmt.Sprintf("%s <- execve %s", header(e.Meta), kernErrorDesc(e.Retval)) } // Update the state with the contents of this event. func (e *execveRet) Update(s *state) error { if prev, found := s.ThreadLeave(e.Meta.TID); found { if call, ok := prev.(*execveCall); ok { if e.Retval >= 0 { return s.CreateProcess(call.getProcess()) } } } return nil } type forkRet struct { Meta tracing.Metadata `kprobe:"metadata"` Retval int `kprobe:"retval"` } // String returns a representation of the event. func (e *forkRet) String() string { return fmt.Sprintf("%s <- fork %d", header(e.Meta), e.Retval) } // Update the state with the contents of this event. func (e *forkRet) Update(s *state) error { if e.Retval <= 0 { return nil } return s.ForkProcess(e.Meta.PID, uint32(e.Retval), kernelTime(e.Meta.Timestamp)) } type doExit struct { Meta tracing.Metadata `kprobe:"metadata"` } // String returns a representation of the event. func (e *doExit) String() string { whatExited := "process" if e.Meta.PID != e.Meta.TID { whatExited = "thread" } return fmt.Sprintf("%s do_exit(%s)", header(e.Meta), whatExited) } // Update the state with the contents of this event. func (e *doExit) Update(s *state) (err error) { // Report exit of the main thread, // or a TID that was originally reported by doFork. if e.Meta.PID == e.Meta.TID { err = s.TerminateProcess(e.Meta.PID) } else if e.Meta.PID != e.Meta.TID && s.processExists(e.Meta.TID) { err = s.TerminateProcess(e.Meta.TID) } // Cleanup any saved thread state s.ThreadLeave(e.Meta.TID) return err } type commitCreds struct { Meta tracing.Metadata `kprobe:"metadata"` UID uint32 `kprobe:"uid"` GID uint32 `kprobe:"gid"` EUID uint32 `kprobe:"euid"` EGID uint32 `kprobe:"egid"` } // String returns a representation of the event. func (e *commitCreds) String() string { return fmt.Sprintf("%s commit_creds(uid=%d, gid=%d, euid=%d, egid=%d)", header(e.Meta), e.UID, e.GID, e.EUID, e.EGID) } // Update the state with the contents of this event. func (e *commitCreds) Update(s *state) error { if prev, found := s.ThreadLeave(e.Meta.TID); found { if call, ok := prev.(*execveCall); ok { // Only inspect commit_creds() calls that happen in the context // of an execve call. Enrich the process with user information. call.creds = e // Re-install the information after enrichment so that execveRet // can access it. return s.ThreadEnter(e.Meta.TID, call) } } return nil } type clockSyncCall struct { Meta tracing.Metadata `kprobe:"metadata"` Ts uint64 `kprobe:"timestamp"` } // String returns a representation of the event. func (e *clockSyncCall) String() string { return fmt.Sprintf("%s sys_uname[clock-sync](ts=0x%x)", header(e.Meta), e.Ts) } // Update the state with the contents of this event. func (e *clockSyncCall) Update(s *state) error { if int(e.Meta.PID) == os.Getpid() { return s.SyncClocks(e.Meta.Timestamp, e.Ts) } return nil } func header(meta tracing.Metadata) string { return fmt.Sprintf("%d probe=%d pid=%d tid=%d", meta.Timestamp, meta.EventID, meta.PID, meta.TID) } func kernErrorDesc(retval int32) string { switch { case retval < 0: errno := syscall.Errno(uintptr(0 - retval)) return fmt.Sprintf("failed errno=%d (%s)", errno, errno.Error()) case retval == 0: return "ok" default: return fmt.Sprintf("ok (value=%d)", retval) } } func readCString(buf []byte) string { if pos := bytes.IndexByte(buf, 0); pos != -1 { return string(buf[:pos]) } return string(buf) + " ..." }