diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/c/generated_tracepoints.c | 30 | ||||
| -rw-r--r-- | internal/c/generated_tracepoints_result.txt | 8 | ||||
| -rw-r--r-- | internal/c/types.h | 12 | ||||
| -rw-r--r-- | internal/event/interface_assertions.go | 3 | ||||
| -rw-r--r-- | internal/eventloop_exit.go | 36 | ||||
| -rw-r--r-- | internal/eventloop_runtime.go | 14 | ||||
| -rw-r--r-- | internal/eventloop_socket_test.go | 38 | ||||
| -rw-r--r-- | internal/generate/bpfhandler.go | 9 | ||||
| -rw-r--r-- | internal/generate/classify.go | 17 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 49 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 17 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 1 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 31 | ||||
| -rw-r--r-- | internal/types/fastdecode.go | 24 | ||||
| -rw-r--r-- | internal/types/fastdecode_test.go | 41 | ||||
| -rw-r--r-- | internal/types/generated_types.go | 71 | ||||
| -rw-r--r-- | internal/types/types_test.go | 25 |
17 files changed, 403 insertions, 23 deletions
diff --git a/internal/c/generated_tracepoints.c b/internal/c/generated_tracepoints.c index 03cf2b4..0f83f35 100644 --- a/internal/c/generated_tracepoints.c +++ b/internal/c/generated_tracepoints.c @@ -952,91 +952,93 @@ int handle_sys_exit_listen(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_accept4 is a struct fd_event +/// sys_enter_accept4 is a struct accept_event SEC("tracepoint/syscalls/sys_enter_accept4") int handle_sys_enter_accept4(struct syscall_trace_enter *ctx) { __u32 pid, tid; if (filter(&pid, &tid)) return 0; - struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); + struct accept_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct accept_event), 0); if (!ev) return 0; - ev->event_type = ENTER_FD_EVENT; + ev->event_type = ENTER_ACCEPT_EVENT; ev->trace_id = SYS_ENTER_ACCEPT4; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); ev->fd = (__s32)ctx->args[0]; + ev->ret = -1; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_exit_accept4 is a struct ret_event (UNCLASSIFIED) +/// sys_exit_accept4 is a struct accept_event SEC("tracepoint/syscalls/sys_exit_accept4") int handle_sys_exit_accept4(struct syscall_trace_exit *ctx) { __u32 pid, tid; if (filter(&pid, &tid)) return 0; - struct ret_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct ret_event), 0); + struct accept_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct accept_event), 0); if (!ev) return 0; - ev->event_type = EXIT_RET_EVENT; + ev->event_type = EXIT_ACCEPT_EVENT; ev->trace_id = SYS_EXIT_ACCEPT4; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = -1; ev->ret = ctx->ret; - ev->ret_type = UNCLASSIFIED; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_enter_accept is a struct fd_event +/// sys_enter_accept is a struct accept_event SEC("tracepoint/syscalls/sys_enter_accept") int handle_sys_enter_accept(struct syscall_trace_enter *ctx) { __u32 pid, tid; if (filter(&pid, &tid)) return 0; - struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0); + struct accept_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct accept_event), 0); if (!ev) return 0; - ev->event_type = ENTER_FD_EVENT; + ev->event_type = ENTER_ACCEPT_EVENT; ev->trace_id = SYS_ENTER_ACCEPT; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); ev->fd = (__s32)ctx->args[0]; + ev->ret = -1; bpf_ringbuf_submit(ev, 0); return 0; } -/// sys_exit_accept is a struct ret_event (UNCLASSIFIED) +/// sys_exit_accept is a struct accept_event SEC("tracepoint/syscalls/sys_exit_accept") int handle_sys_exit_accept(struct syscall_trace_exit *ctx) { __u32 pid, tid; if (filter(&pid, &tid)) return 0; - struct ret_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct ret_event), 0); + struct accept_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct accept_event), 0); if (!ev) return 0; - ev->event_type = EXIT_RET_EVENT; + ev->event_type = EXIT_ACCEPT_EVENT; ev->trace_id = SYS_EXIT_ACCEPT; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->fd = -1; ev->ret = ctx->ret; - ev->ret_type = UNCLASSIFIED; bpf_ringbuf_submit(ev, 0); return 0; diff --git a/internal/c/generated_tracepoints_result.txt b/internal/c/generated_tracepoints_result.txt index 560e24b..a2ad3ca 100644 --- a/internal/c/generated_tracepoints_result.txt +++ b/internal/c/generated_tracepoints_result.txt @@ -1,5 +1,5 @@ -sys_enter_accept is a struct fd_event -sys_enter_accept4 is a struct fd_event +sys_enter_accept is a struct accept_event +sys_enter_accept4 is a struct accept_event sys_enter_access is a struct path_event sys_enter_acct is a struct null_event sys_enter_add_key is a struct null_event @@ -365,8 +365,8 @@ sys_enter_wait4 is a struct null_event sys_enter_waitid is a struct null_event sys_enter_write is a struct fd_event sys_enter_writev is a struct fd_event -sys_exit_accept is a struct ret_event (UNCLASSIFIED) -sys_exit_accept4 is a struct ret_event (UNCLASSIFIED) +sys_exit_accept is a struct accept_event +sys_exit_accept4 is a struct accept_event sys_exit_access is a struct ret_event (UNCLASSIFIED) sys_exit_acct is a struct ret_event (UNCLASSIFIED) sys_exit_add_key is a struct ret_event (UNCLASSIFIED) diff --git a/internal/c/types.h b/internal/c/types.h index 29f18e1..6365e3f 100644 --- a/internal/c/types.h +++ b/internal/c/types.h @@ -25,6 +25,8 @@ #define EXIT_SOCKET_EVENT 20 #define ENTER_SOCKETPAIR_EVENT 21 #define EXIT_SOCKETPAIR_EVENT 22 +#define ENTER_ACCEPT_EVENT 23 +#define EXIT_ACCEPT_EVENT 24 #define UNCLASSIFIED 0 #define READ_CLASSIFIED 1 @@ -143,3 +145,13 @@ struct socketpair_event { __s32 sv1; __s64 ret; }; + +struct accept_event { + __u32 event_type; + __u32 trace_id; + __u64 time; + __u32 pid; + __u32 tid; + __s32 fd; + __s64 ret; +}; diff --git a/internal/event/interface_assertions.go b/internal/event/interface_assertions.go index 3dce64d..fea20ba 100644 --- a/internal/event/interface_assertions.go +++ b/internal/event/interface_assertions.go @@ -47,4 +47,7 @@ var ( // *types.SocketpairEvent carries socketpair domain/type/protocol metadata. _ Event = (*types.SocketpairEvent)(nil) + + // *types.AcceptEvent carries listening-fd input and accepted-fd return metadata. + _ Event = (*types.AcceptEvent)(nil) ) diff --git a/internal/eventloop_exit.go b/internal/eventloop_exit.go index c64515b..9953c13 100644 --- a/internal/eventloop_exit.go +++ b/internal/eventloop_exit.go @@ -30,6 +30,8 @@ func (e *eventLoop) handleTracepointExit(ep *event.Pair) bool { return e.handleSocketExit(ep, ev) case *types.SocketpairEvent: return e.handleSocketpairExit(ep, ev) + case *types.AcceptEvent: + return e.handleAcceptExit(ep, ev) case *types.NullEvent: return e.handleNullExit(ep, ev) case *types.FcntlEvent: @@ -282,10 +284,44 @@ func (e *eventLoop) handleSocketpairExit(ep *event.Pair, socketpairEv *types.Soc return true } +func (e *eventLoop) handleAcceptExit(ep *event.Pair, acceptEv *types.AcceptEvent) bool { + exitEv, ok := ep.ExitEv.(*types.AcceptEvent) + if !ok { + e.recyclePair(ep, "Dropped malformed accept exit event") + return false + } + + listening := e.fdState().resolve(acceptEv.Fd, acceptEv.Pid) + if fd := int32(exitEv.Ret); fd >= 0 { + fdFile := file.NewFd(fd, acceptedSocketDescriptorName(listening), -1) + e.fdState().set(fd, fdFile) + ep.File = fdFile + } else { + ep.File = listening + } + ep.Comm = e.comm(acceptEv.GetTid()) + if !e.Filter().MatchPair(ep) { + ep.Recycle() + return false + } + return true +} + func socketDescriptorName(family, typ, protocol int32) string { return fmt.Sprintf("socket:%d:%d:%d", family, typ, protocol) } +func acceptedSocketDescriptorName(listening file.File) string { + if listening == nil { + return "socket:accepted" + } + name := listening.Name() + if name == "" { + return "socket:accepted" + } + return name +} + func (e *eventLoop) handleNullExit(ep *event.Pair, nullEv *types.NullEvent) bool { if ep.Is(types.SYS_ENTER_IO_URING_SETUP) { retEvent, ok := ep.ExitEv.(*types.RetEvent) diff --git a/internal/eventloop_runtime.go b/internal/eventloop_runtime.go index 55948bc..94648e0 100644 --- a/internal/eventloop_runtime.go +++ b/internal/eventloop_runtime.go @@ -285,6 +285,20 @@ func (e *eventLoop) registerSocketHandlers() { } e.tracepointExited(socketpairEv, ch) } + e.rawHandlers[types.ENTER_ACCEPT_EVENT] = func(raw []byte, _ chan<- *event.Pair) { + acceptEv, ok := decodeRawEvent(e, types.ENTER_ACCEPT_EVENT, raw, types.NewAcceptEventFast) + if !ok { + return + } + e.tracepointEntered(acceptEv) + } + e.rawHandlers[types.EXIT_ACCEPT_EVENT] = func(raw []byte, ch chan<- *event.Pair) { + acceptEv, ok := decodeRawEvent(e, types.EXIT_ACCEPT_EVENT, raw, types.NewAcceptEventFast) + if !ok { + return + } + e.tracepointExited(acceptEv, ch) + } } func decodeRawEvent[T any](e *eventLoop, eventType types.EventType, raw []byte, decode func([]byte) *T) (*T, bool) { diff --git a/internal/eventloop_socket_test.go b/internal/eventloop_socket_test.go index 7712b3c..19c1fa8 100644 --- a/internal/eventloop_socket_test.go +++ b/internal/eventloop_socket_test.go @@ -4,6 +4,7 @@ import ( "testing" "ior/internal/event" + "ior/internal/file" "ior/internal/globalfilter" "ior/internal/types" ) @@ -107,6 +108,37 @@ func TestHandleSocketpairExitTracksReturnedFdsFromExitEvent(t *testing.T) { verifyFileDescriptor(t, el, 62, "socket:1:1:0") } +func TestHandleAcceptExitTracksAcceptedFd(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + + el.fdState().set(11, file.NewFd(11, "socket:1:1:0", -1)) + + enter := &types.AcceptEvent{ + EventType: types.ENTER_ACCEPT_EVENT, + TraceId: types.SYS_ENTER_ACCEPT4, + Time: 100, + Pid: 91, + Tid: 92, + Fd: 11, + Ret: -1, + } + exit := &types.AcceptEvent{ + EventType: types.EXIT_ACCEPT_EVENT, + TraceId: types.SYS_EXIT_ACCEPT4, + Time: 200, + Pid: 91, + Tid: 92, + Fd: -1, + Ret: 77, + } + ep := &event.Pair{EnterEv: enter, ExitEv: exit} + + if ok := el.handleAcceptExit(ep, enter); !ok { + t.Fatal("handleAcceptExit returned false") + } + verifyFileDescriptor(t, el, 77, "socket:1:1:0") +} + func TestInitRawHandlersRegistersSocketEvents(t *testing.T) { el := mustNewEventLoop(t, eventLoopConfig{}) if _, ok := el.rawHandlers[types.ENTER_SOCKET_EVENT]; !ok { @@ -118,4 +150,10 @@ func TestInitRawHandlersRegistersSocketEvents(t *testing.T) { if _, ok := el.rawHandlers[types.EXIT_SOCKETPAIR_EVENT]; !ok { t.Fatal("EXIT_SOCKETPAIR_EVENT handler is not registered") } + if _, ok := el.rawHandlers[types.ENTER_ACCEPT_EVENT]; !ok { + t.Fatal("ENTER_ACCEPT_EVENT handler is not registered") + } + if _, ok := el.rawHandlers[types.EXIT_ACCEPT_EVENT]; !ok { + t.Fatal("EXIT_ACCEPT_EVENT handler is not registered") + } } diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go index 50917e7..bada317 100644 --- a/internal/generate/bpfhandler.go +++ b/internal/generate/bpfhandler.go @@ -77,6 +77,8 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string { return generateExtraSocket() case KindSocketpair: return generateExtraSocketpair(isEnter) + case KindAccept: + return generateExtraAccept(isEnter) case KindOpen: return generateExtraOpen(f) case KindPathname: @@ -164,6 +166,13 @@ func generateExtraSocketpair(isEnter bool) string { return " __s32 family = -1;\n __s32 type = -1;\n __s32 protocol = -1;\n __s32 sv0 = -1;\n __s32 sv1 = -1;\n struct socketpair_ctx *pending = bpf_map_lookup_elem(&socketpair_ctx_map, &tid);\n if (pending) {\n family = pending->family;\n type = pending->type;\n protocol = pending->protocol;\n if (ctx->ret == 0 && pending->usockvec != 0) {\n int sv[2];\n if (bpf_probe_read_user(&sv, sizeof(sv), (void *)pending->usockvec) == 0) {\n sv0 = (__s32)sv[0];\n sv1 = (__s32)sv[1];\n }\n }\n bpf_map_delete_elem(&socketpair_ctx_map, &tid);\n }\n ev->family = family;\n ev->type = type;\n ev->protocol = protocol;\n ev->sv0 = sv0;\n ev->sv1 = sv1;\n ev->ret = ctx->ret;\n" } +func generateExtraAccept(isEnter bool) string { + if isEnter { + return " ev->fd = (__s32)ctx->args[0];\n ev->ret = -1;\n" + } + return " ev->fd = -1;\n ev->ret = ctx->ret;\n" +} + // eventStructName returns the C struct name for a TracepointKind. The mapping // is driven by kindRegistry so adding a new kind only requires a registry entry. func eventStructName(kind TracepointKind) string { diff --git a/internal/generate/classify.go b/internal/generate/classify.go index 56f5cd2..abe6b2e 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -17,6 +17,7 @@ const ( KindOpenByHandleAt KindSocket KindSocketpair + KindAccept ) type RetClassification string @@ -87,6 +88,22 @@ func classifyNameOnly(name string) (ClassificationResult, bool) { return ClassificationResult{Kind: KindSocketpair}, true case "sys_exit_socketpair": return ClassificationResult{Kind: KindSocketpair}, true + case "sys_enter_accept": + return ClassificationResult{Kind: KindAccept}, true + case "sys_exit_accept": + return ClassificationResult{Kind: KindAccept}, true + case "sys_enter_accept4": + return ClassificationResult{Kind: KindAccept}, true + case "sys_exit_accept4": + return ClassificationResult{Kind: KindAccept}, true + case "sys_enter_bind": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_connect": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_listen": + return ClassificationResult{Kind: KindFd}, true + case "sys_enter_shutdown": + return ClassificationResult{Kind: KindFd}, true } if strings.HasPrefix(name, "sys_enter_io_") { return ClassificationResult{Kind: KindNull}, true diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go index e6353b5..07cfe49 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -237,10 +237,49 @@ func TestClassifyPathnameExecve(t *testing.T) { } } -func TestClassifyFdAccept(t *testing.T) { +func TestClassifyAccept(t *testing.T) { r := classifyFromData(t, FormatAccept) - if r.Kind != KindFd { - t.Errorf("accept: got kind %d, want KindFd", r.Kind) + if r.Kind != KindAccept { + t.Errorf("accept: got kind %d, want KindAccept", r.Kind) + } +} + +func TestClassifyAccept4(t *testing.T) { + r := classifyFromData(t, FormatAccept4) + if r.Kind != KindAccept { + t.Errorf("accept4: got kind %d, want KindAccept", r.Kind) + } +} + +func TestClassifyExitAccept(t *testing.T) { + r := classifyFromData(t, FormatExitAccept) + if r.Kind != KindAccept { + t.Errorf("exit_accept: got kind %d, want KindAccept", r.Kind) + } +} + +func TestClassifyExitAccept4(t *testing.T) { + r := classifyFromData(t, FormatExitAccept4) + if r.Kind != KindAccept { + t.Errorf("exit_accept4: got kind %d, want KindAccept", r.Kind) + } +} + +func TestClassifySocketLifecycleFdSyscallsByName(t *testing.T) { + tests := []string{"bind", "connect", "listen", "shutdown"} + for _, name := range tests { + t.Run(name, func(t *testing.T) { + r := ClassifyFormat(&Format{ + Name: "sys_enter_" + name, + ExternalFields: []Field{ + {Type: "long", Name: "__syscall_nr"}, + {Type: "int", Name: "sockfd"}, + }, + }) + if r.Kind != KindFd { + t.Errorf("%s: got kind %d, want KindFd", name, r.Kind) + } + }) } } @@ -301,7 +340,8 @@ func TestClassifySyscallPairAccepted(t *testing.T) { {"symlink", FormatSymlink, FormatExitSymlink, KindName}, {"mknod", FormatMknod, FormatExitMknod, KindPathname}, {"execve", FormatExecve, FormatExitExecve, KindPathname}, - {"accept", FormatAccept, FormatExitAccept, KindFd}, + {"accept", FormatAccept, FormatExitAccept, KindAccept}, + {"accept4", FormatAccept4, FormatExitAccept4, KindAccept}, {"socket", FormatSocket, FormatExitSocket, KindSocket}, {"socketpair", FormatSocketpair, FormatExitSocketpair, KindSocketpair}, {"kill", FormatKill, FormatExitKill, KindNull}, @@ -328,6 +368,7 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) { {"mknod", FormatMknod, FormatExitMknod, FamilyFS}, {"execve", FormatExecve, FormatExitExecve, FamilyProcess}, {"accept", FormatAccept, FormatExitAccept, FamilyNetwork}, + {"accept4", FormatAccept4, FormatExitAccept4, FamilyNetwork}, {"socket", FormatSocket, FormatExitSocket, FamilyNetwork}, {"socketpair", FormatSocketpair, FormatExitSocketpair, FamilyNetwork}, {"kill", FormatKill, FormatExitKill, FamilySignals}, diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 3e29612..d8130c8 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -212,6 +212,19 @@ func TestGenerateSocketpairHandler(t *testing.T) { requireContains(t, output, "ev->family = pending.family;") } +func TestGenerateAcceptHandler(t *testing.T) { + output := generateFromPair(t, FormatAccept, FormatExitAccept) + + requireContains(t, output, "struct accept_event *ev") + requireContains(t, output, "ev->event_type = ENTER_ACCEPT_EVENT;") + requireContains(t, output, "ev->fd = (__s32)ctx->args[0];") + requireContains(t, output, "ev->ret = -1;") + requireContains(t, output, "SEC(\"tracepoint/syscalls/sys_exit_accept\")") + requireContains(t, output, "ev->event_type = EXIT_ACCEPT_EVENT;") + requireContains(t, output, "ev->fd = -1;") + requireContains(t, output, "ev->ret = ctx->ret;") +} + func TestGenerateNameToHandleAtHandler(t *testing.T) { output := generateFromPair(t, FormatNameToHandleAt, FormatExitNameToHandleAt) @@ -321,6 +334,7 @@ func TestGenerateAllEventTypes(t *testing.T) { {KindOpenByHandleAt, "ENTER_OPEN_BY_HANDLE_AT_EVENT", "EXIT_OPEN_BY_HANDLE_AT_EVENT"}, {KindSocket, "ENTER_SOCKET_EVENT", "EXIT_SOCKET_EVENT"}, {KindSocketpair, "ENTER_SOCKETPAIR_EVENT", "EXIT_SOCKETPAIR_EVENT"}, + {KindAccept, "ENTER_ACCEPT_EVENT", "EXIT_ACCEPT_EVENT"}, } for _, tt := range tests { @@ -349,6 +363,7 @@ func TestEventStructNames(t *testing.T) { {KindOpenByHandleAt, "open_by_handle_at_event"}, {KindSocket, "socket_event"}, {KindSocketpair, "socketpair_event"}, + {KindAccept, "accept_event"}, } for _, tt := range tests { @@ -367,7 +382,7 @@ func TestEnterReject(t *testing.T) { t.Error("KindNone should be enter-rejected") } - accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair} + accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair, KindAccept} for _, k := range accepted { if isEnterRejected(k) { t.Errorf("kind %d should NOT be enter-rejected", k) diff --git a/internal/generate/kindregistry.go b/internal/generate/kindregistry.go index f716489..186dbbc 100644 --- a/internal/generate/kindregistry.go +++ b/internal/generate/kindregistry.go @@ -27,6 +27,7 @@ var kindRegistry = map[TracepointKind]kindMeta{ KindOpenByHandleAt: {structName: "open_by_handle_at_event", enterAccepted: true}, KindSocket: {structName: "socket_event", enterAccepted: true}, KindSocketpair: {structName: "socketpair_event", enterAccepted: true}, + KindAccept: {structName: "accept_event", enterAccepted: true}, // KindNone is intentionally absent: it represents "unclassified" and is // never enter-accepted. lookupKind returns the zero kindMeta (enterAccepted=false) // for any unregistered kind, so KindNone is implicitly rejected. diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go index e363a68..090619b 100644 --- a/internal/generate/testdata.go +++ b/internal/generate/testdata.go @@ -793,6 +793,37 @@ format: print fmt: "0x%lx", REC->ret ` +const FormatAccept4 = `name: sys_enter_accept4 +ID: 1810 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int fd; offset:16; size:8; signed:0; + field:struct sockaddr * upeer_sockaddr; offset:24; size:8; signed:0; + field:int * upeer_addrlen; offset:32; size:8; signed:0; + field:int flags; offset:40; size:8; signed:0; + +print fmt: "fd: 0x%08lx, upeer_sockaddr: 0x%08lx, upeer_addrlen: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->fd)), ((unsigned long)(REC->upeer_sockaddr)), ((unsigned long)(REC->upeer_addrlen)), ((unsigned long)(REC->flags)) +` + +const FormatExitAccept4 = `name: sys_exit_accept4 +ID: 1809 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` + const FormatSocket = `name: sys_enter_socket ID: 1818 format: diff --git a/internal/types/fastdecode.go b/internal/types/fastdecode.go index 0967c02..e8689da 100644 --- a/internal/types/fastdecode.go +++ b/internal/types/fastdecode.go @@ -15,6 +15,8 @@ const ( socketEventSize = 36 socketpairEventSize = 56 socketpairEventSizeV1 = 52 + acceptEventSize = 40 + acceptEventSizeV1 = 36 ) func NewOpenEventFast(raw []byte) *OpenEvent { @@ -220,3 +222,25 @@ func NewSocketpairEventFast(raw []byte) *SocketpairEvent { s.Ret = int64(binary.LittleEndian.Uint64(raw[retOffset : retOffset+8])) return s } + +func NewAcceptEventFast(raw []byte) *AcceptEvent { + if len(raw) < acceptEventSizeV1 { + return nil + } + if len(raw) != acceptEventSize && len(raw) != acceptEventSizeV1 { + return NewAcceptEvent(raw) + } + a := poolOfAcceptEvents.Get().(*AcceptEvent) + a.EventType = EventType(binary.LittleEndian.Uint32(raw[0:4])) + a.TraceId = TraceId(binary.LittleEndian.Uint32(raw[4:8])) + a.Time = binary.LittleEndian.Uint64(raw[8:16]) + a.Pid = binary.LittleEndian.Uint32(raw[16:20]) + a.Tid = binary.LittleEndian.Uint32(raw[20:24]) + a.Fd = int32(binary.LittleEndian.Uint32(raw[24:28])) + retOffset := 28 + if len(raw) == acceptEventSize { + retOffset = 32 + } + a.Ret = int64(binary.LittleEndian.Uint64(raw[retOffset : retOffset+8])) + return a +} diff --git a/internal/types/fastdecode_test.go b/internal/types/fastdecode_test.go index f15cc9e..d806b45 100644 --- a/internal/types/fastdecode_test.go +++ b/internal/types/fastdecode_test.go @@ -153,6 +153,19 @@ func TestFastDecodersMatchGeneratedDecoders(t *testing.T) { t.Fatalf("socketpair decode mismatch") } }) + + t.Run("AcceptEvent", func(t *testing.T) { + ev := &AcceptEvent{EventType: ENTER_ACCEPT_EVENT, TraceId: SYS_ENTER_ACCEPT4, Time: 1, Pid: 2, Tid: 3, Fd: 4, Ret: -1} + raw, _ := ev.Bytes() + + slow := NewAcceptEvent(raw) + fast := NewAcceptEventFast(raw) + defer slow.Recycle() + defer fast.Recycle() + if !slow.Equals(fast) { + t.Fatalf("accept decode mismatch") + } + }) } func TestNewSocketpairEventFastKernelLayout(t *testing.T) { @@ -190,6 +203,33 @@ func TestNewSocketpairEventFastKernelLayout(t *testing.T) { } } +func TestNewAcceptEventFastKernelLayout(t *testing.T) { + raw := make([]byte, acceptEventSize) + binary.LittleEndian.PutUint32(raw[0:4], uint32(EXIT_ACCEPT_EVENT)) + binary.LittleEndian.PutUint32(raw[4:8], uint32(SYS_EXIT_ACCEPT4)) + binary.LittleEndian.PutUint64(raw[8:16], 1) + binary.LittleEndian.PutUint32(raw[16:20], 2) + binary.LittleEndian.PutUint32(raw[20:24], 3) + binary.LittleEndian.PutUint32(raw[24:28], uint32(10)) + binary.LittleEndian.PutUint64(raw[32:40], uint64(42)) + + fast := NewAcceptEventFast(raw) + if fast == nil { + t.Fatalf("expected decoded accept event for kernel layout payload") + } + defer fast.Recycle() + + if fast.EventType != EXIT_ACCEPT_EVENT || + fast.TraceId != SYS_EXIT_ACCEPT4 || + fast.Time != 1 || + fast.Pid != 2 || + fast.Tid != 3 || + fast.Fd != 10 || + fast.Ret != 42 { + t.Fatalf("unexpected accept decode: %#v", fast) + } +} + func TestFastDecodersReturnNilOnShortPayload(t *testing.T) { cases := []struct { name string @@ -206,6 +246,7 @@ func TestFastDecodersReturnNilOnShortPayload(t *testing.T) { {name: "OpenByHandleAtEvent", decode: func(raw []byte) bool { return NewOpenByHandleAtEventFast(raw) == nil }}, {name: "SocketEvent", decode: func(raw []byte) bool { return NewSocketEventFast(raw) == nil }}, {name: "SocketpairEvent", decode: func(raw []byte) bool { return NewSocketpairEventFast(raw) == nil }}, + {name: "AcceptEvent", decode: func(raw []byte) bool { return NewAcceptEventFast(raw) == nil }}, } for _, tc := range cases { diff --git a/internal/types/generated_types.go b/internal/types/generated_types.go index bdfce73..05c869a 100644 --- a/internal/types/generated_types.go +++ b/internal/types/generated_types.go @@ -90,6 +90,8 @@ const ENTER_SOCKET_EVENT = 19 const EXIT_SOCKET_EVENT = 20 const ENTER_SOCKETPAIR_EVENT = 21 const EXIT_SOCKETPAIR_EVENT = 22 +const ENTER_ACCEPT_EVENT = 23 +const EXIT_ACCEPT_EVENT = 24 const UNCLASSIFIED = 0 const READ_CLASSIFIED = 1 const WRITE_CLASSIFIED = 2 @@ -1589,3 +1591,72 @@ func (s *SocketpairEvent) Bytes() ([]byte, error) { func (s *SocketpairEvent) Recycle() { poolOfSocketpairEvents.Put(s) } + +type AcceptEvent struct { + EventType EventType + TraceId TraceId + Time uint64 + Pid uint32 + Tid uint32 + Fd int32 + Ret int64 +} + +func (a AcceptEvent) String() string { + return fmt.Sprintf("EventType:%v TraceId:%v Time:%v Pid:%v Tid:%v Fd:%v Ret:%v", a.EventType, a.TraceId, a.Time, a.Pid, a.Tid, a.Fd, a.Ret) +} + +func (a AcceptEvent) Equals(other any) bool { + otherConcrete, ok := other.(*AcceptEvent) + if !ok { + return false + } + return a.EventType == otherConcrete.EventType && a.TraceId == otherConcrete.TraceId && a.Time == otherConcrete.Time && a.Pid == otherConcrete.Pid && a.Tid == otherConcrete.Tid && a.Fd == otherConcrete.Fd && a.Ret == otherConcrete.Ret +} + +func (a *AcceptEvent) GetEventType() EventType { + return a.EventType +} + +func (a *AcceptEvent) GetTraceId() TraceId { + return a.TraceId +} + +func (a *AcceptEvent) GetPid() uint32 { + return a.Pid +} + +func (a *AcceptEvent) GetTid() uint32 { + return a.Tid +} + +func (a *AcceptEvent) GetTime() uint64 { + return a.Time +} + +var poolOfAcceptEvents = sync.Pool{ + New: func() any { return &AcceptEvent{} }, +} + +func NewAcceptEvent(raw []byte) *AcceptEvent { + a := poolOfAcceptEvents.Get().(*AcceptEvent) + if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, a); err != nil { + *a = AcceptEvent{} + poolOfAcceptEvents.Put(a) + return nil + } + return a +} + +func (a *AcceptEvent) Bytes() ([]byte, error) { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, a) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (a *AcceptEvent) Recycle() { + poolOfAcceptEvents.Put(a) +} diff --git a/internal/types/types_test.go b/internal/types/types_test.go index 40b5090..d47609a 100644 --- a/internal/types/types_test.go +++ b/internal/types/types_test.go @@ -190,6 +190,31 @@ func TestSocketpairEventSerialization(t *testing.T) { assertEquals(t, socketpairEv1.Ret, socketpairEv2.Ret) } +func TestAcceptEventSerialization(t *testing.T) { + acceptEv1 := AcceptEvent{ + EventType: ENTER_ACCEPT_EVENT, + TraceId: SYS_ENTER_ACCEPT4, + Time: 3456, + Pid: 34, + Tid: 35, + Fd: 9, + Ret: -1, + } + bytes, err := acceptEv1.Bytes() + if err != nil { + t.Error(err) + } + acceptEv2 := NewAcceptEvent(bytes) + + assertEquals(t, acceptEv1.EventType, acceptEv2.EventType) + assertEquals(t, acceptEv1.TraceId, acceptEv2.TraceId) + assertEquals(t, acceptEv1.Time, acceptEv2.Time) + assertEquals(t, acceptEv1.Pid, acceptEv2.Pid) + assertEquals(t, acceptEv1.Tid, acceptEv2.Tid) + assertEquals(t, acceptEv1.Fd, acceptEv2.Fd) + assertEquals(t, acceptEv1.Ret, acceptEv2.Ret) +} + func TestEqualsDifferentTypes(t *testing.T) { openEv := OpenEvent{EventType: ENTER_OPEN_EVENT, TraceId: SYS_ENTER_OPENAT, Time: 1, Pid: 1, Tid: 1} nullEv := NullEvent{EventType: ENTER_NULL_EVENT, TraceId: SYS_ENTER_SYNC, Time: 1, Pid: 1, Tid: 1} |
