Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions ebpftracer/parsing.go
Original file line number Diff line number Diff line change
@@ -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
}
152 changes: 152 additions & 0 deletions ebpftracer/parsing_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
11 changes: 4 additions & 7 deletions ebpftracer/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
Loading