diff --git a/pkg/eBPF/handler.go b/pkg/eBPF/handler.go index 9aad045..3713d00 100644 --- a/pkg/eBPF/handler.go +++ b/pkg/eBPF/handler.go @@ -8,14 +8,16 @@ import ( "github.com/intelops/tarian-detector/pkg/err" ) +var handlerErr = err.New("ebpf.handler") + +// Handler represents an eBPF handler. type Handler struct { - name string - mapReaders []any - probeLinks []link.Link + name string // Name of the handler + mapReaders []any // List of map readers + probeLinks []link.Link // List of probe links } -var handlerErr = err.New("ebpf.handler") - +// NewHandler creates a new eBPF handler with the given name. func NewHandler(n string) *Handler { return &Handler{ name: n, @@ -24,22 +26,27 @@ func NewHandler(n string) *Handler { } } +// AddProbeLink adds a probe link to the handler. func (h *Handler) AddProbeLink(l link.Link) { h.probeLinks = append(h.probeLinks, l) } +// AddMapReaders adds map readers to the handler. func (h *Handler) AddMapReaders(mrs []any) { h.mapReaders = append(h.mapReaders, mrs...) } +// ReadAsInterface returns a slice of functions that read data from maps. func (h *Handler) ReadAsInterface() ([]func() ([]byte, error), error) { return read(h.mapReaders) } +// Count returns the number of probe links in the handler. func (h *Handler) Count() int { return len(h.probeLinks) } +// Close detaches probes and closes map readers. func (h *Handler) Close() error { if err := detachProbes(h.probeLinks); err != nil { return handlerErr.Throwf("%v", err) @@ -48,14 +55,17 @@ func (h *Handler) Close() error { return closeMapReaders(h.mapReaders) } +// GetName returns the name of the handler. func (h *Handler) GetName() string { return h.name } +// GetMapReaders returns the map readers associated with the handler. func (h *Handler) GetMapReaders() []any { return h.mapReaders } +// GetProbeLinks returns the probe links associated with the handler. func (h *Handler) GetProbeLinks() []link.Link { return h.probeLinks } diff --git a/pkg/eBPF/handler_test.go b/pkg/eBPF/handler_test.go new file mode 100644 index 0000000..962a326 --- /dev/null +++ b/pkg/eBPF/handler_test.go @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package ebpf + +import ( + "os" + "reflect" + "testing" + + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/perf" +) + +// TestHandler_GetName tests the GetName function +func TestHandler_GetName(t *testing.T) { + type fields struct { + name string + mapReaders []any + probeLinks []link.Link + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "valid values", + fields: fields{ + name: "test", + mapReaders: make([]any, 0), + probeLinks: make([]link.Link, 0), + }, + want: "test", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + name: tt.fields.name, + mapReaders: tt.fields.mapReaders, + probeLinks: tt.fields.probeLinks, + } + if got := h.GetName(); got != tt.want { + t.Errorf("Handler.GetName() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHandler_GetMapReaders tests the GetMapReaders function +func TestHandler_GetMapReaders(t *testing.T) { + type fields struct { + name string + mapReaders []any + probeLinks []link.Link + } + tests := []struct { + name string + fields fields + want []any + }{ + { + name: "valid values", + fields: fields{ + name: "test", + mapReaders: make([]any, 0), + probeLinks: make([]link.Link, 0), + }, + want: make([]any, 0), + }, + { + name: "nil values", + fields: fields{ + name: "test", + mapReaders: nil, + probeLinks: nil, + }, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + name: tt.fields.name, + mapReaders: tt.fields.mapReaders, + probeLinks: tt.fields.probeLinks, + } + if got := h.GetMapReaders(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Handler.GetMapReaders() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHandler_GetProbeLinks tests the GetProbeLinks function +func TestHandler_GetProbeLinks(t *testing.T) { + type fields struct { + name string + mapReaders []any + probeLinks []link.Link + } + tests := []struct { + name string + fields fields + want []link.Link + }{ + { + name: "valid values", + fields: fields{ + name: "test", + mapReaders: make([]any, 0), + probeLinks: make([]link.Link, 0), + }, + want: make([]link.Link, 0), + }, + { + name: "nil values", + fields: fields{ + name: "test", + mapReaders: nil, + probeLinks: nil, + }, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + name: tt.fields.name, + mapReaders: tt.fields.mapReaders, + probeLinks: tt.fields.probeLinks, + } + if got := h.GetProbeLinks(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Handler.GetProbeLinks() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHandler_Count tests the Count function +func TestHandler_Count(t *testing.T) { + prog := dummy_kprobe_prog(t) + l, _ := link.Kprobe("vprintk", prog, nil) + + type fields struct { + name string + mapReaders []any + probeLinks []link.Link + } + tests := []struct { + name string + fields fields + want int + }{ + { + name: "valid values", + fields: fields{ + name: "test", + mapReaders: make([]any, 0), + probeLinks: make([]link.Link, 0), + }, + want: 0, + }, + { + name: "add an item to the probelink", + fields: fields{ + name: "test", + mapReaders: nil, + probeLinks: []link.Link{l, l, l}, + }, + want: 3, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + name: tt.fields.name, + mapReaders: tt.fields.mapReaders, + probeLinks: tt.fields.probeLinks, + } + if got := h.Count(); got != tt.want { + t.Errorf("Handler.Count() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHandler_ReadAsInterface tests the ReadAsInterface function +func TestHandler_ReadAsInterface(t *testing.T) { + mapP := dummy_perf_map(t) + + type fields struct { + name string + mapReaders []any + probeLinks []link.Link + } + tests := []struct { + name string + fields fields + want int + wantErr bool + }{ + { + name: "valid values", + fields: fields{ + name: "test", + mapReaders: make([]any, 0), + probeLinks: make([]link.Link, 0), + }, + want: 0, + wantErr: false, + }, + { + name: "invalid values", + fields: fields{ + name: "test", + mapReaders: []any{ + func() *perf.Reader { + r, _ := perf.NewReader(mapP, os.Getpagesize()) + return r + }(), + }, + probeLinks: make([]link.Link, 0), + }, + want: 1, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + name: tt.fields.name, + mapReaders: tt.fields.mapReaders, + probeLinks: tt.fields.probeLinks, + } + got, err := h.ReadAsInterface() + if (err != nil) != tt.wantErr { + t.Errorf("Handler.ReadAsInterface() error = %v, wantErr %v", err, tt.wantErr) + return + } + if len(got) != tt.want { + t.Errorf("Handler.ReadAsInterface() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHandler_Close tests the Close function +func TestHandler_Close(t *testing.T) { + type fields struct { + name string + mapReaders []any + probeLinks []link.Link + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "valid values", + fields: fields{ + name: "test", + mapReaders: make([]any, 0), + probeLinks: make([]link.Link, 0), + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + name: tt.fields.name, + mapReaders: tt.fields.mapReaders, + probeLinks: tt.fields.probeLinks, + } + if err := h.Close(); (err != nil) != tt.wantErr { + t.Errorf("Handler.Close() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/pkg/eBPF/hook.go b/pkg/eBPF/hook.go index 938b549..800b10c 100644 --- a/pkg/eBPF/hook.go +++ b/pkg/eBPF/hook.go @@ -11,16 +11,20 @@ import ( "github.com/intelops/tarian-detector/pkg/err" ) +var hookErr = err.New("ebpf.hook") + +// HookInfoType is an integer type used to represent different types of eBPF hooks. type HookInfoType int +// HookInfo struct contains information about an eBPF hook. type HookInfo struct { - hookType HookInfoType - group string // HookInfoType: Tracepoint needs this Field - name string - opts any // expected values with relavant hook type: cilium/ebpf/link.*TracepointOptions | .RawTracepointOptions | .*KprobeOptions | .CgroupOptions + hookType HookInfoType // Type of the eBPF hook + group string // Group name, required for Tracepoint type hooks + name string // Name of the hook + opts any // Options for the hook, varies based on the hook type } -// Supported ebpf hooks +// Constants representing different types of eBPF hooks. const ( Tracepoint HookInfoType = iota RawTracepoint @@ -35,8 +39,7 @@ const ( ErrInvalidOptionsTypeForBpfHookType string = "unexpected 'Opts' field type detected in the BPF Hook. Expected type: %T, Received type: %T" ) -var hookErr = err.New("ebpf.hook") - +// NewHookInfo creates a new HookInfo instance with default values. func NewHookInfo() *HookInfo { return &HookInfo{ name: "", @@ -46,6 +49,7 @@ func NewHookInfo() *HookInfo { } } +// Tracepoint sets the HookInfo instance to represent a Tracepoint type hook. func (hi *HookInfo) Tracepoint(g string, n string, op ...*link.TracepointOptions) *HookInfo { if len(op) > 0 { hi.opts = op[0] @@ -60,6 +64,7 @@ func (hi *HookInfo) Tracepoint(g string, n string, op ...*link.TracepointOptions return hi } +// RawTracepoint sets the HookInfo instance to represent a RawTracepoint type hook. func (hi *HookInfo) RawTracepoint(op link.RawTracepointOptions) *HookInfo { hi.hookType = RawTracepoint hi.opts = op @@ -67,6 +72,7 @@ func (hi *HookInfo) RawTracepoint(op link.RawTracepointOptions) *HookInfo { return hi } +// Kprobe sets the HookInfo instance to represent a Kprobe type hook. func (hi *HookInfo) Kprobe(n string, op ...*link.KprobeOptions) *HookInfo { if len(op) > 0 { hi.opts = op[0] @@ -80,6 +86,7 @@ func (hi *HookInfo) Kprobe(n string, op ...*link.KprobeOptions) *HookInfo { return hi } +// Kretprobe sets the HookInfo instance to represent a Kretprobe type hook. func (hi *HookInfo) Kretprobe(n string, op ...*link.KprobeOptions) *HookInfo { if len(op) > 0 { hi.opts = op[0] @@ -93,6 +100,7 @@ func (hi *HookInfo) Kretprobe(n string, op ...*link.KprobeOptions) *HookInfo { return hi } +// Cgroup sets the HookInfo instance to represent a Cgroup type hook. func (hi *HookInfo) Cgroup(op link.CgroupOptions) *HookInfo { hi.hookType = Cgroup hi.opts = op @@ -100,6 +108,7 @@ func (hi *HookInfo) Cgroup(op link.CgroupOptions) *HookInfo { return hi } +// AttachProbe attaches the eBPF program to the hook represented by the HookInfo instance. func (hi *HookInfo) AttachProbe(programName *ebpf.Program) (link.Link, error) { switch hi.hookType { case Tracepoint: @@ -151,6 +160,7 @@ func (hi *HookInfo) AttachProbe(programName *ebpf.Program) (link.Link, error) { } } +// detachProbes detaches all the probes represented by the links in the provided slice. func detachProbes(lns []link.Link) error { for _, l := range lns { err := detachProbe(l) @@ -162,26 +172,32 @@ func detachProbes(lns []link.Link) error { return nil } +// detachProbe detaches the probe represented by the provided link. func detachProbe(l link.Link) error { return l.Close() } +// GetHookType returns the type of the hook represented by the HookInfo instance. func (hi *HookInfo) GetHookType() HookInfoType { return hi.hookType } +// GetHookName returns the name of the hook represented by the HookInfo instance. func (hi *HookInfo) GetHookName() string { return hi.name } +// GetHookGroup returns the group of the hook represented by the HookInfo instance. func (hi *HookInfo) GetHookGroup() string { return hi.group } +// GetOptions returns the opts of the hook represented by the HookInfo instance. func (hi *HookInfo) GetOptions() interface{} { return hi.opts } +// String method for the HookInfoType type. It returns a string representation of the HookInfoType. func (hit HookInfoType) String() string { switch hit { case Tracepoint: diff --git a/pkg/eBPF/hook_test.go b/pkg/eBPF/hook_test.go new file mode 100644 index 0000000..d18ca1c --- /dev/null +++ b/pkg/eBPF/hook_test.go @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package ebpf + +import ( + "reflect" + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" +) + +// TestHookInfo tests the HookInfo type +func TestHookInfo(t *testing.T) { + tests := []struct { + name string + hi *HookInfo + want string + }{ + { + name: "Tracepoint", + hi: NewHookInfo().Tracepoint("group", "name"), + want: "Tracepoint", + }, + { + name: "Tracepoint with options", + hi: NewHookInfo().Tracepoint("group", "name", nil), + want: "Tracepoint", + }, + { + name: "RawTracepoint", + hi: NewHookInfo().RawTracepoint(link.RawTracepointOptions{}), + want: "RawTracepoint", + }, + { + name: "Kprobe", + hi: NewHookInfo().Kprobe("name"), + want: "Kprobe", + }, + { + name: "Kprobe with options", + hi: NewHookInfo().Kprobe("name", nil), + want: "Kprobe", + }, + { + name: "Kretprobe", + hi: NewHookInfo().Kretprobe("name"), + want: "Kretprobe", + }, + { + name: "Kretprobe with options", + hi: NewHookInfo().Kretprobe("name", nil), + want: "Kretprobe", + }, + { + name: "Cgroup", + hi: NewHookInfo().Cgroup(link.CgroupOptions{}), + want: "Cgroup", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.hi.GetHookType().String(); got != tt.want { + t.Errorf("HookInfo.GetHookType() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHookInfo_AttachProbe tests the AttachProbe function +func TestHookInfo_AttachProbe(t *testing.T) { + // Mock program + prog := &ebpf.Program{} + + tests := []struct { + name string + hi *HookInfo + wantErr bool + }{ + { + name: "Tracepoint with missing name", + hi: NewHookInfo().Tracepoint("", "name"), + wantErr: true, + }, + { + name: "Tracepoint with missing group", + hi: NewHookInfo().Tracepoint("group", ""), + wantErr: true, + }, + { + name: "Tracepoint with wrong options type", + hi: func() *HookInfo { + hi := NewHookInfo().Tracepoint("group", "test") + hi.opts = link.KprobeOptions{} + return hi + }(), + wantErr: true, + }, + { + name: "Tracepoint", + hi: NewHookInfo().Tracepoint("group", "test"), + wantErr: true, + }, + { + name: "RawTracepoint with wrong options type", + hi: func() *HookInfo { + hi := NewHookInfo() + hi.hookType = RawTracepoint + hi.opts = link.KprobeOptions{} + return hi + }(), + wantErr: true, + }, + { + name: "RawTracepoint", + hi: NewHookInfo().RawTracepoint(link.RawTracepointOptions{Name: "__x64_sys_printk", Program: prog}), + wantErr: true, + }, + { + name: "Kprobe with missing name", + hi: NewHookInfo().Kprobe(""), + wantErr: true, + }, + { + name: "Kretprobe with missing name", + hi: NewHookInfo().Kretprobe(""), + wantErr: true, + }, + { + name: "Kretprobe with wrong options type", + hi: func() *HookInfo { + hi := NewHookInfo().Kretprobe("vprintk") + hi.opts = link.TracepointOptions{} + + return hi + }(), + wantErr: true, + }, + { + name: "Kretprobe", + hi: NewHookInfo().Kretprobe("vprintk"), + wantErr: true, + }, + { + name: "Cgroup with wrong options type", + hi: func() *HookInfo { + hi := NewHookInfo() + hi.hookType = Cgroup + hi.opts = link.KprobeOptions{} + return hi + }(), + wantErr: true, + }, + { + name: "Cgroup", + hi: NewHookInfo().Cgroup(link.CgroupOptions{Path: "", Attach: 0, Program: prog}), + wantErr: true, + }, + { + name: "Invalid HookInfoType", + hi: &HookInfo{hookType: HookInfoType(999)}, // An unknown HookInfoType + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := tt.hi.AttachProbe(prog) + if (err != nil) != tt.wantErr { + t.Errorf("HookInfo.AttachProbe() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestHookInfo_GetHookName tests the GetHookName function +func TestHookInfo_GetHookName(t *testing.T) { + tests := []struct { + name string + hi *HookInfo + want string + }{ + { + name: "Tracepoint", + hi: NewHookInfo().Tracepoint("group", "name"), + want: "name", + }, + { + name: "Kprobe", + hi: NewHookInfo().Kprobe("name"), + want: "name", + }, + { + name: "Kretprobe", + hi: NewHookInfo().Kretprobe("name"), + want: "name", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.hi.GetHookName(); got != tt.want { + t.Errorf("HookInfo.GetHookName() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHookInfo_GetHookGroup tests the GetHookGroup function +func TestHookInfo_GetHookGroup(t *testing.T) { + tests := []struct { + name string + hi *HookInfo + want string + }{ + { + name: "Tracepoint", + hi: NewHookInfo().Tracepoint("group", "name"), + want: "group", + }, + { + name: "Kprobe", + hi: NewHookInfo().Kprobe("name"), + want: "", + }, + { + name: "Kretprobe", + hi: NewHookInfo().Kretprobe("name"), + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.hi.GetHookGroup(); got != tt.want { + t.Errorf("HookInfo.GetHookGroup() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestHookInfo_GetOptions tests the GetOptions function +func TestHookInfo_GetOptions(t *testing.T) { + tests := []struct { + name string + hi *HookInfo + want any + }{ + { + name: "Tracepoint", + hi: NewHookInfo().Tracepoint("group", "name"), + want: &link.TracepointOptions{}, + }, + { + name: "RawTracepoint", + hi: NewHookInfo().RawTracepoint(link.RawTracepointOptions{}), + want: link.RawTracepointOptions{}, + }, + { + name: "Kprobe", + hi: NewHookInfo().Kprobe("name"), + want: &link.KprobeOptions{}, + }, + { + name: "Kretprobe", + hi: NewHookInfo().Kretprobe("name"), + want: &link.KprobeOptions{}, + }, + { + name: "Cgroup", + hi: NewHookInfo().Cgroup(link.CgroupOptions{}), + want: link.CgroupOptions{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.hi.GetOptions() + if reflect.TypeOf(got) != reflect.TypeOf(tt.want) { + t.Errorf("HookInfo.GetOptions() = %T, want %T", got, tt.want) + } + }) + } +} + +// TestHookInfoType_String tests the String function +func TestHookInfoType_String(t *testing.T) { + tests := []struct { + name string + hit HookInfoType + want string + }{ + { + name: "Tracepoint", + hit: Tracepoint, + want: "Tracepoint", + }, + { + name: "RawTracepoint", + hit: RawTracepoint, + want: "RawTracepoint", + }, + { + name: "Kprobe", + hit: Kprobe, + want: "Kprobe", + }, + { + name: "Kretprobe", + hit: Kretprobe, + want: "Kretprobe", + }, + { + name: "Cgroup", + hit: Cgroup, + want: "Cgroup", + }, + { + name: "Unknown", + hit: HookInfoType(999), // An unknown HookInfoType + want: "unknown HookInfoType(999)", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.hit.String(); got != tt.want { + t.Errorf("HookInfoType.String() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_detachProbe tests the detachProbe function +func Test_detachProbe(t *testing.T) { + prog := dummy_kprobe_prog(t) + l, _ := link.Kprobe("vprintk", prog, nil) + + type args struct { + l link.Link + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "invalid link", + args: args{l: l}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := detachProbe(tt.args.l); (err != nil) != tt.wantErr { + t.Errorf("detachProbe() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_detachProbes(t *testing.T) { + prog := dummy_kprobe_prog(t) + l, _ := link.Kprobe("vprintk", prog, nil) + + type args struct { + lns []link.Link + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "link", + args: args{lns: []link.Link{l}}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := detachProbes(tt.args.lns); (err != nil) != tt.wantErr { + t.Errorf("detachProbes() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/pkg/eBPF/map.go b/pkg/eBPF/map.go index 7936c43..9ca6eac 100644 --- a/pkg/eBPF/map.go +++ b/pkg/eBPF/map.go @@ -13,8 +13,12 @@ import ( "github.com/intelops/tarian-detector/pkg/err" ) +var mapErr = err.New("ebpf.map") + +// MapInfoType is an integer type used to represent different types of eBPF maps. type MapInfoType int +// MapInfo holds information about an eBPF map. type MapInfo struct { mapType MapInfoType bpfMap *ebpf.Map @@ -22,25 +26,31 @@ type MapInfo struct { innerMapType MapInfoType } -// Supported ebpfmaps +// Supported eBPF map types. const ( - RingBuffer MapInfoType = iota - PerfEventArray - ArrayOfMaps + RingBuffer MapInfoType = iota // RingBuffer represents a ring buffer eBPF map. + PerfEventArray // PerfEventArray represents a perf event array eBPF map. + ArrayOfMaps // ArrayOfMaps represents an array of maps eBPF map. ) const ( - ErrNilMapPointer string = "nil pointer received, expected cilium/ebpf.Map pointer" - ErrNilMapReader string = "nil pointer received, expected pointer to cilium/ebpf map reader" - ErrUnsupportedBpfMapType string = "unsupported BPF map type: %v" - ErrUnsupportedMapReader string = "unsupported cilium/ebpf map reader: %T" - ErrUnsupportedInnerBpfMapType string = "unsupported BPF map type found within the array of maps: %v" -) + // ErrNilMapPointer is the error message for a nil pointer received, expected cilium/ebpf.Map pointer. + ErrNilMapPointer string = "nil pointer received, expected cilium/ebpf.Map pointer" + + // ErrNilMapReader is the error message for a nil pointer received, expected pointer to cilium/ebpf map reader. + ErrNilMapReader string = "nil pointer received, expected pointer to cilium/ebpf map reader" + + // ErrUnsupportedBpfMapType is the error message for an unsupported BPF map type. + ErrUnsupportedBpfMapType string = "unsupported BPF map type: %v" -var ( - mapErr = err.New("ebpf.map") + // ErrUnsupportedMapReader is the error message for an unsupported cilium/ebpf map reader. + ErrUnsupportedMapReader string = "unsupported cilium/ebpf map reader: %T" + + // ErrUnsupportedInnerBpfMapType is the error message for an unsupported BPF map type found within the array of maps. + ErrUnsupportedInnerBpfMapType string = "unsupported BPF map type found within the array of maps: %v" ) +// NewRingBuf creates a new MapInfo for a ring buffer eBPF map. func NewRingBuf(m *ebpf.Map) *MapInfo { return &MapInfo{ mapType: RingBuffer, @@ -49,6 +59,7 @@ func NewRingBuf(m *ebpf.Map) *MapInfo { } } +// NewPerfEvent creates a new MapInfo for a perf event array eBPF map. func NewPerfEvent(m *ebpf.Map) *MapInfo { return &MapInfo{ mapType: PerfEventArray, @@ -58,6 +69,7 @@ func NewPerfEvent(m *ebpf.Map) *MapInfo { } } +// NewPerfEventWithBuffer creates a new MapInfo for a perf event array eBPF map with a specified buffer. func NewPerfEventWithBuffer(m *ebpf.Map, b *ebpf.Map) *MapInfo { return &MapInfo{ mapType: PerfEventArray, @@ -67,6 +79,7 @@ func NewPerfEventWithBuffer(m *ebpf.Map, b *ebpf.Map) *MapInfo { } } +// NewArrayOfPerfEvent creates a new MapInfo for an array of perf event array eBPF maps. func NewArrayOfPerfEvent(m *ebpf.Map) *MapInfo { return &MapInfo{ mapType: ArrayOfMaps, @@ -75,6 +88,7 @@ func NewArrayOfPerfEvent(m *ebpf.Map) *MapInfo { } } +// NewArrayOfRingBuf creates a new MapInfo for an array of ring buffer eBPF maps. func NewArrayOfRingBuf(m *ebpf.Map) *MapInfo { return &MapInfo{ mapType: ArrayOfMaps, @@ -83,22 +97,28 @@ func NewArrayOfRingBuf(m *ebpf.Map) *MapInfo { } } +// String returns a string representation of the MapInfo. func (mi *MapInfo) String() string { return fmt.Sprintf("%+v", *mi) } +// CreateReaders creates readers for the eBPF map. func (mi *MapInfo) CreateReaders() ([]any, error) { var mr []any switch mi.mapType { case RingBuffer: rb, err := mi.ringbufReader() - mr = append(mr, rb) + if err == nil { + mr = append(mr, rb) + } return mr, err case PerfEventArray: pf, err := mi.perfReader() - mr = append(mr, pf) + if err == nil { + mr = append(mr, pf) + } return mr, err case ArrayOfMaps: @@ -108,12 +128,13 @@ func (mi *MapInfo) CreateReaders() ([]any, error) { } } +// arrayOfMapsReader creates readers for an array of eBPF maps. func (mi *MapInfo) arrayOfMapsReader() ([]any, error) { if mi.bpfMap == nil { return nil, mapErr.Throw(ErrNilMapPointer) } - var arrr []any + var arrMR []any for i := uint32(0); i < mi.bpfMap.MaxEntries(); i++ { var innerMap *ebpf.Map var currMap MapInfo @@ -132,22 +153,23 @@ func (mi *MapInfo) arrayOfMapsReader() ([]any, error) { return nil, mapErr.Throwf("%v", err) } - arrr = append(arrr, rb) + arrMR = append(arrMR, rb) case PerfEventArray: pf, err := currMap.perfReader() if err != nil { return nil, mapErr.Throwf("%v", err) } - arrr = append(arrr, pf) + arrMR = append(arrMR, pf) default: return nil, mapErr.Throwf(ErrUnsupportedInnerBpfMapType, mi.innerMapType) } } - return arrr, nil + return arrMR, nil } +// ringbufReader creates a reader for a ring buffer eBPF map. func (mi *MapInfo) ringbufReader() (*ringbuf.Reader, error) { if mi.bpfMap == nil { return nil, mapErr.Throw(ErrNilMapReader) @@ -161,11 +183,13 @@ func (mi *MapInfo) ringbufReader() (*ringbuf.Reader, error) { return r, nil } +// perfReader creates a reader for a perf event array eBPF map. func (mi *MapInfo) perfReader() (*perf.Reader, error) { if mi.bpfMap == nil { return nil, mapErr.Throw(ErrNilMapReader) } + fmt.Printf("%v\n", mi.bufferSize) p, err := perf.NewReader(mi.bpfMap, mi.bufferSize) if err != nil { return nil, mapErr.Throwf("%v", err) @@ -174,6 +198,7 @@ func (mi *MapInfo) perfReader() (*perf.Reader, error) { return p, nil } +// read creates functions to read from a list of readers. func read(readers []any) ([]func() ([]byte, error), error) { var funcs []func() ([]byte, error) @@ -201,6 +226,7 @@ func read(readers []any) ([]func() ([]byte, error), error) { return funcs, nil } +// readRingbuf creates a function to read from a ring buffer eBPF map. func readRingbuf(r *ringbuf.Reader) (func() ([]byte, error), error) { if r == nil { return nil, mapErr.Throw(ErrNilMapReader) @@ -216,6 +242,7 @@ func readRingbuf(r *ringbuf.Reader) (func() ([]byte, error), error) { }, nil } +// readPerf creates a function to read from a perf event array eBPF map. func readPerf(pr *perf.Reader) (func() ([]byte, error), error) { if pr == nil { return nil, mapErr.Throw(ErrNilMapReader) @@ -231,6 +258,7 @@ func readPerf(pr *perf.Reader) (func() ([]byte, error), error) { }, nil } +// closeMapReaders closes all the readers in the provided slice. func closeMapReaders(readers []any) error { for _, reader := range readers { switch mr := reader.(type) { @@ -252,22 +280,27 @@ func closeMapReaders(readers []any) error { return nil } +// GetMapType returns the type of the eBPF map. func (mi *MapInfo) GetMapType() MapInfoType { return mi.mapType } +// GetInnerMapType returns the type of the inner eBPF map. func (mi *MapInfo) GetInnerMapType() MapInfoType { return mi.innerMapType } +// GetBpfMap returns the actual eBPF map. func (mi *MapInfo) GetBpfMap() *ebpf.Map { return mi.bpfMap } +// GetBufferSize returns the size of the buffer for the eBPF map. func (mi *MapInfo) GetBufferSize() int { return mi.bufferSize } +// String returns a string representation of the MapInfoType. func (mit MapInfoType) String() string { switch mit { case RingBuffer: diff --git a/pkg/eBPF/map_test.go b/pkg/eBPF/map_test.go new file mode 100644 index 0000000..5218e5e --- /dev/null +++ b/pkg/eBPF/map_test.go @@ -0,0 +1,838 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package ebpf + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/perf" + "github.com/cilium/ebpf/ringbuf" +) + +func dummy_ring_map(t *testing.T) *ebpf.Map { + events, err := ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.RingBuf, + MaxEntries: 4096, + }) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { events.Close() }) + return events +} + +func dummy_percpuarray_map(t *testing.T) *ebpf.Map { + events, err := ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.PerCPUArray, + KeySize: 4, + ValueSize: 4, + MaxEntries: 4, + }) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { events.Close() }) + return events +} + +func dummy_array_of_rb_map(t *testing.T) *ebpf.Map { + inner := &ebpf.MapSpec{ + Name: "inner_map", + Type: ebpf.RingBuf, + MaxEntries: 4096, + } + im, err := ebpf.NewMap(inner) + if err != nil { + t.Fatal(err) + } + + events, err := ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.ArrayOfMaps, + KeySize: 4, + ValueSize: 4, + MaxEntries: 2, + InnerMap: inner, + Contents: []ebpf.MapKV{ + {Key: uint32(0), Value: im}, + {Key: uint32(1), Value: im}, + }, + }) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { events.Close() }) + return events +} + +func dummy_array_of_rb_map_err(t *testing.T, size uint32) *ebpf.Map { + inner := &ebpf.MapSpec{ + Name: "inner_map", + Type: ebpf.RingBuf, + MaxEntries: 4096, + } + im, err := ebpf.NewMap(inner) + if err != nil { + t.Fatal(err) + } + + events, err := ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.ArrayOfMaps, + KeySize: 4, + ValueSize: 4, + MaxEntries: size, + InnerMap: inner, + Contents: []ebpf.MapKV{ + {Key: uint32(0), Value: im}, + }, + }) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { events.Close() }) + return events +} + +func dummy_array_of_pea_map(t *testing.T) *ebpf.Map { + inner := &ebpf.MapSpec{ + Type: ebpf.PerfEventArray, + MaxEntries: 4096, + } + im, err := ebpf.NewMap(inner) + if err != nil { + t.Fatal(err) + } + + events, err := ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.ArrayOfMaps, + KeySize: 4, + ValueSize: 4, + MaxEntries: 2, + InnerMap: inner, + Contents: []ebpf.MapKV{ + {Key: uint32(0), Value: im}, + {Key: uint32(1), Value: im}, + }, + }) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { events.Close() }) + return events +} + +// TestNewRingBuf is function to test NewRingBuf +func TestNewRingBuf(t *testing.T) { + ring := dummy_ring_map(t) + + type args struct { + m *ebpf.Map + } + tests := []struct { + name string + args args + want *MapInfo + }{ + { + name: "valid empty values", + args: args{ + m: &ebpf.Map{}, + }, + want: &MapInfo{ + mapType: RingBuffer, + bpfMap: &ebpf.Map{}, + innerMapType: -1, + }, + }, + { + name: "nil values", + args: args{ + m: nil, + }, + want: &MapInfo{ + mapType: RingBuffer, + bpfMap: nil, + innerMapType: -1, + }, + }, + { + name: "valid dummy map", + args: args{ + m: ring, + }, + want: &MapInfo{ + mapType: RingBuffer, + bpfMap: ring, + innerMapType: -1, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewRingBuf(tt.args.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewRingBuf() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestNewPerfEvent is function to test NewPerfEvent +func TestNewPerfEvent(t *testing.T) { + mapP := dummy_perf_map(t) + + type args struct { + m *ebpf.Map + } + tests := []struct { + name string + args args + want *MapInfo + }{ + { + name: "valid empty values", + args: args{ + m: &ebpf.Map{}, + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: &ebpf.Map{}, + innerMapType: -1, + bufferSize: os.Getpagesize(), + }, + }, + { + name: "nil values", + args: args{ + m: nil, + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: nil, + innerMapType: -1, + bufferSize: os.Getpagesize(), + }, + }, + { + name: "valid dummy map", + args: args{ + m: mapP, + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: mapP, + innerMapType: -1, + bufferSize: os.Getpagesize(), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewPerfEvent(tt.args.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewPerfEvent() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestNewPerfEventWithBuffer is function to test NewPerfEventWithBuffer +func TestNewPerfEventWithBuffer(t *testing.T) { + percpu := dummy_percpuarray_map(t) + mapP := dummy_perf_map(t) + + type args struct { + m *ebpf.Map + b *ebpf.Map + } + tests := []struct { + name string + args args + want *MapInfo + }{ + { + name: "valid empty values", + args: args{ + m: &ebpf.Map{}, + b: percpu, + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: &ebpf.Map{}, + innerMapType: -1, + bufferSize: 4, + }, + }, + { + name: "nil values", + args: args{ + m: nil, + b: percpu, + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: nil, + innerMapType: -1, + bufferSize: 4, + }, + }, + { + name: "valid dummy map", + args: args{ + m: mapP, + b: percpu, + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: mapP, + innerMapType: -1, + bufferSize: 4, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewPerfEventWithBuffer(tt.args.m, tt.args.b); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewPerfEventWithBuffer() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestNewArrayOfRingBuf is function to test NewArrayOfRingBuf +func TestNewArrayOfRingBuf(t *testing.T) { + arrRb := dummy_array_of_rb_map(t) + + type args struct { + m *ebpf.Map + } + tests := []struct { + name string + args args + want *MapInfo + }{ + { + name: "valid empty values", + args: args{ + m: &ebpf.Map{}, + }, + want: &MapInfo{ + mapType: ArrayOfMaps, + bpfMap: &ebpf.Map{}, + innerMapType: RingBuffer, + bufferSize: 0, + }, + }, + { + name: "nil values", + args: args{ + m: nil, + }, + want: &MapInfo{ + mapType: ArrayOfMaps, + bpfMap: nil, + innerMapType: RingBuffer, + }, + }, + { + name: "valid dummy map", + args: args{ + m: arrRb, + }, + want: &MapInfo{ + mapType: ArrayOfMaps, + bpfMap: arrRb, + innerMapType: RingBuffer, + bufferSize: 0, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewArrayOfRingBuf(tt.args.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewArrayOfRingBuf() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestNewArrayOfPerfEvent is function to test NewArrayOfPerfEvent +func TestNewArrayOfPerfEvent(t *testing.T) { + arrPf := dummy_array_of_pea_map(t) + + type args struct { + m *ebpf.Map + } + tests := []struct { + name string + args args + want *MapInfo + }{ + { + name: "valid empty values", + args: args{ + m: &ebpf.Map{}, + }, + want: &MapInfo{ + mapType: ArrayOfMaps, + bpfMap: &ebpf.Map{}, + innerMapType: PerfEventArray, + bufferSize: 0, + }, + }, + { + name: "nil values", + args: args{ + m: nil, + }, + want: &MapInfo{ + mapType: ArrayOfMaps, + bpfMap: nil, + innerMapType: PerfEventArray, + }, + }, + { + name: "valid dummy map", + args: args{ + m: arrPf, + }, + want: &MapInfo{ + mapType: ArrayOfMaps, + bpfMap: arrPf, + innerMapType: PerfEventArray, + bufferSize: 0, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewArrayOfPerfEvent(tt.args.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewArrayOfPerfEvent() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfo_String is function to test MapInfo.String +func TestMapInfo_String(t *testing.T) { + type fields struct { + mapType MapInfoType + bpfMap *ebpf.Map + bufferSize int + innerMapType MapInfoType + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "valid values", + fields: fields{ + mapType: RingBuffer, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }, + want: fmt.Sprintf("%+v", MapInfo{ + mapType: RingBuffer, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mi := &MapInfo{ + mapType: tt.fields.mapType, + bpfMap: tt.fields.bpfMap, + bufferSize: tt.fields.bufferSize, + innerMapType: tt.fields.innerMapType, + } + if got := mi.String(); got != tt.want { + t.Errorf("MapInfo.String() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfo_CreateReaders is function to test MapInfo.CreateReaders +func TestMapInfo_CreateReaders(t *testing.T) { + mapRb := dummy_ring_map(t) + mapP := dummy_perf_map(t) + arrRb := dummy_array_of_rb_map(t) + + type fields struct { + mapType MapInfoType + bpfMap *ebpf.Map + bufferSize int + innerMapType MapInfoType + } + tests := []struct { + name string + fields fields + want int + wantErr bool + }{ + { + name: "valid empty ringbuf values", + fields: fields{ + mapType: RingBuffer, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }, + want: 0, + wantErr: true, + }, + { + name: "valid dummy ringbuf", + fields: fields{ + mapType: RingBuffer, + bpfMap: mapRb, + bufferSize: 8, + innerMapType: -1, + }, + want: 1, + wantErr: false, + }, + { + name: "valid empty perf event values", + fields: fields{ + mapType: PerfEventArray, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }, + want: 0, + wantErr: true, + }, + { + name: "valid dummy ringbuf", + fields: fields{ + mapType: PerfEventArray, + bpfMap: mapP, + bufferSize: os.Getpagesize(), + innerMapType: -1, + }, + want: 1, + wantErr: false, + }, + { + name: "nil map array of maps", + fields: fields{ + mapType: ArrayOfMaps, + bpfMap: nil, + bufferSize: 0, + innerMapType: RingBuffer, + }, + want: 0, + wantErr: true, + }, + { + name: "valid dummy array of ringbuf maps", + fields: fields{ + mapType: ArrayOfMaps, + bpfMap: arrRb, + bufferSize: 0, + innerMapType: RingBuffer, + }, + want: 2, + wantErr: false, + }, + { + name: "invalid dummy array of ringbuf maps", + fields: fields{ + mapType: ArrayOfMaps, + bpfMap: dummy_array_of_rb_map_err(t, 2), + bufferSize: 0, + innerMapType: RingBuffer, + }, + want: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mi := &MapInfo{ + mapType: tt.fields.mapType, + bpfMap: tt.fields.bpfMap, + bufferSize: tt.fields.bufferSize, + innerMapType: tt.fields.innerMapType, + } + got, err := mi.CreateReaders() + if (err != nil) != tt.wantErr { + t.Errorf("MapInfo.CreateReaders() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if len(got) != tt.want { + t.Errorf("MapInfo.CreateReaders() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfo_GetMapType is function to test MapInfo.GetMapType +func TestMapInfo_GetMapType(t *testing.T) { + type fields struct { + mapType MapInfoType + bpfMap *ebpf.Map + bufferSize int + innerMapType MapInfoType + } + tests := []struct { + name string + fields fields + want MapInfoType + }{ + { + name: "valid values", + fields: fields{ + mapType: RingBuffer, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }, + want: RingBuffer, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mi := &MapInfo{ + mapType: tt.fields.mapType, + bpfMap: tt.fields.bpfMap, + bufferSize: tt.fields.bufferSize, + innerMapType: tt.fields.innerMapType, + } + if got := mi.GetMapType(); got != tt.want { + t.Errorf("MapInfo.GetMapType() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfo_GetInnerMapType is function to test MapInfo.GetInnerMapType +func TestMapInfo_GetInnerMapType(t *testing.T) { + type fields struct { + mapType MapInfoType + bpfMap *ebpf.Map + bufferSize int + innerMapType MapInfoType + } + tests := []struct { + name string + fields fields + want MapInfoType + }{ + { + name: "valid values", + fields: fields{ + mapType: ArrayOfMaps, + bpfMap: nil, + bufferSize: 0, + innerMapType: RingBuffer, + }, + want: RingBuffer, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mi := &MapInfo{ + mapType: tt.fields.mapType, + bpfMap: tt.fields.bpfMap, + bufferSize: tt.fields.bufferSize, + innerMapType: tt.fields.innerMapType, + } + if got := mi.GetInnerMapType(); got != tt.want { + t.Errorf("MapInfo.GetInnerMapType() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfo_GetBpfMap is function to test MapInfo.GetBpfMap +func TestMapInfo_GetBpfMap(t *testing.T) { + type fields struct { + mapType MapInfoType + bpfMap *ebpf.Map + bufferSize int + innerMapType MapInfoType + } + tests := []struct { + name string + fields fields + want *ebpf.Map + }{ + { + name: "valid values", + fields: fields{ + mapType: RingBuffer, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mi := &MapInfo{ + mapType: tt.fields.mapType, + bpfMap: tt.fields.bpfMap, + bufferSize: tt.fields.bufferSize, + innerMapType: tt.fields.innerMapType, + } + if got := mi.GetBpfMap(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapInfo.GetBpfMap() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfo_GetBufferSize is function to test MapInfo.GetBufferSize +func TestMapInfo_GetBufferSize(t *testing.T) { + type fields struct { + mapType MapInfoType + bpfMap *ebpf.Map + bufferSize int + innerMapType MapInfoType + } + tests := []struct { + name string + fields fields + want int + }{ + { + name: "valid values", + fields: fields{ + mapType: RingBuffer, + bpfMap: nil, + bufferSize: 0, + innerMapType: -1, + }, + want: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mi := &MapInfo{ + mapType: tt.fields.mapType, + bpfMap: tt.fields.bpfMap, + bufferSize: tt.fields.bufferSize, + innerMapType: tt.fields.innerMapType, + } + if got := mi.GetBufferSize(); got != tt.want { + t.Errorf("MapInfo.GetBufferSize() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestMapInfoType_String is function to test MapInfoType.String +func TestMapInfoType_String(t *testing.T) { + tests := []struct { + name string + mit MapInfoType + want string + }{ + { + name: "valid ringbuf", + mit: RingBuffer, + want: "RingBuffer", + }, + { + name: "valid perf", + mit: PerfEventArray, + want: "PerfEventArray", + }, + { + name: "valid array of maps", + mit: ArrayOfMaps, + want: "ArrayOfMaps", + }, + { + name: "invalid values", + mit: 100, + want: "unknown MapInfoType(100)", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.mit.String(); got != tt.want { + t.Errorf("MapInfoType.String() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_closeMapReaders is function to test closeMapReaders +func Test_closeMapReaders(t *testing.T) { + mapP := dummy_perf_map(t) + mapRb := dummy_ring_map(t) + + r, _ := ringbuf.NewReader(mapRb) + p, _ := perf.NewReader(mapP, 4096) + + type args struct { + readers []any + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid empty values", + args: args{ + readers: []any{}, + }, + wantErr: false, + }, + + { + name: "invalid values", + args: args{ + readers: []any{mapP}, + }, + wantErr: true, + }, + { + name: "valid values", + args: args{ + readers: []any{ + r, p, + }, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := closeMapReaders(tt.args.readers); (err != nil) != tt.wantErr { + t.Errorf("closeMapReaders() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/pkg/eBPF/module.go b/pkg/eBPF/module.go index e17e53e..f41f637 100644 --- a/pkg/eBPF/module.go +++ b/pkg/eBPF/module.go @@ -4,22 +4,25 @@ package ebpf import ( - "github.com/cilium/ebpf/rlimit" "github.com/intelops/tarian-detector/pkg/err" ) +var moduleErr = err.New("ebpf.module") + +// EbpfModule represents an eBPF module. type EbpfModule interface { + // GetModule is a method that returns a pointer to a Module and an error. GetModule() (*Module, error) } +// Module represents a module containing eBPF programs and maps. type Module struct { - name string - programs []*ProgramInfo - ebpfMap *MapInfo + name string // Name of the module. + programs []*ProgramInfo // Slice of eBPF program information. + ebpfMap *MapInfo // Information about the eBPF map. } -var moduleErr = err.New("ebpf.module") - +// NewModule creates a new eBPF module with the given name. func NewModule(n string) *Module { return &Module{ name: n, @@ -28,27 +31,22 @@ func NewModule(n string) *Module { } } +// AddProgram appends an eBPF program to the module's list of programs. func (m *Module) AddProgram(prog *ProgramInfo) { m.programs = append(m.programs, prog) } +// Map sets the eBPF map for the module.. func (m *Module) Map(mp *MapInfo) { m.ebpfMap = mp } +// Prepare initializes the module and returns a handler along with any errors encountered. func (m *Module) Prepare() (*Handler, error) { + // Create a new handler with the provided module name. handler := NewHandler(m.name) - err := rlimit.RemoveMemlock() - if err != nil { - return nil, moduleErr.Throwf("%v", err) - } - - /* - * - * attachs programs to the kernel hook points - * - */ + // Attach programs to the kernel hook points for _, prog := range m.programs { hook := prog.hook @@ -64,11 +62,7 @@ func (m *Module) Prepare() (*Handler, error) { handler.AddProbeLink(pL) } - /* - * - * creates map reader to receive data from kernel - * - */ + // Create map reader to receive data from the kernel if m.ebpfMap != nil { mrs, err := m.ebpfMap.CreateReaders() if err != nil { @@ -81,14 +75,17 @@ func (m *Module) Prepare() (*Handler, error) { return handler, nil } +// GetName returns the name of the Module. func (m *Module) GetName() string { return m.name } +// GetPrograms returns the slice of programs in the module. func (m *Module) GetPrograms() []*ProgramInfo { return m.programs } +// GetMap returns the ebpfMap of the Module. func (m *Module) GetMap() *MapInfo { return m.ebpfMap } diff --git a/pkg/eBPF/module_test.go b/pkg/eBPF/module_test.go new file mode 100644 index 0000000..c8a1e66 --- /dev/null +++ b/pkg/eBPF/module_test.go @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package ebpf + +import ( + "os" + "reflect" + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/perf" +) + +func dummy_perf_map(t *testing.T) *ebpf.Map { + events, err := ebpf.NewMap(&ebpf.MapSpec{ + Type: ebpf.PerfEventArray, + }) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { events.Close() }) + return events +} + +// TestNewModule tests the creation of a new Module object +func TestNewModule(t *testing.T) { + type args struct { + n string + } + tests := []struct { + name string + args args + want *Module + }{ + { + name: "default values", + args: args{ + n: "test", + }, + want: &Module{ + name: "test", + ebpfMap: nil, + programs: make([]*ProgramInfo, 0), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewModule(tt.args.n); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewModule() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestModule_AddProgram tests the AddProgram function +func TestModule_AddProgram(t *testing.T) { + type fields struct { + name string + programs []*ProgramInfo + ebpfMap *MapInfo + } + type args struct { + prog *ProgramInfo + } + tests := []struct { + name string + fields fields + args args + want int + }{ + { + name: "valid values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + args: args{ + prog: &ProgramInfo{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: true, + }, + }, + want: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + name: tt.fields.name, + programs: tt.fields.programs, + ebpfMap: tt.fields.ebpfMap, + } + + m.AddProgram(tt.args.prog) + + if len(m.programs) != tt.want { + t.Errorf("Module.AddProgram() = %v, want %v", len(m.programs), tt.want) + } + }) + } +} + +// TestModule_Map tests the Map function +func TestModule_Map(t *testing.T) { + type fields struct { + name string + programs []*ProgramInfo + ebpfMap *MapInfo + } + type args struct { + mp *MapInfo + } + tests := []struct { + name string + fields fields + args args + want *MapInfo + }{ + { + name: "valid default values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + args: args{ + mp: &MapInfo{}, + }, + want: &MapInfo{}, + }, + { + name: "nil values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + args: args{ + mp: nil, + }, + want: nil, + }, { + name: "valid values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + args: args{ + mp: NewRingBuf(nil), + }, + want: &MapInfo{ + mapType: RingBuffer, + bpfMap: nil, + innerMapType: -1, + bufferSize: 0, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + name: tt.fields.name, + programs: tt.fields.programs, + ebpfMap: tt.fields.ebpfMap, + } + m.Map(tt.args.mp) + + if !reflect.DeepEqual(m.ebpfMap, tt.want) { + t.Errorf("Module.Map() = %v, want %v", m.ebpfMap, tt.want) + } + }) + } +} + +// TestModule_GetName tests the GetName function +func TestModule_GetName(t *testing.T) { + type fields struct { + name string + programs []*ProgramInfo + ebpfMap *MapInfo + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "valid values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + want: "test", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + name: tt.fields.name, + programs: tt.fields.programs, + ebpfMap: tt.fields.ebpfMap, + } + if got := m.GetName(); got != tt.want { + t.Errorf("Module.GetName() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestModule_GetPrograms tests the GetPrograms function +func TestModule_GetPrograms(t *testing.T) { + type fields struct { + name string + programs []*ProgramInfo + ebpfMap *MapInfo + } + tests := []struct { + name string + fields fields + want []*ProgramInfo + }{ + { + name: "valid values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + want: make([]*ProgramInfo, 0), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + name: tt.fields.name, + programs: tt.fields.programs, + ebpfMap: tt.fields.ebpfMap, + } + if got := m.GetPrograms(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Module.GetPrograms() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestModule_GetMap tests the GetMap function +func TestModule_GetMap(t *testing.T) { + type fields struct { + name string + programs []*ProgramInfo + ebpfMap *MapInfo + } + tests := []struct { + name string + fields fields + want *MapInfo + }{ + { + name: "valid values", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: NewPerfEvent(nil), + }, + want: &MapInfo{ + mapType: PerfEventArray, + bpfMap: nil, + bufferSize: os.Getpagesize(), + innerMapType: -1, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + name: tt.fields.name, + programs: tt.fields.programs, + ebpfMap: tt.fields.ebpfMap, + } + if got := m.GetMap(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Module.GetMap() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestModule_Prepare tests the Prepare function +func TestModule_Prepare(t *testing.T) { + mapP := dummy_perf_map(t) + prog := dummy_kprobe_prog(t) + + type fields struct { + name string + programs []*ProgramInfo + ebpfMap *MapInfo + } + tests := []struct { + name string + fields fields + want *Handler + wantErr bool + }{ + { + name: "valid values with nil program and map", + fields: fields{ + name: "test", + programs: make([]*ProgramInfo, 0), + ebpfMap: nil, + }, + want: &Handler{ + name: "test", + }, + + wantErr: false, + }, + { + name: "valid values with dummy program and map", + fields: fields{ + name: "test", + programs: []*ProgramInfo{ + NewProgram(prog, NewHookInfo().Kprobe("vprintk")), + }, + ebpfMap: NewPerfEvent(mapP), + }, + want: &Handler{ + name: "test", + probeLinks: []link.Link{ + func() link.Link { + l, _ := link.Kprobe("vprintk", prog, nil) + return l + }(), + }, + mapReaders: []any{ + func() *perf.Reader { + r, _ := perf.NewReader(mapP, os.Getpagesize()) + return r + }(), + }, + }, + wantErr: false, + }, + { + name: "disabled program", + fields: fields{ + name: "test", + programs: []*ProgramInfo{ + NewProgram(prog, NewHookInfo().Kprobe("vprintk")).Disable(), + }, + ebpfMap: nil, + }, + want: &Handler{ + name: "test", + }, + + wantErr: false, + }, + { + name: "nil ebpf prog", + fields: fields{ + name: "test", + programs: []*ProgramInfo{ + NewProgram(prog, NewHookInfo().Kprobe("")), + }, + ebpfMap: nil, + }, + + wantErr: true, + }, + { + name: "wrong map type", + fields: fields{ + name: "test", + ebpfMap: &MapInfo{ + mapType: -1, + }, + }, + + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + name: tt.fields.name, + programs: tt.fields.programs, + ebpfMap: tt.fields.ebpfMap, + } + got, err := m.Prepare() + if (err != nil) != tt.wantErr { + t.Errorf("Module.Prepare() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if (got == nil) && (tt.wantErr) { + return + } + + if got.name != tt.want.name { + t.Errorf("Module.Prepare().name = %v, want %v", got.name, tt.want.name) + } + + if len(got.mapReaders) != len(tt.want.mapReaders) { + t.Errorf("Module.Prepare().mapReaders = %v, want %v", got.mapReaders, tt.want.mapReaders) + } + + if len(got.probeLinks) != len(tt.want.probeLinks) { + t.Errorf("Module.Prepare().probeLinks = %v, want %v", got.probeLinks, tt.want.probeLinks) + } + }) + } +} diff --git a/pkg/eBPF/program.go b/pkg/eBPF/program.go index b386840..1e7c02a 100644 --- a/pkg/eBPF/program.go +++ b/pkg/eBPF/program.go @@ -5,12 +5,14 @@ package ebpf import "github.com/cilium/ebpf" +// ProgramInfo represents information about an eBPF program. type ProgramInfo struct { - name *ebpf.Program - hook *HookInfo - shouldAttach bool + name *ebpf.Program // Pointer to the eBPF program. + hook *HookInfo // Pointer to the associated hook information. + shouldAttach bool // Indicates whether the program should be attached. } +// NewProgram creates a new ProgramInfo instance. func NewProgram(n *ebpf.Program, h *HookInfo) *ProgramInfo { return &ProgramInfo{ name: n, @@ -19,26 +21,31 @@ func NewProgram(n *ebpf.Program, h *HookInfo) *ProgramInfo { } } +// Enable sets the shouldAttach flag to true and returns the updated ProgramInfo. func (pi *ProgramInfo) Enable() *ProgramInfo { pi.shouldAttach = true return pi } +// Disable sets shouldAttach flag to false and returns the updated ProgramInfo. func (pi *ProgramInfo) Disable() *ProgramInfo { pi.shouldAttach = false return pi } +// GetHook returns the HookInfo associated with the ProgramInfo. func (pi *ProgramInfo) GetHook() *HookInfo { return pi.hook } +// GetName returns the name of the program. func (pi *ProgramInfo) GetName() *ebpf.Program { return pi.name } +// GetShouldAttach returns the value of shouldAttach for the ProgramInfo struct func (pi *ProgramInfo) GetShouldAttach() bool { return pi.shouldAttach } diff --git a/pkg/eBPF/program_test.go b/pkg/eBPF/program_test.go new file mode 100644 index 0000000..989d16f --- /dev/null +++ b/pkg/eBPF/program_test.go @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package ebpf + +import ( + "reflect" + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/link" +) + +func dummy_kprobe_prog(t *testing.T) *ebpf.Program { + prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ + Type: ebpf.Kprobe, + AttachType: 0, + AttachTo: "", + License: "MIT", + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.Return(), + }, + }) + if err != nil { + t.Fatalf("dummy_kprobe_prog: %v", err) + } + + t.Cleanup(func() { prog.Close() }) + return prog +} + +// TestNewProgram tests the creation of a new ProgramInfo object +func TestNewProgram(t *testing.T) { + type args struct { + n *ebpf.Program + h *HookInfo + } + tests := []struct { + name string + args args + want *ProgramInfo + }{ + { + name: "valid values", + args: args{ + n: &ebpf.Program{}, + h: &HookInfo{}, + }, + want: &ProgramInfo{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: true, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewProgram(tt.args.n, tt.args.h); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewProgram() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestProgramInfo_Enable tests the Enable function +func TestProgramInfo_Enable(t *testing.T) { + type fields struct { + name *ebpf.Program + hook *HookInfo + shouldAttach bool + } + tests := []struct { + name string + fields fields + want *ProgramInfo + }{ + { + name: "valid values with false", + fields: fields{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: false, + }, + want: &ProgramInfo{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: true, + }, + }, + { + name: "valid values with true", + fields: fields{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: true, + }, + want: &ProgramInfo{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: true, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pi := &ProgramInfo{ + name: tt.fields.name, + hook: tt.fields.hook, + shouldAttach: tt.fields.shouldAttach, + } + if got := pi.Enable(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ProgramInfo.Enable() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestProgramInfo_Disable tests the Disable function +func TestProgramInfo_Disable(t *testing.T) { + type fields struct { + name *ebpf.Program + hook *HookInfo + shouldAttach bool + } + tests := []struct { + name string + fields fields + want *ProgramInfo + }{ + { + name: "valid values with true", + fields: fields{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: true, + }, + want: &ProgramInfo{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: false, + }, + }, + { + name: "valid values with false", + fields: fields{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: false, + }, + want: &ProgramInfo{ + name: &ebpf.Program{}, + hook: &HookInfo{}, + shouldAttach: false, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pi := &ProgramInfo{ + name: tt.fields.name, + hook: tt.fields.hook, + shouldAttach: tt.fields.shouldAttach, + } + if got := pi.Disable(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ProgramInfo.Disable() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestProgramInfo_GetHook tests the GetHook function +func TestProgramInfo_GetHook(t *testing.T) { + type fields struct { + name *ebpf.Program + hook *HookInfo + shouldAttach bool + } + tests := []struct { + name string + fields fields + want *HookInfo + }{ + { + name: "valid values", + fields: fields{ + name: &ebpf.Program{}, + hook: NewHookInfo().Kprobe("test"), + shouldAttach: true, + }, + want: &HookInfo{ + name: "test", + group: "", + opts: &link.KprobeOptions{}, + hookType: 2, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pi := &ProgramInfo{ + name: tt.fields.name, + hook: tt.fields.hook, + shouldAttach: tt.fields.shouldAttach, + } + if got := pi.GetHook(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ProgramInfo.GetHook() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestProgramInfo_GetName tests the GetName function +func TestProgramInfo_GetName(t *testing.T) { + prog := dummy_kprobe_prog(t) + + type fields struct { + name *ebpf.Program + hook *HookInfo + shouldAttach bool + } + tests := []struct { + name string + fields fields + want *ebpf.Program + }{ + { + name: "valid nil values", + fields: fields{ + name: &ebpf.Program{}, + }, + want: &ebpf.Program{}, + }, + { + name: "valid values", + fields: fields{ + name: prog, + }, + want: prog, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pi := &ProgramInfo{ + name: tt.fields.name, + hook: tt.fields.hook, + shouldAttach: tt.fields.shouldAttach, + } + if got := pi.GetName(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ProgramInfo.GetName() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestProgramInfo_GetShouldAttach tests the GetShouldAttach function +func TestProgramInfo_GetShouldAttach(t *testing.T) { + type fields struct { + name *ebpf.Program + hook *HookInfo + shouldAttach bool + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "valid values with true", + fields: fields{ + shouldAttach: true, + }, + want: true, + }, + { + name: "valid values with false", + fields: fields{ + shouldAttach: false, + }, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pi := &ProgramInfo{ + name: tt.fields.name, + hook: tt.fields.hook, + shouldAttach: tt.fields.shouldAttach, + } + if got := pi.GetShouldAttach(); got != tt.want { + t.Errorf("ProgramInfo.GetShouldAttach() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/eventparser/context.go b/pkg/eventparser/context.go index 63fbd44..a09e305 100644 --- a/pkg/eventparser/context.go +++ b/pkg/eventparser/context.go @@ -3,110 +3,114 @@ package eventparser +// TarianMetaData represents the metadata associated with a event being received from the kernel. +// The first 755 bytes received from kernel in form of []byte are of type TarianMetaData type TarianMetaData struct { MetaData struct { - Event int32 - Nparams uint8 - Syscall int32 - Ts uint64 - Processor uint16 - Task struct { - StartTime uint64 - HostPid uint32 - HostTgid uint32 - HostPpid uint32 - Pid uint32 - Tgid uint32 - Ppid uint32 - Uid uint32 - Gid uint32 - CgroupId uint64 - MountNsId uint64 - PidNsId uint64 - ExecId uint64 - ParentExecId uint64 - Comm [16]uint8 - Cwd [256]uint8 + Event int32 // Event identifier + Nparams uint8 // Number of parameters + Syscall int32 // System call i + Ts uint64 // Timestamp + Processor uint16 // Processor number + Task struct { // Task information + StartTime uint64 // Start time + HostPid uint32 // Host process ID + HostTgid uint32 // Host thread group ID + HostPpid uint32 // Host parent process ID + Pid uint32 // Process ID of a namespace + Tgid uint32 // Thread group ID of a namespace + Ppid uint32 // Parent process ID of a namespace + Uid uint32 // User ID + Gid uint32 // Group ID + CgroupId uint64 // Cgroup ID + MountNsId uint64 // Mount namespace ID + PidNsId uint64 // Process namespace ID + ExecId uint64 // Execution ID + ParentExecId uint64 // Parent execution ID + Comm [16]uint8 // Command + Cwd [256]uint8 // Current working directory } } SystemInfo struct { - Sysname [65]uint8 - Nodename [65]uint8 - Release [65]uint8 - Version [65]uint8 - Machine [65]uint8 - Domainname [65]uint8 + Sysname [65]uint8 // System name + Nodename [65]uint8 // Node name + Release [65]uint8 // Release + Version [65]uint8 // Version + Machine [65]uint8 // Machine + Domainname [65]uint8 // Domain name } } +// TarianParamType represents the type of Tarian parameter type TarianParamType uint32 const ( - TDT_NONE TarianParamType = 0 - TDT_U8 TarianParamType = 1 - TDT_U16 TarianParamType = 2 - TDT_U32 TarianParamType = 3 - TDT_U64 TarianParamType = 4 - TDT_S8 TarianParamType = 5 - TDT_S16 TarianParamType = 6 - TDT_S32 TarianParamType = 7 - TDT_S64 TarianParamType = 8 - TDT_IPV6 TarianParamType = 9 - TDT_STR TarianParamType = 10 - TDT_STR_ARR TarianParamType = 11 - TDT_BYTE_ARR TarianParamType = 12 - TDT_IOVEC_ARR TarianParamType = 15 - TDT_SOCKADDR TarianParamType = 14 + TDT_NONE TarianParamType = 0 // TDT_NONE represents the absence of a Tarian parameter + TDT_U8 TarianParamType = 1 // TDT_U8 represents an 8-bit unsigned integer Tarian parameter + TDT_U16 TarianParamType = 2 // TDT_U16 represents a 16-bit unsigned integer Tarian parameter + TDT_U32 TarianParamType = 3 // TDT_U32 represents a 32-bit unsigned integer Tarian parameter + TDT_U64 TarianParamType = 4 // TDT_U64 represents a 64-bit unsigned integer Tarian parameter + TDT_S8 TarianParamType = 5 // TDT_S8 represents an 8-bit signed integer Tarian parameter + TDT_S16 TarianParamType = 6 // TDT_S16 represents a 16-bit signed integer Tarian parameter + TDT_S32 TarianParamType = 7 // TDT_S32 represents a 32-bit signed integer Tarian parameter + TDT_S64 TarianParamType = 8 // TDT_S64 represents a 64-bit signed integer Tarian parameter + TDT_IPV6 TarianParamType = 9 // TDT_IPV6 represents an IPv6 Tarian parameter + TDT_STR TarianParamType = 10 // TDT_STR represents a string Tarian parameter + TDT_STR_ARR TarianParamType = 11 // TDT_STR_ARR represents an array of strings Tarian parameter + TDT_BYTE_ARR TarianParamType = 12 // TDT_BYTE_ARR represents an array of bytes Tarian parameter + TDT_IOVEC_ARR TarianParamType = 15 // TDT_IOVEC_ARR represents an array of I/O vectors Tarian parameter + TDT_SOCKADDR TarianParamType = 14 // TDT_SOCKADDR represents a socket address Tarian parameter ) +// TarianEventsE represents the type for Tarian events enumeration. type TarianEventsE int const ( - TDE_SYSCALL_EXECVE_E = 2 - TDE_SYSCALL_EXECVE_R = 3 + TDE_SYSCALL_EXECVE_E TarianEventsE = 2 // TDE_SYSCALL_EXECVE_E represents the start of an execve syscall + TDE_SYSCALL_EXECVE_R TarianEventsE = 3 // TDE_SYSCALL_EXECVE_R represents the return of an execve syscall - TDE_SYSCALL_EXECVEAT_E = 4 - TDE_SYSCALL_EXECVEAT_R = 5 + TDE_SYSCALL_EXECVEAT_E TarianEventsE = 4 // TDE_SYSCALL_EXECVEAT_E represents the start of an execveat syscall + TDE_SYSCALL_EXECVEAT_R TarianEventsE = 5 // TDE_SYSCALL_EXECVEAT_R represents the return of an execveat syscall - TDE_SYSCALL_CLONE_E = 6 - TDE_SYSCALL_CLONE_R = 7 + TDE_SYSCALL_CLONE_E TarianEventsE = 6 // TDE_SYSCALL_CLONE_E represents the start of a clone syscall + TDE_SYSCALL_CLONE_R TarianEventsE = 7 // TDE_SYSCALL_CLONE_R represents the return of a clone syscall - TDE_SYSCALL_CLOSE_E = 8 - TDE_SYSCALL_CLOSE_R = 9 + TDE_SYSCALL_CLOSE_E TarianEventsE = 8 // TDE_SYSCALL_CLOSE_E represents the start of a close syscall + TDE_SYSCALL_CLOSE_R TarianEventsE = 9 // TDE_SYSCALL_CLOSE_R represents the return of a close syscall - TDE_SYSCALL_READ_E = 10 - TDE_SYSCALL_READ_R = 11 + TDE_SYSCALL_READ_E TarianEventsE = 10 // TDE_SYSCALL_READ_E represents the start of a read syscall + TDE_SYSCALL_READ_R TarianEventsE = 11 // TDE_SYSCALL_READ_R represents the return of a read syscall - TDE_SYSCALL_WRITE_E = 12 - TDE_SYSCALL_WRITE_R = 13 + TDE_SYSCALL_WRITE_E TarianEventsE = 12 // TDE_SYSCALL_WRITE_E represents the start of a write syscall + TDE_SYSCALL_WRITE_R TarianEventsE = 13 // TDE_SYSCALL_WRITE_R represents the return of a write syscall - TDE_SYSCALL_OPEN_E = 14 - TDE_SYSCALL_OPEN_R = 15 + TDE_SYSCALL_OPEN_E TarianEventsE = 14 // TDE_SYSCALL_OPEN_E represents the start of an open syscall + TDE_SYSCALL_OPEN_R TarianEventsE = 15 // TDE_SYSCALL_OPEN_R represents the return of an open syscall - TDE_SYSCALL_READV_E = 16 - TDE_SYSCALL_READV_R = 17 + TDE_SYSCALL_READV_E TarianEventsE = 16 // TDE_SYSCALL_READV_E represents the start of a readv syscall + TDE_SYSCALL_READV_R TarianEventsE = 17 // TDE_SYSCALL_READV_R represents the return of a readv syscall - TDE_SYSCALL_WRITEV_E = 18 - TDE_SYSCALL_WRITEV_R = 19 + TDE_SYSCALL_WRITEV_E TarianEventsE = 18 // TDE_SYSCALL_WRITEV_E represents the start of a writev syscall + TDE_SYSCALL_WRITEV_R TarianEventsE = 19 // TDE_SYSCALL_WRITEV_R represents the return of a writev syscall - TDE_SYSCALL_OPENAT_E = 20 - TDE_SYSCALL_OPENAT_R = 21 + TDE_SYSCALL_OPENAT_E TarianEventsE = 20 // TDE_SYSCALL_OPENAT_E represents the start of an openat syscall + TDE_SYSCALL_OPENAT_R TarianEventsE = 21 // TDE_SYSCALL_OPENAT_R represents the return of an openat syscall - TDE_SYSCALL_OPENAT2_E = 22 - TDE_SYSCALL_OPENAT2_R = 23 + TDE_SYSCALL_OPENAT2_E TarianEventsE = 22 // TDE_SYSCALL_OPENAT2_E represents the start of an openat2 syscall + TDE_SYSCALL_OPENAT2_R TarianEventsE = 23 // TDE_SYSCALL_OPENAT2_R represents the return of an openat2 syscall - TDE_SYSCALL_LISTEN_E = 24 - TDE_SYSCALL_LISTEN_R = 25 + TDE_SYSCALL_LISTEN_E TarianEventsE = 24 // TDE_SYSCALL_LISTEN_E represents the start of a listen syscall + TDE_SYSCALL_LISTEN_R TarianEventsE = 25 // TDE_SYSCALL_LISTEN_R represents the return of a listen syscall - TDE_SYSCALL_SOCKET_E = 26 - TDE_SYSCALL_SOCKET_R = 27 + TDE_SYSCALL_SOCKET_E TarianEventsE = 26 // TDE_SYSCALL_SOCKET_E represents the start of a socket syscall + TDE_SYSCALL_SOCKET_R TarianEventsE = 27 // TDE_SYSCALL_SOCKET_R represents the return of a socket syscall - TDE_SYSCALL_ACCEPT_E = 28 - TDE_SYSCALL_ACCEPT_R = 29 + TDE_SYSCALL_ACCEPT_E TarianEventsE = 28 // TDE_SYSCALL_ACCEPT_E represents the start of an accept syscall + TDE_SYSCALL_ACCEPT_R TarianEventsE = 29 // TDE_SYSCALL_ACCEPT_R represents the return of an accept syscall - TDE_SYSCALL_BIND_E = 30 - TDE_SYSCALL_BIND_R = 31 + TDE_SYSCALL_BIND_E TarianEventsE = 30 // TDE_SYSCALL_BIND_E represents the start of a bind syscall + TDE_SYSCALL_BIND_R TarianEventsE = 31 // TDE_SYSCALL_BIND_R represents the return of a bind syscall - TDE_SYSCALL_CONNECT_E = 32 - TDE_SYSCALL_CONNECT_R = 33 + TDE_SYSCALL_CONNECT_E TarianEventsE = 32 // TDE_SYSCALL_CONNECT_E represents the start of a connect syscall + TDE_SYSCALL_CONNECT_R TarianEventsE = 33 // TDE_SYSCALL_CONNECT_R represents the return of a connect syscall ) diff --git a/pkg/eventparser/parser.go b/pkg/eventparser/parser.go index d59b393..2dbd064 100644 --- a/pkg/eventparser/parser.go +++ b/pkg/eventparser/parser.go @@ -14,12 +14,17 @@ import ( var parserErr = err.New("eventparser.parser") +// TarianEventMap represents a map of Tarian events +var Events TarianEventMap + +// ByteStream represents a stream of bytes. type ByteStream struct { - data []byte - position int - nparams uint8 + data []byte // data is the array of bytes in the stream + position int // position is the current position in the stream + nparams uint8 // nparams is the number of parameters } +// NewByteStream creates a new ByteStream with the given input data and n parameters. func NewByteStream(inputData []byte, n uint8) *ByteStream { return &ByteStream{ data: inputData, @@ -28,27 +33,27 @@ func NewByteStream(inputData []byte, n uint8) *ByteStream { } } -var Events TarianEventMap - +// ParseByteArray takes a byte array as input and returns a map[string]any and an error. +// It first retrieves the eventId from the input data, then checks if the event exists in the Events map. +// It then reads the TarianMetaData from the data, updates the Syscall if needed, and parses the parameters. +// Finally, it returns the parsed record and any error encountered during parsing. func ParseByteArray(data []byte) (map[string]any, error) { - /* - Assuming a specific byte pattern within the byte array: + // Assuming a specific byte pattern within the byte array: + // tarianmetadata + params - tarianmetadata + params - */ eventId, err := getEventId(data) if err != nil { return nil, parserErr.Throwf("%v", err) } - event, noEvent := Events[eventId] + event, noEvent := Events[TarianEventsE(eventId)] if !noEvent { return nil, parserErr.Throwf("missing event from 'var Events TarianEventMap' for key: %v", eventId) } var metaData TarianMetaData lenMetaData := binary.Size(metaData) - err = binary.Read(bytes.NewReader(data[:lenMetaData]), binary.LittleEndian, &metaData) + err = binary.Read(bytes.NewReader(data), binary.LittleEndian, &metaData) if err != nil { return nil, parserErr.Throwf("%v", err) } @@ -71,6 +76,7 @@ func ParseByteArray(data []byte) (map[string]any, error) { return record, nil } +// parseParams parses the parameters of a TarianEvent from the ByteStream func (bs *ByteStream) parseParams(event TarianEvent) ([]arg, error) { tParams := event.params if len(tParams) <= 0 { @@ -80,10 +86,12 @@ func (bs *ByteStream) parseParams(event TarianEvent) ([]arg, error) { args := make([]arg, 0, bs.nparams) for i := 0; i < int(bs.nparams); i++ { + // parseParams parses the parameters of a TarianEvent from the ByteStream if bs.position >= len(bs.data) { break } + // Check if the index is out of range for tParams if i >= len(tParams) { break } @@ -99,10 +107,12 @@ func (bs *ByteStream) parseParams(event TarianEvent) ([]arg, error) { return args, nil } +// parseParam parses the given parameter based on its type and returns the parsed value. func (bs *ByteStream) parseParam(p Param) (arg, error) { var pVal any var err error + // Switch based on the parameter type switch p.paramType { case TDT_U8: pVal, err = bs.parseUint8() @@ -135,6 +145,8 @@ func (bs *ByteStream) parseParam(p Param) (arg, error) { return p.processValue(pVal) } +// parseUint8 reads an 8-bit unsigned integer from the ByteStream and returns it. +// It also increments the position by 1. func (bs *ByteStream) parseUint8() (uint8, error) { val, err := utils.Uint8(bs.data, bs.position) bs.position += 1 @@ -142,6 +154,8 @@ func (bs *ByteStream) parseUint8() (uint8, error) { return val, err } +// parseUint16 reads a 16-bit unsigned integer from the ByteStream and returns it. +// It also increments the position by 2. func (bs *ByteStream) parseUint16() (uint16, error) { val, err := utils.Uint16(bs.data, bs.position) bs.position += 2 @@ -149,6 +163,8 @@ func (bs *ByteStream) parseUint16() (uint16, error) { return val, err } +// parseUint32 reads a 32-bit unsigned integer from the ByteStream and returns it. +// It also increments the position by 4. func (bs *ByteStream) parseUint32() (uint32, error) { val, err := utils.Uint32(bs.data, bs.position) bs.position += 4 @@ -156,6 +172,8 @@ func (bs *ByteStream) parseUint32() (uint32, error) { return val, err } +// parseUint64 reads a 64-bit unsigned integer from the ByteStream and returns it. +// It also increments the position by 8. func (bs *ByteStream) parseUint64() (uint64, error) { val, err := utils.Uint64(bs.data, bs.position) bs.position += 8 @@ -163,6 +181,8 @@ func (bs *ByteStream) parseUint64() (uint64, error) { return val, err } +// parseInt8 reads an 8-bit signed integer from the ByteStream and returns it. +// It also increments the position by 1. func (bs *ByteStream) parseInt8() (int8, error) { val, err := utils.Int8(bs.data, bs.position) bs.position += 1 @@ -170,6 +190,8 @@ func (bs *ByteStream) parseInt8() (int8, error) { return val, err } +// parseInt16 reads a 16-bit signed integer from the ByteStream and returns it. +// It also increments the position by 2. func (bs *ByteStream) parseInt16() (int16, error) { val, err := utils.Int16(bs.data, bs.position) bs.position += 2 @@ -177,6 +199,8 @@ func (bs *ByteStream) parseInt16() (int16, error) { return val, err } +// parseInt32 reads a 32-bit signed integer from the ByteStream and returns it. +// It also increments the position by 4. func (bs *ByteStream) parseInt32() (int32, error) { val, err := utils.Int32(bs.data, bs.position) bs.position += 4 @@ -184,6 +208,8 @@ func (bs *ByteStream) parseInt32() (int32, error) { return val, err } +// parseInt64 reads a 64-bit signed integer from the ByteStream and returns it. +// It also increments the position by 8. func (bs *ByteStream) parseInt64() (int64, error) { val, err := utils.Int64(bs.data, bs.position) bs.position += 8 @@ -191,6 +217,8 @@ func (bs *ByteStream) parseInt64() (int64, error) { return val, err } +// parseIpv4 reads a 32-bit IPv4 address from the ByteStream and returns it. +// It also increments the position by 4. func (bs *ByteStream) parseIpv4() string { val := utils.Ipv4(bs.data, bs.position) bs.position += 4 @@ -198,6 +226,8 @@ func (bs *ByteStream) parseIpv4() string { return val } +// parseIpv6 reads a 128-bit IPv6 address from the ByteStream and returns it. +// It also increments the position by 16. func (bs *ByteStream) parseIpv6() string { val := utils.Ipv6(bs.data, bs.position) bs.position += 16 @@ -205,6 +235,8 @@ func (bs *ByteStream) parseIpv6() string { return val } +// parseString reads a string from the ByteStream and returns it. +// It also increments the position by the length of the string. func (bs *ByteStream) parseString() (string, error) { slen, err := bs.parseUint16() if err != nil { @@ -217,6 +249,8 @@ func (bs *ByteStream) parseString() (string, error) { return val, nil } +// parseRawArray reads a raw array from the ByteStream and returns it. +// It also increments the position by the length of the array. func (bs *ByteStream) parseRawArray() ([]byte, error) { slen, err := bs.parseUint16() if err != nil { @@ -229,6 +263,8 @@ func (bs *ByteStream) parseRawArray() ([]byte, error) { return val, nil } +// parseSocketAddress reads a socket address from the ByteStream and returns it. +// It also increments the position by the length of the socket address. func (bs *ByteStream) parseSocketAddress() (any, error) { family, err := bs.parseUint8() if err != nil { @@ -304,11 +340,8 @@ func (bs *ByteStream) parseSocketAddress() (any, error) { } } +// getEventId reads the eventId from the data and returns it as an int. func getEventId(data []byte) (int, error) { - if len(data) < 4 { - return 0, parserErr.Throwf("input data length is %d, expected to be at least %d", len(data), 4) - } - id, err := utils.Int32(data, 0) if err != nil { return 0, parserErr.Throwf("failed to read eventId from data: %v", err) @@ -317,6 +350,7 @@ func getEventId(data []byte) (int, error) { return int(id), nil } +// toMap converts the TarianMetaData struct to a map[string]any. func toMap(t TarianMetaData) map[string]any { m := make(map[string]any) @@ -324,7 +358,7 @@ func toMap(t TarianMetaData) map[string]any { m["syscallId"] = t.MetaData.Syscall m["processor"] = t.MetaData.Processor - // task + // task fields m["threadStartTime"] = t.MetaData.Task.StartTime m["hostProcessId"] = t.MetaData.Task.HostPid m["hostThreadId"] = t.MetaData.Task.HostTgid @@ -342,6 +376,7 @@ func toMap(t TarianMetaData) map[string]any { m["processName"] = utils.ToString(t.MetaData.Task.Comm[:], 0, len(t.MetaData.Task.Comm)) m["directory"] = utils.ToString(t.MetaData.Task.Cwd[:], 0, len(t.MetaData.Task.Cwd)) + // SystemInfo fields m["sysname"] = utils.ToString(t.SystemInfo.Sysname[:], 0, len(t.SystemInfo.Sysname)) m["nodename"] = utils.ToString(t.SystemInfo.Nodename[:], 0, len(t.SystemInfo.Nodename)) m["release"] = utils.ToString(t.SystemInfo.Release[:], 0, len(t.SystemInfo.Release)) diff --git a/pkg/eventparser/parser_test.go b/pkg/eventparser/parser_test.go new file mode 100644 index 0000000..c6b7411 --- /dev/null +++ b/pkg/eventparser/parser_test.go @@ -0,0 +1,852 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package eventparser + +import ( + "fmt" + "reflect" + "testing" +) + +// TestNewByteStream tests the NewByteStream function. +func TestNewByteStream(t *testing.T) { + type args struct { + inputData []byte + n uint8 + } + tests := []struct { + name string + args args + want *ByteStream + }{ + { + name: "valid values", + args: args{ + inputData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + n: 8, + }, + want: &ByteStream{ + data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + position: 0, + nparams: 8, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewByteStream(tt.args.inputData, tt.args.n); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewByteStream() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestParseByteArray tests the ParseByteArray function. +func TestParseByteArray(t *testing.T) { + type args struct { + data []byte + } + tests := []struct { + name string + args args + loadEvents bool + want map[string]any + wantErr bool + }{ + { + name: "buffer size lessthan 4", + args: args{ + data: []byte{1, 2, 3}, + }, + loadEvents: false, + want: nil, + wantErr: true, + }, + { + name: "invalid event id", + args: args{ + data: []byte{1, 2, 3, 4}, + }, + loadEvents: false, + want: nil, + wantErr: true, + }, + { + name: "invalid buffer", + args: args{ + data: []byte{12, 0, 0, 0, 5, 6, 7, 8, 9}, + }, + loadEvents: true, + want: nil, + wantErr: true, + }, + { + name: "error parse params", + args: args{ + data: func() []byte { + data := make([]byte, 764) + + data[0] = 12 // eventId + data[4] = 2 // nparams + + return data + }(), + }, + loadEvents: true, + want: nil, + wantErr: true, + }, + { + name: "error nparams > actual written params", + args: args{ + data: func() []byte { + data := make([]byte, 765+7) + + data[0] = 12 // eventId + data[4] = 4 // nparams + + return data + }(), + }, + loadEvents: true, + want: map[string]any{ + "eventId": "sys_write_entry", + "timestamp": uint64(0), + "syscallId": int32(1), + "processor": uint16(0), + "threadStartTime": uint64(0), + "hostProcessId": uint32(0), + "hostThreadId": uint32(0), + "hostParentProcessId": uint32(0), + "processId": uint32(0), + "threadId": uint32(0), + "parentProcessId": uint32(0), + "userId": uint32(0), + "groupId": uint32(0), + "cgroupId": uint64(0), + "mountNamespace": uint64(0), + "pidNamespace": uint64(0), + "execId": uint64(0), + "parentExecId": uint64(0), + "processName": "", + "directory": "", + "sysname": "", + "nodename": "", + "release": "", + "version": "", + "machine": "", + "domainname": "", + "context": []arg{ + { + Name: "fd", + Value: "0", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "buf", + Value: fmt.Sprintf("%v", []byte{}), + TarianType: 12, + LinuxType: "const char *", + }, + { + Name: "count", + Value: "0", + TarianType: 3, + LinuxType: "size_t", + }, + }, + }, + wantErr: false, + }, + { + name: "valid sys_write_entry record parsing", + args: args{ + data: []byte{12, 0, 0, 0, 3, 1, 0, 0, 0, 19, 2, 55, 188, 204, 13, 0, 0, 0, 0, 85, 79, 20, 171, 7, 0, 0, 0, 170, 12, 0, 0, 170, 12, 0, 0, 66, 7, 0, 0, 170, 12, 0, 0, 170, 12, 0, 0, 66, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 5, 0, 0, 0, 0, 0, 0, 1, 0, 0, 240, 0, 0, 0, 0, 252, 255, 255, 239, 0, 0, 0, 0, 85, 79, 20, 171, 175, 12, 0, 0, 17, 97, 89, 157, 70, 7, 0, 0, 102, 114, 111, 110, 116, 101, 110, 100, 0, 0, 0, 0, 0, 0, 0, 0, 47, 104, 111, 109, 101, 47, 99, 114, 97, 118, 101, 108, 97, 64, 97, 112, 112, 115, 116, 101, 107, 99, 111, 114, 112, 46, 108, 111, 99, 97, 108, 0, 47, 101, 109, 98, 101, 100, 100, 101, 100, 45, 49, 100, 49, 56, 49, 101, 99, 100, 101, 102, 51, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 76, 80, 84, 68, 65, 76, 48, 54, 51, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 46, 49, 53, 46, 48, 45, 57, 55, 45, 103, 101, 110, 101, 114, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 49, 48, 55, 126, 50, 48, 46, 48, 52, 46, 49, 45, 85, 98, 117, 110, 116, 117, 32, 83, 77, 80, 32, 70, 114, 105, 32, 70, 101, 98, 32, 57, 32, 49, 52, 58, 50, 48, 58, 49, 49, 32, 85, 84, 67, 32, 50, 48, 50, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 56, 54, 95, 54, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 110, 111, 110, 101, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 17, 0, 73, 110, 118, 97, 108, 105, 100, 32, 112, 97, 115, 115, 119, 111, 114, 100, 0, 17, 0, 0, 0}, + }, + loadEvents: true, + want: map[string]any{ + "eventId": "sys_write_entry", + "timestamp": uint64(15172982211091), + "syscallId": int32(1), + "processor": uint16(0), + "threadStartTime": uint64(32935006037), + "hostProcessId": uint32(3242), + "hostThreadId": uint32(3242), + "hostParentProcessId": uint32(1858), + "processId": uint32(3242), + "threadId": uint32(3242), + "parentProcessId": uint32(1858), + "userId": uint32(0), + "groupId": uint32(0), + "cgroupId": uint64(1366), + "mountNamespace": uint64(4026531841), + "pidNamespace": uint64(4026531836), + "execId": uint64(13948629045077), + "parentExecId": uint64(7999868985617), + "processName": "frontend", + "directory": string([]byte{47, 104, 111, 109, 101, 47, 99, 114, 97, 118, 101, 108, 97, 64, 97, 112, 112, 115, 116, 101, 107, 99, 111, 114, 112, 46, 108, 111, 99, 97, 108, 0, 47, 101, 109, 98, 101, 100, 100, 101, 100, 45, 49, 100, 49, 56, 49, 101, 99, 100, 101, 102, 51, 102}), + "sysname": "Linux", + "nodename": "ACLPTDAL0631", + "release": "5.15.0-97-generic", + "version": "#107~20.04.1-Ubuntu SMP Fri Feb 9 14:20:11 UTC 2024", + "machine": "x86_64", + "domainname": "(none)", + "context": []arg{ + { + Name: "fd", + Value: "1", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "buf", + Value: fmt.Sprintf("%v", []byte{73, 110, 118, 97, 108, 105, 100, 32, 112, 97, 115, 115, 119, 111, 114, 100, 0}), + TarianType: 12, + LinuxType: "const char *", + }, + { + Name: "count", + Value: "17", + TarianType: 3, + LinuxType: "size_t", + }, + }, + }, + wantErr: false, + }, + { + name: "valid sys_socket_entry record parsing", + loadEvents: true, + wantErr: false, + args: args{ + data: []byte{26, 0, 0, 0, 3, 41, 0, 0, 0, 81, 74, 169, 30, 184, 30, 0, 0, 15, 0, 69, 7, 11, 28, 5, 1, 0, 0, 191, 227, 36, 0, 191, 227, 36, 0, 173, 200, 0, 0, 191, 227, 36, 0, 191, 227, 36, 0, 10, 205, 0, 0, 24, 197, 143, 36, 193, 191, 143, 36, 182, 14, 0, 0, 0, 0, 0, 0, 1, 0, 0, 240, 0, 0, 0, 0, 252, 255, 255, 239, 0, 0, 0, 0, 69, 7, 11, 28, 191, 227, 36, 0, 51, 160, 181, 22, 173, 200, 0, 0, 99, 111, 100, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 104, 111, 109, 101, 47, 99, 114, 97, 118, 101, 108, 97, 64, 97, 112, 112, 115, 116, 101, 107, 99, 111, 114, 112, 46, 108, 111, 99, 97, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 76, 80, 84, 68, 65, 76, 48, 54, 51, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 46, 49, 53, 46, 48, 45, 57, 55, 45, 103, 101, 110, 101, 114, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 49, 48, 55, 126, 50, 48, 46, 48, 52, 46, 49, 45, 85, 98, 117, 110, 116, 117, 32, 83, 77, 80, 32, 70, 114, 105, 32, 70, 101, 98, 32, 57, 32, 49, 52, 58, 50, 48, 58, 49, 49, 32, 85, 84, 67, 32, 50, 48, 50, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 56, 54, 95, 54, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 110, 111, 110, 101, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + want: map[string]any{ + "eventId": "sys_socket_entry", + "timestamp": uint64(33776137226833), + "syscallId": int32(41), + "processor": uint16(15), + "threadStartTime": uint64(1121456949061), + "hostProcessId": uint32(2417599), + "hostThreadId": uint32(2417599), + "hostParentProcessId": uint32(51373), + "processId": uint32(2417599), + "threadId": uint32(2417599), + "parentProcessId": uint32(52490), + "userId": uint32(613401880), + "groupId": uint32(613400513), + "cgroupId": uint64(3766), + "mountNamespace": uint64(4026531841), + "pidNamespace": uint64(4026531836), + "execId": uint64(10383509110327109), + "parentExecId": uint64(220645735899187), + "processName": "code", + "directory": "/home/cravela@appstekcorp.local", + "sysname": "Linux", + "nodename": "ACLPTDAL0631", + "release": "5.15.0-97-generic", + "version": "#107~20.04.1-Ubuntu SMP Fri Feb 9 14:20:11 UTC 2024", + "machine": "x86_64", + "domainname": "(none)", + "context": []arg{ + { + Name: "family", + Value: "AF_INET", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "type", + Value: "SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "protocol", + Value: "IPPROTO_IP", + TarianType: 7, + LinuxType: "int", + }, + }, + }, + }, + { + name: "valid sys_bind_entry record parsing family AF_INET6", + loadEvents: true, + wantErr: false, + args: args{ + data: []byte{30, 0, 0, 0, 3, 49, 0, 0, 0, 34, 193, 57, 22, 40, 33, 0, 0, 4, 0, 71, 195, 17, 22, 40, 33, 0, 0, 199, 125, 41, 0, 199, 125, 41, 0, 72, 45, 1, 0, 199, 125, 41, 0, 199, 125, 41, 0, 72, 45, 1, 0, 24, 197, 143, 36, 193, 191, 143, 36, 246, 14, 0, 0, 0, 0, 0, 0, 1, 0, 0, 240, 0, 0, 0, 0, 252, 255, 255, 239, 0, 0, 0, 0, 71, 195, 17, 22, 239, 125, 41, 0, 250, 174, 10, 131, 89, 45, 1, 0, 98, 105, 110, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 104, 111, 109, 101, 47, 99, 114, 97, 118, 101, 108, 97, 64, 97, 112, 112, 115, 116, 101, 107, 99, 111, 114, 112, 46, 108, 111, 99, 97, 108, 47, 80, 114, 111, 106, 101, 99, 116, 115, 47, 68, 79, 85, 66, 76, 69, 47, 68, 69, 76, 69, 84, 69, 47, 68, 69, 76, 69, 84, 69, 47, 116, 97, 114, 105, 97, 110, 45, 100, 101, 116, 101, 99, 116, 111, 114, 47, 112, 107, 103, 47, 101, 66, 80, 70, 47, 99, 47, 98, 112, 102, 47, 110, 101, 116, 119, 111, 114, 107, 95, 98, 105, 110, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 76, 80, 84, 68, 65, 76, 48, 54, 51, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 46, 49, 53, 46, 48, 45, 57, 55, 45, 103, 101, 110, 101, 114, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 49, 48, 55, 126, 50, 48, 46, 48, 52, 46, 49, 45, 85, 98, 117, 110, 116, 117, 32, 83, 77, 80, 32, 70, 114, 105, 32, 70, 101, 98, 32, 57, 32, 49, 52, 58, 50, 48, 58, 49, 49, 32, 85, 84, 67, 32, 50, 48, 50, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 56, 54, 95, 54, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 110, 111, 110, 101, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 31, 144, 28, 0, 0, 0}, + }, + want: map[string]any{ + "eventId": "sys_bind_entry", + "timestamp": uint64(36456055292194), + "syscallId": int32(49), + "processor": uint16(4), + "threadStartTime": uint64(36456052671303), + "hostProcessId": uint32(2719175), + "hostThreadId": uint32(2719175), + "hostParentProcessId": uint32(77128), + "processId": uint32(2719175), + "threadId": uint32(2719175), + "parentProcessId": uint32(77128), + "userId": uint32(613401880), + "groupId": uint32(613400513), + "cgroupId": uint64(3830), + "mountNamespace": uint64(4026531841), + "pidNamespace": uint64(4026531836), + "execId": uint64(11678939866055495), + "parentExecId": uint64(331337450565370), + "processName": "bind", + "directory": "/home/cravela@appstekcorp.local/Projects/DOUBLE/DELETE/DELETE/tarian-detector/pkg/eBPF/c/bpf/network_bind", + "sysname": "Linux", + "nodename": "ACLPTDAL0631", + "release": "5.15.0-97-generic", + "version": "#107~20.04.1-Ubuntu SMP Fri Feb 9 14:20:11 UTC 2024", + "machine": "x86_64", + "domainname": "(none)", + "context": []arg{ + { + Name: "fd", + Value: "3", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "umyaddr", + Value: fmt.Sprintf("%+v", struct { + Family string + Sa_addr string + Sa_port uint16 + }{Family: "AF_INET6", Sa_addr: "::1", Sa_port: 8080}), + TarianType: 14, + LinuxType: "struct sockaddr *", + }, + {Name: "addrlen", Value: "28", TarianType: 7, LinuxType: "int"}, + }, + }, + }, + { + name: "valid sys_bind_entry record parsing family AF_INET", + loadEvents: true, + wantErr: false, + args: args{ + data: []byte{30, 0, 0, 0, 3, 49, 0, 0, 0, 176, 136, 54, 5, 54, 34, 0, 0, 6, 0, 114, 61, 18, 5, 54, 34, 0, 0, 157, 39, 17, 0, 157, 39, 17, 0, 72, 45, 1, 0, 157, 39, 17, 0, 157, 39, 17, 0, 72, 45, 1, 0, 24, 197, 143, 36, 193, 191, 143, 36, 246, 14, 0, 0, 0, 0, 0, 0, 1, 0, 0, 240, 0, 0, 0, 0, 252, 255, 255, 239, 0, 0, 0, 0, 114, 61, 18, 5, 191, 39, 17, 0, 250, 174, 10, 131, 89, 45, 1, 0, 98, 105, 110, 100, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 104, 111, 109, 101, 47, 99, 114, 97, 118, 101, 108, 97, 64, 97, 112, 112, 115, 116, 101, 107, 99, 111, 114, 112, 46, 108, 111, 99, 97, 108, 47, 80, 114, 111, 106, 101, 99, 116, 115, 47, 68, 79, 85, 66, 76, 69, 47, 68, 69, 76, 69, 84, 69, 47, 68, 69, 76, 69, 84, 69, 47, 116, 97, 114, 105, 97, 110, 45, 100, 101, 116, 101, 99, 116, 111, 114, 47, 112, 107, 103, 47, 101, 66, 80, 70, 47, 99, 47, 98, 112, 102, 47, 110, 101, 116, 119, 111, 114, 107, 95, 98, 105, 110, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 76, 80, 84, 68, 65, 76, 48, 54, 51, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 46, 49, 53, 46, 48, 45, 57, 55, 45, 103, 101, 110, 101, 114, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 49, 48, 55, 126, 50, 48, 46, 48, 52, 46, 49, 45, 85, 98, 117, 110, 116, 117, 32, 83, 77, 80, 32, 70, 114, 105, 32, 70, 101, 98, 32, 57, 32, 49, 52, 58, 50, 48, 58, 49, 49, 32, 85, 84, 67, 32, 50, 48, 50, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 56, 54, 95, 54, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 110, 111, 110, 101, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 31, 144, 16, 0, 0, 0, 0, 0, 0, 0}, + }, + want: map[string]any{ + "eventId": "sys_bind_entry", + "timestamp": uint64(37615411038384), + "syscallId": int32(49), + "processor": uint16(6), + "threadStartTime": uint64(37615408659826), + "hostProcessId": uint32(1124253), + "hostThreadId": uint32(1124253), + "hostParentProcessId": uint32(77128), + "processId": uint32(1124253), + "threadId": uint32(1124253), + "parentProcessId": uint32(77128), + "userId": uint32(613401880), + "groupId": uint32(613400513), + "cgroupId": uint64(3830), + "mountNamespace": uint64(4026531841), + "pidNamespace": uint64(4026531836), + "execId": uint64(4828775981399410), + "parentExecId": uint64(331337450565370), + "processName": "bind4", + "directory": "/home/cravela@appstekcorp.local/Projects/DOUBLE/DELETE/DELETE/tarian-detector/pkg/eBPF/c/bpf/network_bind", + "sysname": "Linux", + "nodename": "ACLPTDAL0631", + "release": "5.15.0-97-generic", + "version": "#107~20.04.1-Ubuntu SMP Fri Feb 9 14:20:11 UTC 2024", + "machine": "x86_64", + "domainname": "(none)", + "context": []arg{ + { + Name: "fd", + Value: "3", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "umyaddr", + Value: fmt.Sprintf("%+v", struct { + Family string + Sa_addr string + Sa_port uint16 + }{Family: "AF_INET", Sa_addr: "0.0.0.0", Sa_port: 8080}), + TarianType: 14, + LinuxType: "struct sockaddr *", + }, + {Name: "addrlen", Value: "16", TarianType: 7, LinuxType: "int"}, + }, + }, + }, + { + name: "valid sys_bind_entry record parsing family AF_UNIX", + loadEvents: true, + wantErr: false, + args: args{ + data: []byte{30, 0, 0, 0, 3, 49, 0, 0, 0, 85, 21, 60, 167, 192, 34, 0, 0, 2, 0, 146, 158, 19, 167, 192, 34, 0, 0, 7, 172, 37, 0, 7, 172, 37, 0, 16, 221, 29, 0, 7, 172, 37, 0, 7, 172, 37, 0, 16, 221, 29, 0, 24, 197, 143, 36, 193, 191, 143, 36, 198, 31, 0, 0, 0, 0, 0, 0, 1, 0, 0, 240, 0, 0, 0, 0, 252, 255, 255, 239, 0, 0, 0, 0, 146, 158, 19, 167, 199, 174, 37, 0, 39, 14, 153, 126, 157, 255, 29, 0, 117, 110, 105, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 104, 111, 109, 101, 47, 99, 114, 97, 118, 101, 108, 97, 64, 97, 112, 112, 115, 116, 101, 107, 99, 111, 114, 112, 46, 108, 111, 99, 97, 108, 47, 80, 114, 111, 106, 101, 99, 116, 115, 47, 68, 79, 85, 66, 76, 69, 47, 68, 69, 76, 69, 84, 69, 47, 68, 69, 76, 69, 84, 69, 47, 116, 97, 114, 105, 97, 110, 45, 100, 101, 116, 101, 99, 116, 111, 114, 47, 112, 107, 103, 47, 101, 66, 80, 70, 47, 99, 47, 98, 112, 102, 47, 110, 101, 116, 119, 111, 114, 107, 95, 98, 105, 110, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 76, 80, 84, 68, 65, 76, 48, 54, 51, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 46, 49, 53, 46, 48, 45, 57, 55, 45, 103, 101, 110, 101, 114, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 49, 48, 55, 126, 50, 48, 46, 48, 52, 46, 49, 45, 85, 98, 117, 110, 116, 117, 32, 83, 77, 80, 32, 70, 114, 105, 32, 70, 101, 98, 32, 57, 32, 49, 52, 58, 50, 48, 58, 49, 49, 32, 85, 84, 67, 32, 50, 48, 50, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 56, 54, 95, 54, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 110, 111, 110, 101, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 21, 0, 47, 116, 109, 112, 47, 109, 121, 95, 117, 110, 105, 120, 95, 115, 111, 99, 107, 101, 116, 50, 0, 110, 0, 0, 0, 0, 0, 0}, + }, + want: map[string]any{ + "eventId": "sys_bind_entry", + "timestamp": uint64(38210834797909), + "syscallId": int32(49), + "processor": uint16(2), + "threadStartTime": uint64(38210832146066), + "hostProcessId": uint32(2468871), + "hostThreadId": uint32(2468871), + "hostParentProcessId": uint32(1957136), + "processId": uint32(2468871), + "threadId": uint32(2468871), + "parentProcessId": uint32(1957136), + "userId": uint32(613401880), + "groupId": uint32(613400513), + "cgroupId": uint64(8134), + "mountNamespace": uint64(4026531841), + "pidNamespace": uint64(4026531836), + "execId": uint64(10606746663100050), + "parentExecId": uint64(8443826223517223), + "processName": "unix", + "directory": "/home/cravela@appstekcorp.local/Projects/DOUBLE/DELETE/DELETE/tarian-detector/pkg/eBPF/c/bpf/network_bind", + "sysname": "Linux", + "nodename": "ACLPTDAL0631", + "release": "5.15.0-97-generic", + "version": "#107~20.04.1-Ubuntu SMP Fri Feb 9 14:20:11 UTC 2024", + "machine": "x86_64", + "domainname": "(none)", + "context": []arg{ + { + Name: "fd", + Value: "3", + TarianType: 7, + LinuxType: "int", + }, + { + Name: "umyaddr", + Value: fmt.Sprintf("%+v", struct { + Family string + Sun_path string + }{Family: "AF_UNIX", Sun_path: "/tmp/my_unix_socket2"}), + TarianType: 14, + LinuxType: "struct sockaddr *", + }, + {Name: "addrlen", Value: "110", TarianType: 7, LinuxType: "int"}, + }, + }, + }, + } + + for _, tt := range tests { + if tt.loadEvents { + LoadTarianEvents() + } + + t.Run(tt.name, func(t *testing.T) { + got, err := ParseByteArray(tt.args.data) + if (err != nil) != tt.wantErr { + t.Errorf("ParseByteArray() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseByteArray() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestByteStream_parseParams tests the parseParams function. +func TestByteStream_parseParams(t *testing.T) { + type args struct { + event TarianEvent + } + tests := []struct { + name string + fields ByteStream + args args + want []arg + wantErr bool + }{ + { + name: "empty tarian event", + wantErr: true, + }, { + name: "break bs.position >= len(bs.data)", + fields: ByteStream{ + position: 0, + nparams: 2, + }, + args: args{ + event: TarianEvent{ + name: "test", + syscallId: 0, + eventSize: 783, + params: []Param{ + Param{}, Param{}, + }, + }, + }, + want: []arg{}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bs := &ByteStream{ + data: tt.fields.data, + position: tt.fields.position, + nparams: tt.fields.nparams, + } + got, err := bs.parseParams(tt.args.event) + if (err != nil) != tt.wantErr { + t.Errorf("ByteStream.parseParams() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ByteStream.parseParams() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestByteStream_parseParam tests the parseParam function. +func TestByteStream_parseParam(t *testing.T) { + type args struct { + p Param + } + tests := []struct { + name string + fields ByteStream + args args + want arg + wantErr bool + }{ + { + name: "call Uint8", + fields: ByteStream{ + data: []byte{1, 2, 3}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_U8, + linuxType: "uint8_t", + }, + }, + want: arg{ + Name: "test", + Value: "1", + TarianType: 1, + LinuxType: "uint8_t", + }, + wantErr: false, + }, + { + name: "call Uint16", + fields: ByteStream{ + data: []byte{1, 2, 3}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_U16, + linuxType: "uint16_t", + }, + }, + want: arg{ + Name: "test", + Value: "513", + TarianType: 2, + LinuxType: "uint16_t", + }, + wantErr: false, + }, + { + name: "call Uint64", + fields: ByteStream{ + data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_U64, + linuxType: "uint64_t", + }, + }, + want: arg{ + Name: "test", + Value: "578437695752307201", + TarianType: 4, + LinuxType: "uint64_t", + }, + wantErr: false, + }, + { + name: "call Int8", + fields: ByteStream{ + data: []byte{1, 2, 3}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_S8, + linuxType: "int8_t", + }, + }, + want: arg{"test", "1", 5, "int8_t"}, + wantErr: false, + }, + { + name: "call Int16", + fields: ByteStream{ + data: []byte{1, 2, 3}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_S16, + linuxType: "int16_t", + }, + }, + want: arg{"test", "513", 6, "int16_t"}, + wantErr: false, + }, + { + name: "call Int64", + fields: ByteStream{ + data: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_S64, + linuxType: "int64_t", + }, + }, + want: arg{"test", "578437695752307201", 8, "int64_t"}, + wantErr: false, + }, + { + name: "call String", + fields: ByteStream{ + data: []byte{4, 0, 65, 66, 67, 68}, + position: 0, + nparams: 2, + }, + args: args{ + p: Param{ + name: "test", + paramType: TDT_STR, + linuxType: "string", + }, + }, + want: arg{"test", "ABCD", 10, "string"}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bs := &ByteStream{ + data: tt.fields.data, + position: tt.fields.position, + nparams: tt.fields.nparams, + } + got, err := bs.parseParam(tt.args.p) + if (err != nil) != tt.wantErr { + t.Errorf("ByteStream.parseParam() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ByteStream.parseParam() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestByteStream_parseString tests the parseString function. +func TestByteStream_parseString(t *testing.T) { + type fields struct { + data []byte + position int + nparams uint8 + } + tests := []struct { + name string + fields fields + want string + wantErr bool + }{ + { + name: "invalid values", + fields: fields{ + data: []byte{1}, + position: 0, + nparams: 2, + }, + want: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bs := &ByteStream{ + data: tt.fields.data, + position: tt.fields.position, + nparams: tt.fields.nparams, + } + got, err := bs.parseString() + if (err != nil) != tt.wantErr { + t.Errorf("ByteStream.parseString() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ByteStream.parseString() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestByteStream_parseRawArray tests the parseRawArray function. +func TestByteStream_parseRawArray(t *testing.T) { + type fields struct { + data []byte + position int + nparams uint8 + } + tests := []struct { + name string + fields fields + want []byte + wantErr bool + }{ + { + name: "invalid values", + fields: fields{ + data: []byte{1}, + position: 0, + nparams: 2, + }, + want: []byte{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bs := &ByteStream{ + data: tt.fields.data, + position: tt.fields.position, + nparams: tt.fields.nparams, + } + got, err := bs.parseRawArray() + if (err != nil) != tt.wantErr { + t.Errorf("ByteStream.parseRawArray() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ByteStream.parseRawArray() = %v, want %v", got, tt.want) + } + }) + } +} + +// TestByteStream_parseSocketAddress tests the parseSocketAddress function. +func TestByteStream_parseSocketAddress(t *testing.T) { + type fields struct { + data []byte + position int + nparams uint8 + } + tests := []struct { + name string + fields fields + want any + wantErr bool + }{ + { + name: "invalid values", + fields: fields{ + data: []byte{}, + position: 0, + nparams: 2, + }, + want: nil, + wantErr: true, + }, + { + name: "unhandled case", + fields: fields{ + data: []byte{0}, + position: 0, + nparams: 2, + }, + want: nil, + wantErr: false, + }, + { + name: "AF_INET Uint16 error", + fields: fields{ + data: []byte{2, 8, 9, 0, 3, 1}, + position: 0, + nparams: 2, + }, + want: nil, + wantErr: true, + }, + { + name: "AF_INET6 Uint16 error", + fields: fields{ + data: []byte{10, 8, 9, 0, 3, 1}, + position: 0, + nparams: 2, + }, + want: nil, + wantErr: true, + }, + { + name: "AF_UNIX Uint16 error", + fields: fields{ + data: []byte{1, 8}, + position: 0, + nparams: 2, + }, + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bs := &ByteStream{ + data: tt.fields.data, + position: tt.fields.position, + nparams: tt.fields.nparams, + } + got, err := bs.parseSocketAddress() + if (err != nil) != tt.wantErr { + t.Errorf("ByteStream.parseSocketAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ByteStream.parseSocketAddress() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/eventparser/probes.go b/pkg/eventparser/probes.go index 4a5e3a9..8baf01c 100644 --- a/pkg/eventparser/probes.go +++ b/pkg/eventparser/probes.go @@ -11,33 +11,39 @@ import ( var probesErr = err.New("eventparser.probes") +// arg represents an argument with its name, value, and types for Tarian and Linux. type arg struct { - Name string - Value string - TarianType uint32 - LinuxType string + Name string // Name is the name of the argument + Value string // Value is the value of the argument + TarianType uint32 // TarianType is the Tarian type of the argument + LinuxType string // LinuxType is the Linux type of the argumen } +// Param represents a parameter with its name, type, Linux type, and processing function type Param struct { - name string - paramType TarianParamType - linuxType string - function func(any) (string, error) + name string // name of the parameter + paramType TarianParamType // type of the parameter + linuxType string // Linux type of the parameter + function func(any) (string, error) // function to process the parameter } +// TarianEvent represents an event with a name, syscall ID, event size, and parameters. type TarianEvent struct { - name string - syscallId int - eventSize uint32 - params []Param + name string // name of the event + syscallId int // syscall ID + eventSize uint32 // size of the event + params []Param // parameters of the event } -type TarianEventMap map[int]TarianEvent +// TarianEventMap is a map type that maps TarianEventsE to TarianEvent +type TarianEventMap map[TarianEventsE]TarianEvent -func (te TarianEventMap) AddTarianEvent(idx int, event TarianEvent) { +// AddTarianEvent adds a TarianEvent to the TarianEventMap at the specified index. +func (te TarianEventMap) AddTarianEvent(idx TarianEventsE, event TarianEvent) { te[idx] = event } +// NewTarianEvent creates a new TarianEvent with the given id, name, size, and params. func NewTarianEvent(id int, name string, size uint32, params ...Param) TarianEvent { return TarianEvent{ name: name, @@ -47,10 +53,12 @@ func NewTarianEvent(id int, name string, size uint32, params ...Param) TarianEve } } +// LoadTarianEvents loads the Tarian events into 'Events' variable by generating them using GenerateTarianEvents function func LoadTarianEvents() { Events = GenerateTarianEvents() } +// GenerateTarianEvents creates and returns a TarianEventMap func GenerateTarianEvents() TarianEventMap { events := make(TarianEventMap) @@ -254,9 +262,11 @@ func GenerateTarianEvents() TarianEventMap { return events } +// processValue processes the value and returns the argument and an error, if any. func (p *Param) processValue(val interface{}) (arg, error) { arg := arg{} + // If a function is provided, call it with the value and handle the parsed value and error if p.function != nil { parsedValue, err := p.function(val) if err != nil { diff --git a/pkg/eventparser/probes_test.go b/pkg/eventparser/probes_test.go new file mode 100644 index 0000000..d32e97b --- /dev/null +++ b/pkg/eventparser/probes_test.go @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package eventparser + +import ( + "fmt" + "reflect" + "testing" +) + +// TestLoadTarianEvents tests the LoadTarianEvents function. It ensures that it correctly loads all events. +func TestLoadTarianEvents(t *testing.T) { + tests := []struct { + name string + }{{name: "valid case"}} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + LoadTarianEvents() + + if len(Events) != 32 { + t.Errorf("LoadTarianEvents() = %v, want %v", len(Events), 32) + } + }) + } +} + +// TestParam_processValue tests the processValue function. +func TestParam_processValue(t *testing.T) { + type fields struct { + name string + paramType TarianParamType + linuxType string + function func(any) (string, error) + } + type args struct { + val interface{} + } + tests := []struct { + name string + fields fields + args args + want arg + wantErr bool + }{ + { + name: "function error", + fields: fields{ + name: "test", + paramType: TDT_S32, + linuxType: "int", + function: func(v interface{}) (string, error) { + return "", fmt.Errorf("test") + }, + }, + args: args{ + val: 123, + }, + want: arg{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Param{ + name: tt.fields.name, + paramType: tt.fields.paramType, + linuxType: tt.fields.linuxType, + function: tt.fields.function, + } + got, err := p.processValue(tt.args.val) + if (err != nil) != tt.wantErr { + t.Errorf("Param.processValue() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Param.processValue() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/eventparser/transform.go b/pkg/eventparser/transform.go index de67297..4802fd2 100644 --- a/pkg/eventparser/transform.go +++ b/pkg/eventparser/transform.go @@ -13,25 +13,27 @@ import ( var transformErr = err.New("eventparser.transform") const ( - AT_FDCWD = -100 - AT_SYMLINK_FOLLOW = 0x400 - AT_SYMLINK_NOFOLLOW = 0x100 - AT_REMOVEDIR = 0x200 - AT_NO_AUTOMOUNT = 0x800 - AT_EMPTY_PATH = 0x1000 - AT_STATX_SYNC_TYPE = 0x6000 - AT_STATX_SYNC_AS_STAT = 0x0000 - AT_STATX_FORCE_SYNC = 0x2000 - AT_STATX_DONT_SYNC = 0x4000 - AT_RECURSIVE = 0x8000 - AT_EACCESS = 0x200 + AT_FDCWD = -100 // Special value indicating the *at functions should use the current working directory. + AT_SYMLINK_FOLLOW = 0x400 // Follow symbolic links. + AT_SYMLINK_NOFOLLOW = 0x100 // Do not follow symbolic links. + AT_REMOVEDIR = 0x200 // Remove directory instead of unlinking file. + AT_NO_AUTOMOUNT = 0x800 // Suppress terminal automount traversal. + AT_EMPTY_PATH = 0x1000 // Used for an empty pathname. + AT_STATX_SYNC_TYPE = 0x6000 // Synchronization type for querying file attributes. + AT_STATX_SYNC_AS_STAT = 0x0000 // Synchronize as stat (default behavior). + AT_STATX_FORCE_SYNC = 0x2000 // Force synchronization. + AT_STATX_DONT_SYNC = 0x4000 // Do not synchronize. + AT_RECURSIVE = 0x8000 // Recursive behavior. + AT_EACCESS = 0x200 // Test access permitted for effective IDs, not real IDs. ) var execveatDird = map[int32]string{ - AT_FDCWD: "AT_FDCWD", - AT_EMPTY_PATH: "AT_EMPTY_PATH", + AT_FDCWD: "AT_FDCWD", // Special value for current working directory + AT_EMPTY_PATH: "AT_EMPTY_PATH", // Empty pathname } +// parseExecveatDird parses the provided value and returns a descriptive string +// corresponding to the given integer constant. func parseExecveatDird(dird any) (string, error) { d, ok := dird.(int32) if !ok { @@ -50,10 +52,12 @@ var execveatFlag = []struct { flag int32 name string }{ - {AT_EMPTY_PATH, "AT_EMPTY_PATH"}, - {AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW"}, + {AT_EMPTY_PATH, "AT_EMPTY_PATH"}, // Flag for an empty pathname + {AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW"}, // Avoid following symbolic links } +// parseExecveatFlags parses the given flag value and returns a string representation +// of the corresponding flags based on the execveatFlag definitions. func parseExecveatFlags(flag any) (string, error) { f, ok := flag.(int32) if !ok { @@ -74,34 +78,36 @@ func parseExecveatFlags(flag any) (string, error) { return strings.Join(fs, "|"), nil } +// Constants representing various clone flags used in system calls. const ( - CSIGNAL = 0x000000ff - CLONE_VM = 0x00000100 - CLONE_FS = 0x00000200 - CLONE_FILES = 0x00000400 - CLONE_SIGHAND = 0x00000800 - CLONE_PIDFD = 0x00001000 - CLONE_PTRACE = 0x00002000 - CLONE_VFORK = 0x00004000 - CLONE_PARENT = 0x00008000 - CLONE_THREAD = 0x00010000 - CLONE_NEWNS = 0x00020000 - CLONE_SYSVSEM = 0x00040000 - CLONE_SETTLS = 0x00080000 - CLONE_PARENT_SETTID = 0x00100000 - CLONE_CHILD_CLEARTID = 0x00200000 - CLONE_DETACHED = 0x00400000 - CLONE_UNTRACED = 0x00800000 - CLONE_CHILD_SETTID = 0x01000000 - CLONE_NEWCGROUP = 0x02000000 - CLONE_NEWUTS = 0x04000000 - CLONE_NEWIPC = 0x08000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWNET = 0x40000000 - CLONE_IO = 0x80000000 + CSIGNAL = 0x000000ff // Signal mask to be sent at exit. + CLONE_VM = 0x00000100 // Share memory space with the parent process. + CLONE_FS = 0x00000200 // Share filesystem information. + CLONE_FILES = 0x00000400 // Share file descriptors. + CLONE_SIGHAND = 0x00000800 // Share signal handlers. + CLONE_PIDFD = 0x00001000 // Use pidfd instead of child's PID. + CLONE_PTRACE = 0x00002000 // Allow tracing of child processes. + CLONE_VFORK = 0x00004000 // Create a new process but share memory until exec. + CLONE_PARENT = 0x00008000 // Set parent process ID to the calling process. + CLONE_THREAD = 0x00010000 // Create a thread (shared memory, signal handlers, etc.). + CLONE_NEWNS = 0x00020000 // Create a new mount namespace. + CLONE_SYSVSEM = 0x00040000 // Share System V semaphores. + CLONE_SETTLS = 0x00080000 // Set TLS (Thread-Local Storage) for the child. + CLONE_PARENT_SETTID = 0x00100000 // Set the parent's TID (Thread ID). + CLONE_CHILD_CLEARTID = 0x00200000 // Clear the TID in the child. + CLONE_DETACHED = 0x00400000 // Create a detached thread. + CLONE_UNTRACED = 0x00800000 // Do not report the child's status to the parent. + CLONE_CHILD_SETTID = 0x01000000 // Set the child's TID. + CLONE_NEWCGROUP = 0x02000000 // Create a new cgroup namespace. + CLONE_NEWUTS = 0x04000000 // Create a new UTS (hostname) namespace. + CLONE_NEWIPC = 0x08000000 // Create a new IPC namespace. + CLONE_NEWUSER = 0x10000000 // Create a new user namespace. + CLONE_NEWPID = 0x20000000 // Create a new PID namespace. + CLONE_NEWNET = 0x40000000 // Create a new network namespace. + CLONE_IO = 0x80000000 // Clone I/O context. ) +// cloneFlags represents various clone flags used in system calls. var cloneFlags = map[uint64]string{ CSIGNAL: "CSIGNAL", CLONE_VM: "CLONE_VM", @@ -130,6 +136,8 @@ var cloneFlags = map[uint64]string{ CLONE_IO: "CLONE_IO", } +// parseCloneFlags parses the given flag value and returns a string representation +// of the corresponding flags based on the cloneFlags definitions. func parseCloneFlags(flag any) (string, error) { f, ok := flag.(uint64) if !ok { @@ -145,10 +153,7 @@ func parseCloneFlags(flag any) (string, error) { } if f > 0 { - sigs, err := parseSignal(uint16(f)) - if err != nil { - return fmt.Sprintf("%v", f), err - } + sigs := parseSignal(uint16(f)) fs = append(fs, sigs) } @@ -160,49 +165,52 @@ func parseCloneFlags(flag any) (string, error) { return strings.Join(fs, "|"), nil } +// signalMap represents various signal numbers and their corresponding names. var signalMap = map[uint16]string{ - 1: "SIGHUP", - 2: "SIGINT", - 3: "SIGQUIT", - 4: "SIGILL", - 5: "SIGTRAP", - 6: "SIGABRT", - 7: "SIGBUS", - 8: "SIGFPE", - 9: "SIGKILL", - 10: "SIGUSR1", - 11: "SIGSEGV", - 12: "SIGUSR2", - 13: "SIGPIPE", - 14: "SIGALRM", - 15: "SIGTERM", - 16: "SIGSTKFLT", - 17: "SIGCHLD", - 18: "SIGCONT", - 19: "SIGSTOP", - 20: "SIGTSTP", - 21: "SIGTTIN", - 22: "SIGTTOU", - 23: "SIGURG", - 24: "SIGXCPU", - 25: "SIGXFSZ", - 26: "SIGVTALRM", - 27: "SIGPROF", - 28: "SIGWINCH", - 29: "SIGIO", - 30: "SIGPWR", - 31: "SIGSYS", + 1: "SIGHUP", // Hangup (terminal line disconnected). + 2: "SIGINT", // Interrupt (Ctrl+C). + 3: "SIGQUIT", // Quit (Ctrl+\). + 4: "SIGILL", // Illegal instruction. + 5: "SIGTRAP", // Trace/breakpoint trap. + 6: "SIGABRT", // Aborted. + 7: "SIGBUS", // Bus error. + 8: "SIGFPE", // Floating-point exception. + 9: "SIGKILL", // Killed (cannot be caught or ignored). + 10: "SIGUSR1", // User-defined signal 1. + 11: "SIGSEGV", // Segmentation fault. + 12: "SIGUSR2", // User-defined signal 2. + 13: "SIGPIPE", // Broken pipe. + 14: "SIGALRM", // Alarm clock. + 15: "SIGTERM", // Termination (software termination signal). + 16: "SIGSTKFLT", // Stack fault. + 17: "SIGCHLD", // Child process terminated or stopped. + 18: "SIGCONT", // Continue executing if stopped. + 19: "SIGSTOP", // Stop executing (cannot be caught or ignored). + 20: "SIGTSTP", // Terminal stop signal (Ctrl+Z). + 21: "SIGTTIN", // Background process attempting read from terminal. + 22: "SIGTTOU", // Background process attempting write to terminal. + 23: "SIGURG", // Urgent data is available on a socket. + 24: "SIGXCPU", // CPU time limit exceeded. + 25: "SIGXFSZ", // File size limit exceeded. + 26: "SIGVTALRM", // Virtual timer expired. + 27: "SIGPROF", // Profiling timer expired. + 28: "SIGWINCH", // Window size change. + 29: "SIGIO", // I/O now possible (e.g., socket ready for reading/writing). + 30: "SIGPWR", // Power failure or restart. + 31: "SIGSYS", // Bad system call. } -func parseSignal(sig uint16) (string, error) { +// parseSignal takes a signal number (sig) and returns its corresponding name. +func parseSignal(sig uint16) string { name, ok := signalMap[sig] if !ok { - return fmt.Sprintf("%v", sig), nil + return fmt.Sprintf("%v", sig) } - return name, nil + return name } +// parseOpenMode takes an open mode value (mode) and returns its octal representation. func parseOpenMode(mode any) (string, error) { m, ok := mode.(uint32) if !ok { @@ -212,30 +220,32 @@ func parseOpenMode(mode any) (string, error) { return fmt.Sprintf("%04o", m), nil } +// Constants representing various file open modes. const ( - O_ACCMODE = 0003 - O_RDONLY = 00 - O_WRONLY = 01 - O_RDWR = 02 - O_CREAT = 0100 - O_EXCL = 0200 - O_NOCTTY = 0400 - O_TRUNC = 01000 - O_APPEND = 02000 - O_NONBLOCK = 04000 - O_SYNC = 04010000 - O_ASYNC = 020000 - O_LARGEFILE = 0100000 - O_DIRECTORY = 0200000 - O_NOFOLLOW = 0400000 - O_CLOEXEC = 02000000 - O_DIRECT = 040000 - O_NOATIME = 01000000 - O_PATH = 010000000 - O_DSYNC = 010000 - O_TMPFILE = 020000000 + O_ACCMODE = 0003 // Mask for access mode (read, write, execute). + O_RDONLY = 00 // Open for read-only. + O_WRONLY = 01 // Open for write-only. + O_RDWR = 02 // Open for read-write. + O_CREAT = 0100 // Create the file if it does not exist. + O_EXCL = 0200 // Exclusive use: fail if file already exists. + O_NOCTTY = 0400 // Do not make the file a controlling terminal. + O_TRUNC = 01000 // Truncate the file if it already exists. + O_APPEND = 02000 // Append data to the file. + O_NONBLOCK = 04000 // Non-blocking mode. + O_SYNC = 04010000 // Synchronize data on file write. + O_ASYNC = 020000 // Enable asynchronous I/O. + O_LARGEFILE = 0100000 // Enable large file support. + O_DIRECTORY = 0200000 // Fail if not a directory. + O_NOFOLLOW = 0400000 // Do not follow symbolic links. + O_CLOEXEC = 02000000 // Close the file descriptor upon exec. + O_DIRECT = 040000 // Direct I/O flag. + O_NOATIME = 01000000 // Do not update file access time. + O_PATH = 010000000 // Open directory without following symlinks. + O_DSYNC = 010000 // Synchronize data on file write, but not metadata. + O_TMPFILE = 020000000 // Create an unnamed temporary file. ) +// openFlags represents various file open flags. var openFlags = map[int32]string{ O_ACCMODE: "O_ACCMODE", O_CREAT: "O_CREAT", @@ -257,10 +267,12 @@ var openFlags = map[int32]string{ O_TMPFILE: "O_TMPFILE", } +// parseOpenFlags takes an open flags value (flags) and returns a string representation +// of the corresponding flags based on the openFlags definitions. func parseOpenFlags(flags any) (string, error) { f, ok := flags.(int32) if !ok { - return fmt.Sprintf("%v", f), transformErr.Throwf("parseOpenFlags: parse value error expected %T received %T", f, flags) + return fmt.Sprintf("%v", flags), transformErr.Throwf("parseOpenFlags: parse value error expected %T received %T", f, flags) } var fs []string @@ -282,33 +294,38 @@ func parseOpenFlags(flags any) (string, error) { return strings.Join(fs, "|"), nil } +// parseOpenat2Flags takes an openat2 flags value (flags) and returns a string representation +// of the corresponding flags based on the openFlags definitions. func parseOpenat2Flags(flags any) (string, error) { f, ok := flags.(int64) if !ok { - return fmt.Sprintf("%v", f), transformErr.Throwf("parseOpenat2Flags: parse value error expected %T received %T", f, flags) + return fmt.Sprintf("%v", flags), transformErr.Throwf("parseOpenat2Flags: parse value error expected %T received %T", f, flags) } return parseOpenFlags(int32(f)) } +// parseOpenat2Mode takes an openat2 mode value (mode) and returns its octal representation. func parseOpenat2Mode(mode any) (string, error) { m, ok := mode.(int64) if !ok { - return fmt.Sprintf("%v", m), transformErr.Throwf("parseOpenat2Mode: parse value error expected %T received %T", m, mode) + return fmt.Sprintf("%v", mode), transformErr.Throwf("parseOpenat2Mode: parse value error expected %T received %T", m, mode) } return parseOpenMode(uint32(m)) } +// Constants representing various options for file path resolution. const ( - RESOLVE_NO_XDEV = 0x01 - RESOLVE_NO_MAGICLINKS = 0x02 - RESOLVE_NO_SYMLINKS = 0x04 - RESOLVE_BENEATH = 0x08 - RESOLVE_IN_ROOT = 0x10 - RESOLVE_CACHED = 0x20 + RESOLVE_NO_XDEV = 0x01 // Do not cross mount points (stay within the same filesystem). + RESOLVE_NO_MAGICLINKS = 0x02 // Do not follow magic links (e.g., /proc/self/exe). + RESOLVE_NO_SYMLINKS = 0x04 // Do not follow symbolic links. + RESOLVE_BENEATH = 0x08 // Resolve paths only if they are beneath the specified directory. + RESOLVE_IN_ROOT = 0x10 // Resolve paths relative to the root directory. + RESOLVE_CACHED = 0x20 // Use cached information for resolution. ) +// openat2ResolveFlags represents various options for file path resolution in the openat2 system call. var openat2ResolveFlags = map[int64]string{ RESOLVE_NO_XDEV: "RESOLVE_NO_XDEV", RESOLVE_NO_MAGICLINKS: "RESOLVE_NO_MAGICLINKS", @@ -318,10 +335,12 @@ var openat2ResolveFlags = map[int64]string{ RESOLVE_CACHED: "RESOLVE_CACHED", } +// parseOpenat2Resolve takes an openat2 resolve value (resolve) and returns a string representation +// of the corresponding flags based on the openat2ResolveFlags definitions. func parseOpenat2Resolve(resovle any) (string, error) { r, ok := resovle.(int64) if !ok { - return fmt.Sprintf("%v", r), transformErr.Throwf("parseOpenat2Resolve: parse value error expected %T received %T", r, resovle) + return fmt.Sprintf("%v", resovle), transformErr.Throwf("parseOpenat2Resolve: parse value error expected %T received %T", r, resovle) } var rs []string @@ -338,64 +357,67 @@ func parseOpenat2Resolve(resovle any) (string, error) { return strings.Join(rs, "|"), nil } +// Constants representing address families in networking. const ( - AF_UNIX = 1 - AF_INET = 2 - AF_INET6 = 10 + AF_UNIX = 1 // Unix domain sockets (local communication). + AF_INET = 2 // IPv4 addresses. + AF_INET6 = 10 // IPv6 addresses. ) +// socketFamilyNames represents various address families used in networking. var socketFamilyNames = map[int32]string{ - 0: "AF_UNSPEC", - 1: "AF_UNIX", - 2: "AF_INET", - 3: "AF_AX2", - 4: "AF_IPX", - 5: "AF_APPLETALK", - 6: "AF_NETROM", - 7: "AF_BRIDGE", - 8: "AF_ATMPVC", - 9: "AF_X25", - 10: "AF_INET6", - 11: "AF_ROSE", - 12: "AF_DECnet", - 13: "AF_NETBEUI", - 14: "AF_SECURITY", - 15: "AF_KEY", - 16: "AF_NETLINK", - 17: "AF_PACKET", - 18: "AF_ASH", - 19: "AF_ECONET", - 20: "AF_ATMSVC", - 21: "AF_RDS", - 22: "AF_SNA", - 23: "AF_IRDA", - 24: "AF_PPPOX", - 25: "AF_WANPIPE", - 26: "AF_LLC", - 27: "AF_IB", - 28: "AF_MPLS", - 29: "AF_CAN", - 30: "AF_TIPC", - 31: "AF_BLUETOOTH", - 32: "AF_IUCV", - 33: "AF_RXRPC", - 34: "AF_ISDN", - 35: "AF_PHONET", - 36: "AF_IEEE802154", - 37: "AF_CAIF", - 38: "AF_ALG", - 39: "AF_NFC", - 40: "AF_VSOCK", - 41: "AF_KCM", - 42: "AF_QIPCRTR", - 43: "AF_SMC", - 44: "AF_XDP", + 0: "AF_UNSPEC", // Unspecified address family. + 1: "AF_UNIX", // Unix domain sockets (local communication). + 2: "AF_INET", // IPv4 addresses. + 3: "AF_AX25", // AX.25 amateur radio protocol. + 4: "AF_IPX", // IPX/SPX protocol. + 5: "AF_APPLETALK", // AppleTalk protocol. + 6: "AF_NETROM", // Amateur radio NET/ROM protocol. + 7: "AF_BRIDGE", // Ethernet bridging. + 8: "AF_ATMPVC", // ATM PVCs. + 9: "AF_X25", // X.25 protocol. + 10: "AF_INET6", // IPv6 addresses. + 11: "AF_ROSE", // Amateur Radio X.25 PLP protocol. + 12: "AF_DECnet", // DECnet protocol. + 13: "AF_NETBEUI", // NetBIOS over IEEE 802.2. + 14: "AF_SECURITY", // Security callback pseudo AF. + 15: "AF_KEY", // PF_KEY key management API. + 16: "AF_NETLINK", // Netlink sockets. + 17: "AF_PACKET", // Low-level packet interface. + 18: "AF_ASH", // Ash. + 19: "AF_ECONET", // Acorn Econet. + 20: "AF_ATMSVC", // ATM SVCs. + 21: "AF_RDS", // Reliable Datagram Sockets. + 22: "AF_SNA", // Linux SNA Project. + 23: "AF_IRDA", // IRDA sockets. + 24: "AF_PPPOX", // PPPoX sockets. + 25: "AF_WANPIPE", // Wanpipe API sockets. + 26: "AF_LLC", // Linux LLC. + 27: "AF_IB", // InfiniBand. + 28: "AF_MPLS", // MPLS. + 29: "AF_CAN", // Controller Area Network. + 30: "AF_TIPC", // TIPC sockets. + 31: "AF_BLUETOOTH", // Bluetooth sockets. + 32: "AF_IUCV", // IUCV sockets. + 33: "AF_RXRPC", // RxRPC sockets. + 34: "AF_ISDN", // ISDN sockets. + 35: "AF_PHONET", // Phonet sockets. + 36: "AF_IEEE802154", // IEEE 802.15.4 sockets. + 37: "AF_CAIF", // CAIF sockets. + 38: "AF_ALG", // Algorithm sockets. + 39: "AF_NFC", // NFC sockets. + 40: "AF_VSOCK", // vSockets. + 41: "AF_KCM", // Kernel Connection Multiplexor. + 42: "AF_QIPCRTR", // Quick IPC router. + 43: "AF_SMC", // SMC (System Management Controller) protocol. + 44: "AF_XDP", // XDP (eXpress Data Path) sockets. } -func parseSocketFamily(n any) (string, error) { - f, ok := n.(int32) +// parseSocketFamily takes a socket family value (n) and returns its corresponding name. +func parseSocketFamily(family any) (string, error) { + f, ok := family.(int32) if !ok { - return fmt.Sprintf("%v", f), transformErr.Throwf("parseSocketFamily: parse value error expected %T received %T", f, n) + return fmt.Sprintf("%v", family), transformErr.Throwf("parseSocketFamily: parse value error expected %T received %T", f, family) } if name, ok := socketFamilyNames[f]; ok { @@ -405,25 +427,28 @@ func parseSocketFamily(n any) (string, error) { return fmt.Sprintf("%v", f), nil } +// socketTypes represents various socket types. var socketTypes = map[int32]string{ - 1: "SOCK_STREAM", - 2: "SOCK_DGRAM", - 3: "SOCK_RAW", - 4: "SOCK_RDM", - 5: "SOCK_SEQPACKET", - 6: "SOCK_DCCP", - 10: "SOCK_PACKET", + 1: "SOCK_STREAM", // Provides reliable, stream-oriented communication (e.g., TCP). + 2: "SOCK_DGRAM", // Provides unreliable, datagram-oriented communication (e.g., UDP). + 3: "SOCK_RAW", // Provides raw network protocol access. + 4: "SOCK_RDM", // Provides reliable datagram communication. + 5: "SOCK_SEQPACKET", // Provides reliable, sequenced packet communication. + 6: "SOCK_DCCP", // Datagram Congestion Control Protocol. + 10: "SOCK_PACKET", // Low-level packet interface. } +// Constants representing socket options. const ( - SOCK_CLOEXEC = 02000000 - SOCK_NONBLOCK = 00004000 + SOCK_CLOEXEC = 02000000 // Close the socket descriptor upon exec. + SOCK_NONBLOCK = 00004000 // Enable non-blocking mode for the socket. ) -func parseSocketType(n any) (string, error) { - t, ok := n.(int32) +// parseSocketType takes a socket type value (n) and returns its corresponding name. +func parseSocketType(typ any) (string, error) { + t, ok := typ.(int32) if !ok { - return fmt.Sprintf("%v", t), transformErr.Throwf("parseSocketType: parse value error expected %T received %T", t, n) + return fmt.Sprintf("%v", typ), transformErr.Throwf("parseSocketType: parse value error expected %T received %T", t, typ) } var ts []string @@ -444,38 +469,40 @@ func parseSocketType(n any) (string, error) { return strings.Join(ts, "|"), nil } +// socketProtocols represents various IP protocols used in networking. var socketProtocols = map[int32]string{ - 0: "IPPROTO_IP", - 1: "IPPROTO_ICMP", - 2: "IPPROTO_IGMP", - 4: "IPPROTO_IPIP", - 6: "IPPROTO_TCP", - 8: "IPPROTO_EGP", - 12: "IPPROTO_PUP", - 17: "IPPROTO_UDP", - 22: "IPPROTO_IDP", - 29: "IPPROTO_TP", - 33: "IPPROTO_DCCP", - 41: "IPPROTO_IPV6", - 46: "IPPROTO_RSVP", - 47: "IPPROTO_GRE", - 50: "IPPROTO_ESP", - 51: "IPPROTO_AH", - 92: "IPPROTO_MTP", - 94: "IPPROTO_BEETPH", - 98: "IPPROTO_ENCAP", - 103: "IPPROTO_PIM", - 108: "IPPROTO_COMP", - 132: "IPPROTO_SCTP", - 136: "IPPROTO_UDPLITE", - 137: "IPPROTO_MPLS", - 255: "IPPROTO_RAW", + 0: "IPPROTO_IP", // Internet Protocol (IP). + 1: "IPPROTO_ICMP", // Internet Control Message Protocol (ICMP). + 2: "IPPROTO_IGMP", // Internet Group Management Protocol (IGMP). + 4: "IPPROTO_IPIP", // IP in IP encapsulation. + 6: "IPPROTO_TCP", // Transmission Control Protocol (TCP). + 8: "IPPROTO_EGP", // Exterior Gateway Protocol (EGP). + 12: "IPPROTO_PUP", // PARC Universal Packet Protocol (PUP). + 17: "IPPROTO_UDP", // User Datagram Protocol (UDP). + 22: "IPPROTO_IDP", // Xerox NS IDP. + 29: "IPPROTO_TP", // ISO Transport Protocol Class 4 (TP). + 33: "IPPROTO_DCCP", // Datagram Congestion Control Protocol (DCCP). + 41: "IPPROTO_IPV6", // IPv6 header. + 46: "IPPROTO_RSVP", // Resource Reservation Protocol (RSVP). + 47: "IPPROTO_GRE", // Generic Routing Encapsulation (GRE). + 50: "IPPROTO_ESP", // Encapsulating Security Payload (ESP). + 51: "IPPROTO_AH", // Authentication Header (AH). + 92: "IPPROTO_MTP", // Multicast Transport Protocol (MTP). + 94: "IPPROTO_BEETPH", // BEET PH Protocol. + 98: "IPPROTO_ENCAP", // Encapsulation Header (ENCAP). + 103: "IPPROTO_PIM", // Protocol Independent Multicast (PIM). + 108: "IPPROTO_COMP", // Compression Header Protocol. + 132: "IPPROTO_SCTP", // Stream Control Transmission Protocol (SCTP). + 136: "IPPROTO_UDPLITE", // UDP-Lite. + 137: "IPPROTO_MPLS", // MPLS in IP. + 255: "IPPROTO_RAW", // Raw IP packets. } -func parseSocketProtocol(n any) (string, error) { - p, ok := n.(int32) +// parseSocketProtocol takes a socket protocol value (n) and returns its corresponding name. +func parseSocketProtocol(protocol any) (string, error) { + p, ok := protocol.(int32) if !ok { - return fmt.Sprintf("%v", p), transformErr.Throwf("parseSocketProtocol: parse value error expected %T received %T", p, n) + return fmt.Sprintf("%v", protocol), transformErr.Throwf("parseSocketProtocol: parse value error expected %T received %T", p, protocol) } if prot, ok := socketProtocols[p]; ok { diff --git a/pkg/eventparser/transform_test.go b/pkg/eventparser/transform_test.go new file mode 100644 index 0000000..9500050 --- /dev/null +++ b/pkg/eventparser/transform_test.go @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of Tarian & the Organization created Tarian + +package eventparser + +import ( + "testing" +) + +// Test_parseExecveatDird tests the parseExecveatDird function. +func Test_parseExecveatDird(t *testing.T) { + type args struct { + dird any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "valid values", + args: args{ + dird: int32(-100), + }, + want: "AT_FDCWD", + wantErr: false, + }, + { + name: "invalid value type", + args: args{ + dird: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid undefined value ", + args: args{ + dird: int32(256), + }, + want: "256", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseExecveatDird(tt.args.dird) + if (err != nil) != tt.wantErr { + t.Errorf("parseExecveatDird() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseExecveatDird() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseExecveatFlags tests the parseExecveatFlags function. +func Test_parseExecveatFlags(t *testing.T) { + type args struct { + flag any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + flag: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid undefined values", + args: args{ + flag: int32(0), + }, + want: "0", + wantErr: false, + }, + { + name: "valid value", + args: args{ + flag: int32(4096), + }, + want: "AT_EMPTY_PATH", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseExecveatFlags(tt.args.flag) + if (err != nil) != tt.wantErr { + t.Errorf("parseExecveatFlags() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseExecveatFlags() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseCloneFlags tests the parseCloneFlags function. +func Test_parseCloneFlags(t *testing.T) { + type args struct { + flag any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + flag: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid undefined values", + args: args{ + flag: uint64(0), + }, + want: "0", + wantErr: false, + }, + { + name: "valid value", + args: args{ + flag: uint64(131072 | 14), + }, + want: "CLONE_NEWNS|SIGALRM", + wantErr: false, + }, + { + name: "invalid value for signal", + args: args{ + flag: uint64(131072 | 32), + }, + want: "CLONE_NEWNS|32", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseCloneFlags(tt.args.flag) + if (err != nil) != tt.wantErr { + t.Errorf("parseCloneFlags() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseCloneFlags() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseOpenMode tests the parseOpenMode function. +func Test_parseOpenMode(t *testing.T) { + type args struct { + mode any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + mode: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid value", + args: args{ + mode: uint32(384), + }, + want: "0600", + wantErr: false, + }, + { + name: "valid value with sticky bit", + args: args{ + mode: uint32(928), + }, + want: "1640", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseOpenMode(tt.args.mode) + if (err != nil) != tt.wantErr { + t.Errorf("parseOpenMode() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseOpenMode() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseOpenFlags tests the parseOpenFlags function. +func Test_parseOpenFlags(t *testing.T) { + type args struct { + flags any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + flags: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid value write only", + args: args{ + flags: int32(1), + }, + want: "O_WRONLY", + wantErr: false, + }, + { + name: "valid value read only", + args: args{ + flags: int32(0 | 64), + }, + want: "O_RDONLY|O_CREAT", + wantErr: false, + }, + { + name: "valid value read and write", + args: args{ + flags: int32(2 | 64), + }, + want: "O_RDWR|O_CREAT", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseOpenFlags(tt.args.flags) + if (err != nil) != tt.wantErr { + t.Errorf("parseOpenFlags() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseOpenFlags() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseOpenat2Flags tests the parseOpenat2Flags function. +func Test_parseOpenat2Flags(t *testing.T) { + type args struct { + flags any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + flags: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid value", + args: args{ + flags: int64(0 | 64), + }, + want: "O_RDONLY|O_CREAT", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseOpenat2Flags(tt.args.flags) + if (err != nil) != tt.wantErr { + t.Errorf("parseOpenat2Flags() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseOpenat2Flags() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseOpenat2Mode tests the parseOpenat2Mode function. +func Test_parseOpenat2Mode(t *testing.T) { + type args struct { + mode any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + mode: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid value", + args: args{ + mode: int64(384), + }, + want: "0600", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseOpenat2Mode(tt.args.mode) + if (err != nil) != tt.wantErr { + t.Errorf("parseOpenat2Mode() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseOpenat2Mode() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseOpenat2Resolve tests the parseOpenat2Resolve function. +func Test_parseOpenat2Resolve(t *testing.T) { + type args struct { + resovle any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + resovle: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid undefined value", + args: args{ + resovle: int64(0), + }, + want: "0", + wantErr: false, + }, + { + name: "valid value", + args: args{ + resovle: int64(32), + }, + want: "RESOLVE_CACHED", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseOpenat2Resolve(tt.args.resovle) + if (err != nil) != tt.wantErr { + t.Errorf("parseOpenat2Resolve() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseOpenat2Resolve() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseSocketFamily tests the parseSocketFamily function. +func Test_parseSocketFamily(t *testing.T) { + type args struct { + n any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + n: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid value", + args: args{ + n: int32(45), + }, + want: "45", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseSocketFamily(tt.args.n) + if (err != nil) != tt.wantErr { + t.Errorf("parseSocketFamily() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseSocketFamily() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseSocketType tests the parseSocketType function. +func Test_parseSocketType(t *testing.T) { + type args struct { + n any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + n: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid undefine value", + args: args{ + n: int32(11), + }, + want: "11", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseSocketType(tt.args.n) + if (err != nil) != tt.wantErr { + t.Errorf("parseSocketType() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseSocketType() = %v, want %v", got, tt.want) + } + }) + } +} + +// Test_parseSocketProtocol tests the parseSocketProtocol function. +func Test_parseSocketProtocol(t *testing.T) { + type args struct { + n any + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "invalid value type", + args: args{ + n: 123, + }, + want: "123", + wantErr: true, + }, + { + name: "valid value", + args: args{ + n: int32(266), + }, + want: "266", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseSocketProtocol(tt.args.n) + if (err != nil) != tt.wantErr { + t.Errorf("parseSocketProtocol() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("parseSocketProtocol() = %v, want %v", got, tt.want) + } + }) + } +}