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
139 changes: 139 additions & 0 deletions ebpftracer/parsing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package ebpftracer

import (
"encoding/binary"
"fmt"
"time"

"github.com/coroot/coroot-node-agent/ebpftracer/l7"
)

func parseL7Event(data []byte) (Event, error) {
if len(data) < 8248 { // Size of l7Event struct
return Event{}, fmt.Errorf("buffer too short for l7Event: %d", len(data))
}

fd := binary.LittleEndian.Uint64(data[0:8])
connectionTimestamp := binary.LittleEndian.Uint64(data[8:16])
pid := binary.LittleEndian.Uint32(data[16:20])
status := int32(binary.LittleEndian.Uint32(data[20:24]))
duration := binary.LittleEndian.Uint64(data[24:32])
protocol := data[32]
method := data[33]
// padding 34-36
statementId := binary.LittleEndian.Uint32(data[36:40])
payloadSize := binary.LittleEndian.Uint64(data[40:48])
responseSize := binary.LittleEndian.Uint64(data[48:56])

// Cap sizes to 4096 as per struct definition
if payloadSize > 4096 {
payloadSize = 4096
}
if responseSize > 4096 {
responseSize = 4096
}

payloadData := make([]byte, payloadSize)
copy(payloadData, data[56:56+payloadSize])

responseData := make([]byte, responseSize)
copy(responseData, data[4152:4152+responseSize])

req := &l7.RequestData{
Protocol: l7.Protocol(protocol),
Status: l7.Status(status),
Duration: time.Duration(duration),
Method: l7.Method(method),
StatementId: statementId,
PayloadSize: payloadSize,
ResponseSize: responseSize,
Payload: payloadData,
Response: responseData,
}

return Event{
Type: EventTypeL7Request,
Pid: pid,
Fd: fd,
Timestamp: connectionTimestamp,
L7Request: req,
}, nil
}
Comment on lines +11 to +61

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function, and others in this file, use many magic numbers for struct sizes and field offsets (e.g., 8248, 0:8, 8:16, 56, 4152). This makes the code harder to read and maintain. If the eBPF C struct layout changes, updating these numbers would be error-prone.

I recommend defining these values as constants at the package level. This will make the code self-documenting and much easier to maintain.

For example, for l7Event:

const (
    // l7Event C struct layout
    l7EventSize = 8248

    l7EventFdOffset                  = 0
    l7EventConnectionTimestampOffset = 8
    // ... and so on for all fields
    l7EventPayloadOffset      = 56
    l7EventResponseOffset     = 4152 // payloadOffset + 4096
)

You could then use these constants in parseL7Event and apply a similar pattern for the other parsing functions.


func parseTCPEvent(data []byte) (Event, error) {
if len(data) < 102 { // Size of tcpEvent struct
return Event{}, fmt.Errorf("buffer too short for tcpEvent: %d", len(data))
}

fd := binary.LittleEndian.Uint64(data[0:8])
timestamp := binary.LittleEndian.Uint64(data[8:16])
duration := binary.LittleEndian.Uint64(data[16:24])
typ := EventType(binary.LittleEndian.Uint32(data[24:28]))
pid := binary.LittleEndian.Uint32(data[28:32])
bytesSent := binary.LittleEndian.Uint64(data[32:40])
bytesReceived := binary.LittleEndian.Uint64(data[40:48])
sPort := binary.LittleEndian.Uint16(data[48:50])
dPort := binary.LittleEndian.Uint16(data[50:52])
aPort := binary.LittleEndian.Uint16(data[52:54])

var sAddr, dAddr, aAddr [16]byte
copy(sAddr[:], data[54:70])
copy(dAddr[:], data[70:86])
copy(aAddr[:], data[86:102])

event := Event{
Type: typ,
Pid: pid,
SrcAddr: ipPort(sAddr, sPort),
DstAddr: ipPort(dAddr, dPort),
ActualDstAddr: ipPort(aAddr, aPort),
Fd: fd,
Timestamp: timestamp,
Duration: time.Duration(duration),
}

if typ == EventTypeConnectionClose {
event.TrafficStats = &TrafficStats{
BytesSent: bytesSent,
BytesReceived: bytesReceived,
}
}

return event, nil
}

func parseFileEvent(data []byte) (Event, error) {
if len(data) < 32 { // Size of fileEvent struct
return Event{}, fmt.Errorf("buffer too short for fileEvent: %d", len(data))
}

typ := EventType(binary.LittleEndian.Uint32(data[0:4]))
pid := binary.LittleEndian.Uint32(data[4:8])
fd := binary.LittleEndian.Uint64(data[8:16])
mnt := binary.LittleEndian.Uint64(data[16:24])
logVal := binary.LittleEndian.Uint64(data[24:32])

return Event{
Type: typ,
Pid: pid,
Fd: fd,
Mnt: mnt,
Log: logVal > 0,
}, nil
}

func parseProcEvent(data []byte) (Event, error) {
if len(data) < 12 { // Size of procEvent struct
return Event{}, fmt.Errorf("buffer too short for procEvent: %d", len(data))
}

typ := EventType(binary.LittleEndian.Uint32(data[0:4]))
pid := binary.LittleEndian.Uint32(data[4:8])
reason := binary.LittleEndian.Uint32(data[8:12])

return Event{
Type: typ,
Reason: EventReason(reason),
Pid: pid,
}, nil
}
196 changes: 196 additions & 0 deletions ebpftracer/parsing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package ebpftracer

import (
"bytes"
"encoding/binary"
"testing"
"time"

"github.com/coroot/coroot-node-agent/ebpftracer/l7"
"github.com/stretchr/testify/assert"
"inet.af/netaddr"
)

// Original struct definitions for testing (renamed to avoid conflict)
type l7EventTest struct {
Fd uint64
ConnectionTimestamp uint64
Pid uint32
Status int32
Duration uint64
Protocol uint8
Method uint8
Padding uint16
StatementId uint32
PayloadSize uint64
ResponseSize uint64
Payload [4096]byte
Response [4096]byte
}

type tcpEventTest struct {
Fd uint64
Timestamp uint64
Duration uint64
Type EventType
Pid uint32
BytesSent uint64
BytesReceived uint64
SPort uint16
DPort uint16
Aport uint16
SAddr [16]byte
DAddr [16]byte
AAddr [16]byte
}

type fileEventTest struct {
Type EventType
Pid uint32
Fd uint64
Mnt uint64
Log uint64
}

type procEventTest struct {
Type EventType
Pid uint32
Reason uint32
}

func TestParseL7Event(t *testing.T) {
expectedPayload := []byte("request payload")
expectedResponse := []byte("response payload")

v := &l7EventTest{
Fd: 12345,
ConnectionTimestamp: 67890,
Pid: 111,
Status: 200,
Duration: 500,
Protocol: uint8(l7.ProtocolHTTP),
Method: 1, // Arbitrary method
StatementId: 999,
PayloadSize: uint64(len(expectedPayload)),
ResponseSize: uint64(len(expectedResponse)),
}
copy(v.Payload[:], expectedPayload)
copy(v.Response[:], expectedResponse)

buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, v)
assert.NoError(t, err)

data := buf.Bytes()

// Test new parsing
event, err := parseL7Event(data)
assert.NoError(t, err)

assert.Equal(t, EventTypeL7Request, event.Type)
assert.Equal(t, v.Fd, event.Fd)
assert.Equal(t, v.ConnectionTimestamp, event.Timestamp)
assert.Equal(t, v.Pid, event.Pid)

req := event.L7Request
assert.Equal(t, l7.ProtocolHTTP, req.Protocol)
assert.Equal(t, l7.Status(200), req.Status)
assert.Equal(t, time.Duration(500), req.Duration)
assert.Equal(t, l7.Method(1), req.Method)
assert.Equal(t, v.StatementId, req.StatementId)
assert.Equal(t, v.PayloadSize, req.PayloadSize)
assert.Equal(t, v.ResponseSize, req.ResponseSize)
assert.Equal(t, expectedPayload, req.Payload)
assert.Equal(t, expectedResponse, req.Response)
}

func TestParseTCPEvent(t *testing.T) {
sAddr := [16]byte{192, 168, 1, 1} // Rest are 0
dAddr := [16]byte{10, 0, 0, 1}

v := &tcpEventTest{
Fd: 555,
Timestamp: 123456789,
Duration: 100,
Type: EventTypeConnectionClose,
Pid: 777,
BytesSent: 1000,
BytesReceived: 2000,
SPort: 1234,
DPort: 80,
Aport: 5678,
SAddr: sAddr,
DAddr: dAddr,
AAddr: sAddr,
}

buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, v)
assert.NoError(t, err)

data := buf.Bytes()

event, err := parseTCPEvent(data)
assert.NoError(t, err)

assert.Equal(t, EventTypeConnectionClose, event.Type)
assert.Equal(t, v.Fd, event.Fd)
assert.Equal(t, v.Timestamp, event.Timestamp)
assert.Equal(t, time.Duration(100), event.Duration)
assert.Equal(t, v.Pid, event.Pid)

assert.NotNil(t, event.TrafficStats)
assert.Equal(t, v.BytesSent, event.TrafficStats.BytesSent)
assert.Equal(t, v.BytesReceived, event.TrafficStats.BytesReceived)

// Verify IPs
i, _ := netaddr.FromStdIP(sAddr[:])
expectedSrc := netaddr.IPPortFrom(i, v.SPort)
assert.Equal(t, expectedSrc, event.SrcAddr)
}

func TestParseFileEvent(t *testing.T) {
v := &fileEventTest{
Type: EventTypeFileOpen,
Pid: 888,
Fd: 999,
Mnt: 10,
Log: 1,
}

buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, v)
assert.NoError(t, err)

data := buf.Bytes()

event, err := parseFileEvent(data)
assert.NoError(t, err)

assert.Equal(t, EventTypeFileOpen, event.Type)
assert.Equal(t, v.Pid, event.Pid)
assert.Equal(t, v.Fd, event.Fd)
assert.Equal(t, v.Mnt, event.Mnt)
assert.True(t, event.Log)
}

func TestParseProcEvent(t *testing.T) {
v := &procEventTest{
Type: EventTypeProcessStart,
Pid: 123,
Reason: uint32(EventReasonOOMKill),
}

buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, v)
assert.NoError(t, err)

data := buf.Bytes()

event, err := parseProcEvent(data)
assert.NoError(t, err)

assert.Equal(t, EventTypeProcessStart, event.Type)
assert.Equal(t, v.Pid, event.Pid)
assert.Equal(t, EventReasonOOMKill, event.Reason)
}
Loading
Loading