diff --git a/ebpftracer/parsing.go b/ebpftracer/parsing.go new file mode 100644 index 00000000..fa1d3a1c --- /dev/null +++ b/ebpftracer/parsing.go @@ -0,0 +1,85 @@ +package ebpftracer + +import ( + "encoding/binary" + "fmt" +) + +func parseL7Event(data []byte, v *l7Event) error { + if len(data) < 56 { + return fmt.Errorf("buffer too short for l7Event: %d < 56", len(data)) + } + v.Fd = binary.LittleEndian.Uint64(data[0:8]) + v.ConnectionTimestamp = binary.LittleEndian.Uint64(data[8:16]) + v.Pid = binary.LittleEndian.Uint32(data[16:20]) + v.Status = int32(binary.LittleEndian.Uint32(data[20:24])) + v.Duration = binary.LittleEndian.Uint64(data[24:32]) + v.Protocol = data[32] + v.Method = data[33] + // Padding 2 bytes at 34-36 + v.StatementId = binary.LittleEndian.Uint32(data[36:40]) + v.PayloadSize = binary.LittleEndian.Uint64(data[40:48]) + v.ResponseSize = binary.LittleEndian.Uint64(data[48:56]) + + offset := 56 + pSize := int(v.PayloadSize) + if pSize > len(v.Payload) { + pSize = len(v.Payload) + } + if len(data) >= offset+pSize { + copy(v.Payload[:pSize], data[offset:offset+pSize]) + } + + offset += 4096 + rSize := int(v.ResponseSize) + if rSize > len(v.Response) { + rSize = len(v.Response) + } + if len(data) >= offset+rSize { + copy(v.Response[:rSize], data[offset:offset+rSize]) + } + return nil +} + +func parseTcpEvent(data []byte, v *tcpEvent) error { + if len(data) < 102 { + return fmt.Errorf("buffer too short for tcpEvent: %d < 102", len(data)) + } + v.Fd = binary.LittleEndian.Uint64(data[0:8]) + v.Timestamp = binary.LittleEndian.Uint64(data[8:16]) + v.Duration = binary.LittleEndian.Uint64(data[16:24]) + v.Type = EventType(binary.LittleEndian.Uint32(data[24:28])) + v.Pid = binary.LittleEndian.Uint32(data[28:32]) + v.BytesSent = binary.LittleEndian.Uint64(data[32:40]) + v.BytesReceived = binary.LittleEndian.Uint64(data[40:48]) + v.SPort = binary.LittleEndian.Uint16(data[48:50]) + v.DPort = binary.LittleEndian.Uint16(data[50:52]) + v.Aport = binary.LittleEndian.Uint16(data[52:54]) + + copy(v.SAddr[:], data[54:70]) + copy(v.DAddr[:], data[70:86]) + copy(v.AAddr[:], data[86:102]) + return nil +} + +func parseFileEvent(data []byte, v *fileEvent) error { + if len(data) < 32 { + return fmt.Errorf("buffer too short for fileEvent: %d < 32", len(data)) + } + v.Type = EventType(binary.LittleEndian.Uint32(data[0:4])) + v.Pid = binary.LittleEndian.Uint32(data[4:8]) + v.Fd = binary.LittleEndian.Uint64(data[8:16]) + v.Mnt = binary.LittleEndian.Uint64(data[16:24]) + v.Log = binary.LittleEndian.Uint64(data[24:32]) + return nil +} + +func parseProcEvent(data []byte, v *procEvent) error { + if len(data) < 12 { + return fmt.Errorf("buffer too short for procEvent: %d < 12", len(data)) + } + v.Type = EventType(binary.LittleEndian.Uint32(data[0:4])) + v.Pid = binary.LittleEndian.Uint32(data[4:8]) + v.Reason = binary.LittleEndian.Uint32(data[8:12]) + return nil +} diff --git a/ebpftracer/parsing_test.go b/ebpftracer/parsing_test.go new file mode 100644 index 00000000..cd8c7e1b --- /dev/null +++ b/ebpftracer/parsing_test.go @@ -0,0 +1,152 @@ +package ebpftracer + +import ( + "bytes" + "encoding/binary" + "testing" +) + +// Test to verify correctness of manual parsing vs binary.Read +func TestL7EventManualCorrectness(t *testing.T) { + original := &l7Event{ + Fd: 123, + ConnectionTimestamp: 456, + Pid: 789, + Status: 200, + Duration: 1000, + Protocol: 1, + Method: 2, + StatementId: 333, + PayloadSize: 10, + ResponseSize: 20, + } + copy(original.Payload[:], []byte("request-payload")) + copy(original.Response[:], []byte("response-payload")) + + buf := new(bytes.Buffer) + if err := binary.Write(buf, binary.LittleEndian, original); err != nil { + t.Fatal(err) + } + data := buf.Bytes() + + parsed := &l7Event{} + if err := parseL7Event(data, parsed); err != nil { + t.Fatal(err) + } + + if parsed.Fd != original.Fd { + t.Errorf("Fd mismatch: %v != %v", parsed.Fd, original.Fd) + } + if parsed.Pid != original.Pid { + t.Errorf("Pid mismatch: %v != %v", parsed.Pid, original.Pid) + } + if parsed.Status != original.Status { + t.Errorf("Status mismatch: %v != %v", parsed.Status, original.Status) + } + if !bytes.Equal(parsed.Payload[:original.PayloadSize], original.Payload[:original.PayloadSize]) { + t.Error("Payload mismatch") + } + if !bytes.Equal(parsed.Response[:original.ResponseSize], original.Response[:original.ResponseSize]) { + t.Error("Response mismatch") + } +} + +func TestTcpEventManualCorrectness(t *testing.T) { + original := &tcpEvent{ + Fd: 999, + Pid: 888, + SPort: 1000, + DPort: 80, + } + // Set some bytes in SAddr + original.SAddr[0] = 10 + original.SAddr[15] = 1 + + buf := new(bytes.Buffer) + if err := binary.Write(buf, binary.LittleEndian, original); err != nil { + t.Fatal(err) + } + data := buf.Bytes() + + parsed := &tcpEvent{} + if err := parseTcpEvent(data, parsed); err != nil { + t.Fatal(err) + } + + if parsed.Fd != original.Fd { + t.Errorf("Fd mismatch: %v != %v", parsed.Fd, original.Fd) + } + if parsed.Pid != original.Pid { + t.Errorf("Pid mismatch: %v != %v", parsed.Pid, original.Pid) + } + if parsed.SPort != original.SPort { + t.Errorf("SPort mismatch: %v != %v", parsed.SPort, original.SPort) + } + if parsed.SAddr != original.SAddr { + t.Errorf("SAddr mismatch: %v != %v", parsed.SAddr, original.SAddr) + } +} + +func TestProcEventManualCorrectness(t *testing.T) { + original := &procEvent{ + Type: EventTypeProcessStart, + Pid: 12345, + Reason: 1, + } + buf := new(bytes.Buffer) + if err := binary.Write(buf, binary.LittleEndian, original); err != nil { + t.Fatal(err) + } + data := buf.Bytes() + + parsed := &procEvent{} + if err := parseProcEvent(data, parsed); err != nil { + t.Fatal(err) + } + + if parsed.Type != original.Type { + t.Errorf("Type mismatch: %v != %v", parsed.Type, original.Type) + } + if parsed.Pid != original.Pid { + t.Errorf("Pid mismatch: %v != %v", parsed.Pid, original.Pid) + } + if parsed.Reason != original.Reason { + t.Errorf("Reason mismatch: %v != %v", parsed.Reason, original.Reason) + } +} + +func TestFileEventManualCorrectness(t *testing.T) { + original := &fileEvent{ + Type: EventTypeFileOpen, + Pid: 54321, + Fd: 99, + Mnt: 88, + Log: 77, + } + buf := new(bytes.Buffer) + if err := binary.Write(buf, binary.LittleEndian, original); err != nil { + t.Fatal(err) + } + data := buf.Bytes() + + parsed := &fileEvent{} + if err := parseFileEvent(data, parsed); err != nil { + t.Fatal(err) + } + + if parsed.Type != original.Type { + t.Errorf("Type mismatch: %v != %v", parsed.Type, original.Type) + } + if parsed.Pid != original.Pid { + t.Errorf("Pid mismatch: %v != %v", parsed.Pid, original.Pid) + } + if parsed.Fd != original.Fd { + t.Errorf("Fd mismatch: %v != %v", parsed.Fd, original.Fd) + } + if parsed.Mnt != original.Mnt { + t.Errorf("Mnt mismatch: %v != %v", parsed.Mnt, original.Mnt) + } + if parsed.Log != original.Log { + t.Errorf("Log mismatch: %v != %v", parsed.Log, original.Log) + } +} diff --git a/ebpftracer/tracer.go b/ebpftracer/tracer.go index 1277aee5..d51738cf 100644 --- a/ebpftracer/tracer.go +++ b/ebpftracer/tracer.go @@ -4,7 +4,6 @@ import ( "bytes" "compress/gzip" "encoding/base64" - "encoding/binary" "errors" "fmt" "io" @@ -469,9 +468,7 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy switch typ { case perfMapTypeL7Events: v := &l7Event{} - data := rec.RawSample - - if err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, v); err != nil { + if err := parseL7Event(rec.RawSample, v); err != nil { klog.Warningln("failed to read l7 event:", err) continue } @@ -508,21 +505,21 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy } case perfMapTypeFileEvents: v := &fileEvent{} - if err := binary.Read(bytes.NewBuffer(rec.RawSample), binary.LittleEndian, v); err != nil { + if err := parseFileEvent(rec.RawSample, v); err != nil { klog.Warningln("failed to read file event:", err) continue } event = Event{Type: v.Type, Pid: v.Pid, Fd: v.Fd, Mnt: v.Mnt, Log: v.Log > 0} case perfMapTypeProcEvents: v := &procEvent{} - if err := binary.Read(bytes.NewBuffer(rec.RawSample), binary.LittleEndian, v); err != nil { + if err := parseProcEvent(rec.RawSample, v); err != nil { klog.Warningln("failed to read proc event:", err) continue } event = Event{Type: v.Type, Reason: EventReason(v.Reason), Pid: v.Pid} case perfMapTypeTCPEvents: v := &tcpEvent{} - if err := binary.Read(bytes.NewBuffer(rec.RawSample), binary.LittleEndian, v); err != nil { + if err := parseTcpEvent(rec.RawSample, v); err != nil { klog.Warningln("failed to read tcp event:", err) continue }