tracer.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. package ebpftracer
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "github.com/cilium/ebpf"
  7. "github.com/cilium/ebpf/link"
  8. "github.com/cilium/ebpf/perf"
  9. "github.com/coroot/coroot-node-agent/common"
  10. "github.com/coroot/coroot-node-agent/proc"
  11. "golang.org/x/mod/semver"
  12. "golang.org/x/sys/unix"
  13. "inet.af/netaddr"
  14. "k8s.io/klog/v2"
  15. "os"
  16. "strconv"
  17. "strings"
  18. )
  19. type EventType uint32
  20. type EventReason uint32
  21. const (
  22. EventTypeProcessStart EventType = 1
  23. EventTypeProcessExit EventType = 2
  24. EventTypeConnectionOpen EventType = 3
  25. EventTypeConnectionClose EventType = 4
  26. EventTypeConnectionError EventType = 5
  27. EventTypeListenOpen EventType = 6
  28. EventTypeListenClose EventType = 7
  29. EventTypeFileOpen EventType = 8
  30. EventTypeTCPRetransmit EventType = 9
  31. EventReasonOOMKill EventReason = 1
  32. )
  33. type Event struct {
  34. Type EventType
  35. Reason EventReason
  36. Pid uint32
  37. SrcAddr netaddr.IPPort
  38. DstAddr netaddr.IPPort
  39. Fd uint32
  40. }
  41. type Tracer struct {
  42. collection *ebpf.Collection
  43. readers map[string]*perf.Reader
  44. links []link.Link
  45. }
  46. func NewTracer(events chan<- Event, kernelVersion string) (*Tracer, error) {
  47. t := &Tracer{readers: map[string]*perf.Reader{}}
  48. if err := t.ebpf(events, kernelVersion); err != nil {
  49. return nil, err
  50. }
  51. if err := t.init(events); err != nil {
  52. return nil, err
  53. }
  54. return t, nil
  55. }
  56. func (t *Tracer) Close() {
  57. for _, l := range t.links {
  58. l.Close()
  59. }
  60. for _, r := range t.readers {
  61. r.Close()
  62. }
  63. t.collection.Close()
  64. }
  65. func (t *Tracer) init(ch chan<- Event) error {
  66. pids, err := proc.ListPids()
  67. if err != nil {
  68. return fmt.Errorf("failed to list pids: %w", err)
  69. }
  70. for _, pid := range pids {
  71. ch <- Event{Type: EventTypeProcessStart, Pid: pid}
  72. }
  73. fds, sockets := readFds(pids)
  74. for _, fd := range fds {
  75. ch <- Event{Type: EventTypeFileOpen, Pid: fd.pid, Fd: fd.fd}
  76. }
  77. listens := map[uint64]bool{}
  78. for _, s := range sockets {
  79. if s.Listen {
  80. listens[uint64(s.pid)<<32|uint64(s.SAddr.Port())] = true
  81. }
  82. }
  83. for _, s := range sockets {
  84. typ := EventTypeConnectionOpen
  85. if s.Listen {
  86. typ = EventTypeListenOpen
  87. } else if listens[uint64(s.pid)<<32|uint64(s.SAddr.Port())] || s.DAddr.Port() > s.SAddr.Port() { // inbound
  88. continue
  89. }
  90. ch <- Event{
  91. Type: typ,
  92. Pid: s.pid,
  93. SrcAddr: s.SAddr,
  94. DstAddr: s.DAddr,
  95. }
  96. }
  97. return nil
  98. }
  99. func (t *Tracer) ebpf(ch chan<- Event, kernelVersion string) error {
  100. kv := "v" + common.KernelMajorMinor(kernelVersion)
  101. var prg []byte
  102. for _, p := range ebpfProg {
  103. if semver.Compare(kv, p.v) >= 0 {
  104. prg = p.p
  105. break
  106. }
  107. }
  108. if len(prg) == 0 {
  109. return fmt.Errorf("unsupported kernel version: %s", kernelVersion)
  110. }
  111. spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(prg))
  112. if err != nil {
  113. return fmt.Errorf("failed to load spec: %w", err)
  114. }
  115. _ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY})
  116. c, err := ebpf.NewCollection(spec)
  117. if err != nil {
  118. return fmt.Errorf("failed to load collection: %w", err)
  119. }
  120. t.collection = c
  121. events := map[string]rawEvent{
  122. "proc_events": &procEvent{},
  123. "tcp_listen_events": &tcpEvent{},
  124. "tcp_connect_events": &tcpEvent{},
  125. "tcp_retransmit_events": &tcpEvent{},
  126. "file_events": &fileEvent{},
  127. }
  128. for name, typ := range events {
  129. r, err := perf.NewReader(t.collection.Maps[name], os.Getpagesize())
  130. if err != nil {
  131. t.Close()
  132. return fmt.Errorf("failed to create ebpf reader: %w", err)
  133. }
  134. t.readers[name] = r
  135. go runEventsReader(name, r, ch, typ)
  136. }
  137. for name, spec := range spec.Programs {
  138. p := t.collection.Programs[name]
  139. var err error
  140. var l link.Link
  141. switch spec.Type {
  142. case ebpf.TracePoint:
  143. parts := strings.SplitN(spec.AttachTo, "/", 2)
  144. l, err = link.Tracepoint(parts[0], parts[1], p)
  145. case ebpf.Kprobe:
  146. l, err = link.Kprobe(spec.AttachTo, p)
  147. }
  148. if err != nil {
  149. t.Close()
  150. return fmt.Errorf("failed to link program: %w", err)
  151. }
  152. t.links = append(t.links, l)
  153. }
  154. return nil
  155. }
  156. func (t EventType) String() string {
  157. switch t {
  158. case EventTypeProcessStart:
  159. return "process-start"
  160. case EventTypeProcessExit:
  161. return "process-exit"
  162. case EventTypeConnectionOpen:
  163. return "connection-open"
  164. case EventTypeConnectionClose:
  165. return "connection-close"
  166. case EventTypeConnectionError:
  167. return "connection-error"
  168. case EventTypeListenOpen:
  169. return "listen-open"
  170. case EventTypeListenClose:
  171. return "listen-close"
  172. case EventTypeFileOpen:
  173. return "file-open"
  174. case EventTypeTCPRetransmit:
  175. return "tcp-retransmit"
  176. }
  177. return "unknown: " + strconv.Itoa(int(t))
  178. }
  179. func (t EventReason) String() string {
  180. switch t {
  181. case EventReasonOOMKill:
  182. return "oom-kill"
  183. }
  184. return "unknown: " + strconv.Itoa(int(t))
  185. }
  186. type rawEvent interface {
  187. Event() Event
  188. }
  189. type procEvent struct {
  190. Type uint32
  191. Pid uint32
  192. Reason uint32
  193. }
  194. func (e procEvent) Event() Event {
  195. return Event{Type: EventType(e.Type), Reason: EventReason(e.Reason), Pid: e.Pid}
  196. }
  197. type tcpEvent struct {
  198. Type uint32
  199. Pid uint32
  200. SPort uint16
  201. DPort uint16
  202. SAddr [16]byte
  203. DAddr [16]byte
  204. }
  205. func (e tcpEvent) Event() Event {
  206. return Event{Type: EventType(e.Type), Pid: e.Pid, SrcAddr: ipPort(e.SAddr, e.SPort), DstAddr: ipPort(e.DAddr, e.DPort)}
  207. }
  208. type fileEvent struct {
  209. Type uint32
  210. Pid uint32
  211. Fd uint32
  212. }
  213. func (e fileEvent) Event() Event {
  214. return Event{Type: EventType(e.Type), Pid: e.Pid, Fd: e.Fd}
  215. }
  216. func runEventsReader(name string, r *perf.Reader, ch chan<- Event, e rawEvent) {
  217. for {
  218. rec, err := r.Read()
  219. if err != nil {
  220. if perf.IsClosed(err) {
  221. break
  222. }
  223. continue
  224. }
  225. if rec.LostSamples > 0 {
  226. klog.Errorln(name, "lost samples:", rec.LostSamples)
  227. }
  228. if err := binary.Read(bytes.NewBuffer(rec.RawSample), binary.LittleEndian, e); err != nil {
  229. klog.Warningln("failed to read msg:", err)
  230. continue
  231. }
  232. ch <- e.Event()
  233. }
  234. }
  235. func ipPort(ip [16]byte, port uint16) netaddr.IPPort {
  236. i, _ := netaddr.FromStdIP(ip[:])
  237. return netaddr.IPPortFrom(i, port)
  238. }