diff --git a/Makefile b/Makefile index 31fc3195..61946fa3 100644 --- a/Makefile +++ b/Makefile @@ -91,8 +91,8 @@ lint: deps fmt fmt: go fmt ./... clang-format -i bpf/ptcpdump.c - clang-format -i bpf/headers/custom.h - clang-format -i bpf/headers/gotls.h + clang-format -i bpf/*.h + clang-format -i bpf/headers/*.h .PHONY: vet vet: diff --git a/bpf/bpf.go b/bpf/bpf.go index 689719ea..f17d9a36 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -19,7 +19,7 @@ import ( ) // $TARGET is set by the Makefile -//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -no-strip -target $TARGET -type gconfig_t -type packet_event_t -type exec_event_t -type exit_event_t -type flow_pid_key_t -type process_meta_t -type packet_event_meta_t -type go_keylog_event_t Bpf ./ptcpdump.c -- -I./headers -I./headers/$TARGET -I. -Wall +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -no-strip -target $TARGET -type gconfig_t -type packet_event_t -type exec_event_t -type exit_event_t -type flow_pid_key_t -type process_meta_t -type packet_event_meta_t -type go_keylog_event_t -type new_netdevice_event_t -type netdevice_change_event_t -type mount_event_t Bpf ./ptcpdump.c -- -I./headers -I./headers/$TARGET -I. -Wall const tcFilterName = "ptcpdump" const logSzie = ebpf.DefaultVerifierLogSize * 64 @@ -47,6 +47,8 @@ type Options struct { pidnsIds []uint32 netnsIds []uint32 maxPayloadSize uint32 + hookMount bool + hookNetDev bool kernelTypes *btf.Spec } @@ -276,6 +278,39 @@ func (b *BPF) AttachKprobes() error { b.links = append(b.links, lk) } + if b.opts.hookNetDev { + lk, err = link.Kprobe("register_netdevice", + b.objs.KprobeRegisterNetdevice, &link.KprobeOptions{}) + if err != nil { + return fmt.Errorf("attach kprobe/register_netdevice: %w", err) + } + b.links = append(b.links, lk) + lk, err = link.Kretprobe("register_netdevice", + b.objs.KretprobeRegisterNetdevice, &link.KprobeOptions{}) + if err != nil { + return fmt.Errorf("attach kretprobe/register_netdevice: %w", err) + } + b.links = append(b.links, lk) + lk, err = link.Kretprobe("__dev_get_by_index", + b.objs.KretprobeDevGetByIndex, &link.KprobeOptions{}) + if err != nil { + return fmt.Errorf("attach kretprobe/__dev_get_by_index: %w", err) + } + b.links = append(b.links, lk) + lk, err = link.Kprobe("__dev_change_net_namespace", + b.objs.KprobeDevChangeNetNamespace, &link.KprobeOptions{}) + if err != nil { + return fmt.Errorf("attach kprobe/__dev_change_net_namespace: %w", err) + } + b.links = append(b.links, lk) + lk, err = link.Kretprobe("__dev_change_net_namespace", + b.objs.KretprobeDevChangeNetNamespace, &link.KprobeOptions{}) + if err != nil { + return fmt.Errorf("attach kretprobe/__dev_change_net_namespace: %w", err) + } + b.links = append(b.links, lk) + } + return nil } @@ -309,6 +344,19 @@ func (b *BPF) AttachTracepoints() error { b.links = append(b.links, lk) } + if b.opts.hookMount { + lk, err = link.Tracepoint("syscalls", "sys_enter_mount", b.objs.TracepointSyscallsSysEnterMount, &link.TracepointOptions{}) + if err != nil { + return fmt.Errorf("attach tracepoint/syscalls/sys_enter_mount: %w", err) + } + b.links = append(b.links, lk) + lk, err = link.Tracepoint("syscalls", "sys_exit_mount", b.objs.TracepointSyscallsSysExitMount, &link.TracepointOptions{}) + if err != nil { + return fmt.Errorf("attach tracepoint/syscalls/sys_exit_mount: %w", err) + } + b.links = append(b.links, lk) + } + return nil } @@ -386,9 +434,9 @@ func attachTcHook(ifindex int, prog *ebpf.Program, ingress bool) (func(), error) }, }, } - log.Infof("try to add tc filter with handle %d", hid) + log.Infof("try to add tc filter with handle %d to %d", hid, ifindex) if err = tcnl.Filter().Add(filter); err != nil { - log.Infof("add tc filter: %s", err) + log.Infof("add tc filter: %+v", err) if !errors.Is(err, unix.EEXIST) { return closeFunc, fmt.Errorf("add tc filter: %w", err) } else { @@ -580,6 +628,16 @@ func (opts *Options) WithMaxPayloadSize(n uint32) *Options { return opts } +func (opts *Options) WithHookMount(v bool) *Options { + opts.hookMount = v + return opts +} + +func (opts *Options) WithHookNetDev(v bool) *Options { + opts.hookNetDev = v + return opts +} + func (opts *Options) WithKernelTypes(spec *btf.Spec) *Options { opts.kernelTypes = spec return opts diff --git a/bpf/bpf_arm64_bpfel.go b/bpf/bpf_arm64_bpfel.go index f182054b..3848f73c 100644 --- a/bpf/bpf_arm64_bpfel.go +++ b/bpf/bpf_arm64_bpfel.go @@ -12,6 +12,12 @@ import ( "github.com/cilium/ebpf" ) +type BpfEnterMountBufT struct { + Fs uint64 + Src uint64 + Dest uint64 +} + type BpfExecEventT struct { Meta BpfProcessMetaT FilenameTruncated uint8 @@ -57,6 +63,12 @@ type BpfGoKeylogEventT struct { SecretLen uint8 } +type BpfMountEventT struct { + Fs [8]int8 + Src [4096]int8 + Dest [4096]int8 +} + type BpfNatFlowT struct { Saddr [2]uint64 Daddr [2]uint64 @@ -65,6 +77,24 @@ type BpfNatFlowT struct { _ [4]byte } +type BpfNetdeviceBufT struct { + Dev uint64 + Net uint64 +} + +type BpfNetdeviceChangeEventT struct { + OldDevice BpfNetdeviceT + NewDevice BpfNetdeviceT +} + +type BpfNetdeviceT struct { + NetnsId uint32 + Ifindex uint32 + Name [16]int8 +} + +type BpfNewNetdeviceEventT struct{ Dev BpfNetdeviceT } + type BpfPacketEventMetaT struct { Timestamp uint64 PacketType uint8 @@ -130,17 +160,24 @@ type BpfSpecs struct { type BpfProgramSpecs struct { CgroupSockCreate *ebpf.ProgramSpec `ebpf:"cgroup__sock_create"` CgroupSockRelease *ebpf.ProgramSpec `ebpf:"cgroup__sock_release"` + KprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.ProgramSpec `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.ProgramSpec `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.ProgramSpec `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.ProgramSpec `ebpf:"tc_egress"` TcIngress *ebpf.ProgramSpec `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } @@ -149,22 +186,29 @@ type BpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type BpfMapSpecs struct { - ConfigMap *ebpf.MapSpec `ebpf:"config_map"` - ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` - ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` - ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` - FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` - PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` - SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.MapSpec `ebpf:"config_map"` + EnterMountBufs *ebpf.MapSpec `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` + ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` + ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` + FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` + MountEventStack *ebpf.MapSpec `ebpf:"mount_event_stack"` + MountEvents *ebpf.MapSpec `ebpf:"mount_events"` + NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.MapSpec `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.MapSpec `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.MapSpec `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` + PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` + SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.MapSpec `ebpf:"tid_netdevice_map"` } // BpfObjects contains all objects after they have been loaded into the kernel. @@ -186,27 +230,35 @@ func (o *BpfObjects) Close() error { // // It can be passed to LoadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type BpfMaps struct { - ConfigMap *ebpf.Map `ebpf:"config_map"` - ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` - ExecEvents *ebpf.Map `ebpf:"exec_events"` - ExitEvents *ebpf.Map `ebpf:"exit_events"` - FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` - PacketEvents *ebpf.Map `ebpf:"packet_events"` - SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.Map `ebpf:"config_map"` + EnterMountBufs *ebpf.Map `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` + ExecEvents *ebpf.Map `ebpf:"exec_events"` + ExitEvents *ebpf.Map `ebpf:"exit_events"` + FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` + MountEventStack *ebpf.Map `ebpf:"mount_event_stack"` + MountEvents *ebpf.Map `ebpf:"mount_events"` + NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.Map `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.Map `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.Map `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` + PacketEvents *ebpf.Map `ebpf:"packet_events"` + SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.Map `ebpf:"tid_netdevice_map"` } func (m *BpfMaps) Close() error { return _BpfClose( m.ConfigMap, + m.EnterMountBufs, m.ExecEventStack, m.ExecEvents, m.ExitEvents, @@ -218,10 +270,16 @@ func (m *BpfMaps) Close() error { m.FlowPidMap, m.GoKeylogBufStorage, m.GoKeylogEvents, + m.MountEventStack, + m.MountEvents, m.NatFlowMap, + m.NetdeviceBufs, + m.NetdeviceChangeEvents, + m.NewNetdeviceEvents, m.PacketEventStack, m.PacketEvents, m.SockCookiePidMap, + m.TidNetdeviceMap, ) } @@ -231,17 +289,24 @@ func (m *BpfMaps) Close() error { type BpfPrograms struct { CgroupSockCreate *ebpf.Program `ebpf:"cgroup__sock_create"` CgroupSockRelease *ebpf.Program `ebpf:"cgroup__sock_release"` + KprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.Program `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.Program `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.Program `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.Program `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.Program `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.Program `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.Program `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.Program `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.Program `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.Program `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.Program `ebpf:"tc_egress"` TcIngress *ebpf.Program `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } @@ -250,17 +315,24 @@ func (p *BpfPrograms) Close() error { return _BpfClose( p.CgroupSockCreate, p.CgroupSockRelease, + p.KprobeDevChangeNetNamespace, p.KprobeNfNatManipPkt, p.KprobeNfNatPacket, + p.KprobeRegisterNetdevice, p.KprobeSecuritySkClassifyFlow, p.KprobeTcpSendmsg, p.KprobeUdpSendSkb, p.KprobeUdpSendmsg, + p.KretprobeDevChangeNetNamespace, + p.KretprobeDevGetByIndex, + p.KretprobeRegisterNetdevice, p.RawTracepointSchedProcessExec, p.RawTracepointSchedProcessExit, p.RawTracepointSchedProcessFork, p.TcEgress, p.TcIngress, + p.TracepointSyscallsSysEnterMount, + p.TracepointSyscallsSysExitMount, p.UprobeGoBuiltinTlsWriteKeyLog, p.UprobeGoBuiltinTlsWriteKeyLogRet, ) diff --git a/bpf/bpf_arm64_bpfel.o b/bpf/bpf_arm64_bpfel.o index 9408f8cd..5c466392 100644 Binary files a/bpf/bpf_arm64_bpfel.o and b/bpf/bpf_arm64_bpfel.o differ diff --git a/bpf/bpf_legacy.go b/bpf/bpf_legacy.go index 0e918770..3811302d 100644 --- a/bpf/bpf_legacy.go +++ b/bpf/bpf_legacy.go @@ -23,6 +23,13 @@ type BpfObjectsForLegacyKernel struct { RawTracepointSchedProcessExec *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.Program `ebpf:"raw_tracepoint__sched_process_fork"` + KprobeRegisterNetdevice *ebpf.Program `ebpf:"kprobe__register_netdevice"` + KretprobeRegisterNetdevice *ebpf.Program `ebpf:"kretprobe__register_netdevice"` + KprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace"` + KretprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.Program `ebpf:"kretprobe__dev_get_by_index"` + TracepointSyscallsSysEnterMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_mount"` TcEgress *ebpf.Program `ebpf:"tc_egress"` TcIngress *ebpf.Program `ebpf:"tc_ingress"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log"` @@ -41,6 +48,13 @@ func (b *BpfObjects) FromLegacy(o *BpfObjectsForLegacyKernel) { b.RawTracepointSchedProcessExec = o.RawTracepointSchedProcessExec b.RawTracepointSchedProcessExit = o.RawTracepointSchedProcessExit b.RawTracepointSchedProcessFork = o.RawTracepointSchedProcessFork + b.KprobeRegisterNetdevice = o.KprobeRegisterNetdevice + b.KretprobeRegisterNetdevice = o.KretprobeRegisterNetdevice + b.KprobeDevChangeNetNamespace = o.KprobeDevChangeNetNamespace + b.KretprobeDevChangeNetNamespace = o.KretprobeDevChangeNetNamespace + b.KretprobeDevGetByIndex = o.KretprobeDevGetByIndex + b.TracepointSyscallsSysEnterMount = o.TracepointSyscallsSysEnterMount + b.TracepointSyscallsSysExitMount = o.TracepointSyscallsSysExitMount b.TcEgress = o.TcEgress b.TcIngress = o.TcIngress b.UprobeGoBuiltinTlsWriteKeyLog = o.UprobeGoBuiltinTlsWriteKeyLog diff --git a/bpf/bpf_legacy_arm64_bpfel.go b/bpf/bpf_legacy_arm64_bpfel.go index c9c91c08..72ee7d07 100644 --- a/bpf/bpf_legacy_arm64_bpfel.go +++ b/bpf/bpf_legacy_arm64_bpfel.go @@ -53,17 +53,24 @@ type bpf_legacySpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpf_legacyProgramSpecs struct { + KprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.ProgramSpec `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.ProgramSpec `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.ProgramSpec `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.ProgramSpec `ebpf:"tc_egress"` TcIngress *ebpf.ProgramSpec `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } @@ -72,22 +79,29 @@ type bpf_legacyProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpf_legacyMapSpecs struct { - ConfigMap *ebpf.MapSpec `ebpf:"config_map"` - ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` - ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` - ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` - FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` - PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` - SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.MapSpec `ebpf:"config_map"` + EnterMountBufs *ebpf.MapSpec `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` + ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` + ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` + FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` + MountEventStack *ebpf.MapSpec `ebpf:"mount_event_stack"` + MountEvents *ebpf.MapSpec `ebpf:"mount_events"` + NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.MapSpec `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.MapSpec `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.MapSpec `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` + PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` + SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.MapSpec `ebpf:"tid_netdevice_map"` } // bpf_legacyObjects contains all objects after they have been loaded into the kernel. @@ -109,27 +123,35 @@ func (o *bpf_legacyObjects) Close() error { // // It can be passed to loadBpf_legacyObjects or ebpf.CollectionSpec.LoadAndAssign. type bpf_legacyMaps struct { - ConfigMap *ebpf.Map `ebpf:"config_map"` - ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` - ExecEvents *ebpf.Map `ebpf:"exec_events"` - ExitEvents *ebpf.Map `ebpf:"exit_events"` - FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` - PacketEvents *ebpf.Map `ebpf:"packet_events"` - SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.Map `ebpf:"config_map"` + EnterMountBufs *ebpf.Map `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` + ExecEvents *ebpf.Map `ebpf:"exec_events"` + ExitEvents *ebpf.Map `ebpf:"exit_events"` + FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` + MountEventStack *ebpf.Map `ebpf:"mount_event_stack"` + MountEvents *ebpf.Map `ebpf:"mount_events"` + NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.Map `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.Map `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.Map `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` + PacketEvents *ebpf.Map `ebpf:"packet_events"` + SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.Map `ebpf:"tid_netdevice_map"` } func (m *bpf_legacyMaps) Close() error { return _Bpf_legacyClose( m.ConfigMap, + m.EnterMountBufs, m.ExecEventStack, m.ExecEvents, m.ExitEvents, @@ -141,10 +163,16 @@ func (m *bpf_legacyMaps) Close() error { m.FlowPidMap, m.GoKeylogBufStorage, m.GoKeylogEvents, + m.MountEventStack, + m.MountEvents, m.NatFlowMap, + m.NetdeviceBufs, + m.NetdeviceChangeEvents, + m.NewNetdeviceEvents, m.PacketEventStack, m.PacketEvents, m.SockCookiePidMap, + m.TidNetdeviceMap, ) } @@ -152,34 +180,48 @@ func (m *bpf_legacyMaps) Close() error { // // It can be passed to loadBpf_legacyObjects or ebpf.CollectionSpec.LoadAndAssign. type bpf_legacyPrograms struct { + KprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.Program `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.Program `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.Program `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.Program `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.Program `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.Program `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.Program `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.Program `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.Program `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.Program `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.Program `ebpf:"tc_egress"` TcIngress *ebpf.Program `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } func (p *bpf_legacyPrograms) Close() error { return _Bpf_legacyClose( + p.KprobeDevChangeNetNamespace, p.KprobeNfNatManipPkt, p.KprobeNfNatPacket, + p.KprobeRegisterNetdevice, p.KprobeSecuritySkClassifyFlow, p.KprobeTcpSendmsg, p.KprobeUdpSendSkb, p.KprobeUdpSendmsg, + p.KretprobeDevChangeNetNamespace, + p.KretprobeDevGetByIndex, + p.KretprobeRegisterNetdevice, p.RawTracepointSchedProcessExec, p.RawTracepointSchedProcessExit, p.RawTracepointSchedProcessFork, p.TcEgress, p.TcIngress, + p.TracepointSyscallsSysEnterMount, + p.TracepointSyscallsSysExitMount, p.UprobeGoBuiltinTlsWriteKeyLog, p.UprobeGoBuiltinTlsWriteKeyLogRet, ) diff --git a/bpf/bpf_legacy_arm64_bpfel.o b/bpf/bpf_legacy_arm64_bpfel.o index 9e028656..b6838134 100644 Binary files a/bpf/bpf_legacy_arm64_bpfel.o and b/bpf/bpf_legacy_arm64_bpfel.o differ diff --git a/bpf/bpf_legacy_x86_bpfel.go b/bpf/bpf_legacy_x86_bpfel.go index 16bdc130..4ddfdc43 100644 --- a/bpf/bpf_legacy_x86_bpfel.go +++ b/bpf/bpf_legacy_x86_bpfel.go @@ -53,17 +53,24 @@ type bpf_legacySpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpf_legacyProgramSpecs struct { + KprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.ProgramSpec `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.ProgramSpec `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.ProgramSpec `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.ProgramSpec `ebpf:"tc_egress"` TcIngress *ebpf.ProgramSpec `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } @@ -72,22 +79,29 @@ type bpf_legacyProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpf_legacyMapSpecs struct { - ConfigMap *ebpf.MapSpec `ebpf:"config_map"` - ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` - ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` - ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` - FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` - PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` - SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.MapSpec `ebpf:"config_map"` + EnterMountBufs *ebpf.MapSpec `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` + ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` + ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` + FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` + MountEventStack *ebpf.MapSpec `ebpf:"mount_event_stack"` + MountEvents *ebpf.MapSpec `ebpf:"mount_events"` + NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.MapSpec `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.MapSpec `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.MapSpec `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` + PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` + SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.MapSpec `ebpf:"tid_netdevice_map"` } // bpf_legacyObjects contains all objects after they have been loaded into the kernel. @@ -109,27 +123,35 @@ func (o *bpf_legacyObjects) Close() error { // // It can be passed to loadBpf_legacyObjects or ebpf.CollectionSpec.LoadAndAssign. type bpf_legacyMaps struct { - ConfigMap *ebpf.Map `ebpf:"config_map"` - ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` - ExecEvents *ebpf.Map `ebpf:"exec_events"` - ExitEvents *ebpf.Map `ebpf:"exit_events"` - FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` - PacketEvents *ebpf.Map `ebpf:"packet_events"` - SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.Map `ebpf:"config_map"` + EnterMountBufs *ebpf.Map `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` + ExecEvents *ebpf.Map `ebpf:"exec_events"` + ExitEvents *ebpf.Map `ebpf:"exit_events"` + FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` + MountEventStack *ebpf.Map `ebpf:"mount_event_stack"` + MountEvents *ebpf.Map `ebpf:"mount_events"` + NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.Map `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.Map `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.Map `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` + PacketEvents *ebpf.Map `ebpf:"packet_events"` + SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.Map `ebpf:"tid_netdevice_map"` } func (m *bpf_legacyMaps) Close() error { return _Bpf_legacyClose( m.ConfigMap, + m.EnterMountBufs, m.ExecEventStack, m.ExecEvents, m.ExitEvents, @@ -141,10 +163,16 @@ func (m *bpf_legacyMaps) Close() error { m.FlowPidMap, m.GoKeylogBufStorage, m.GoKeylogEvents, + m.MountEventStack, + m.MountEvents, m.NatFlowMap, + m.NetdeviceBufs, + m.NetdeviceChangeEvents, + m.NewNetdeviceEvents, m.PacketEventStack, m.PacketEvents, m.SockCookiePidMap, + m.TidNetdeviceMap, ) } @@ -152,34 +180,48 @@ func (m *bpf_legacyMaps) Close() error { // // It can be passed to loadBpf_legacyObjects or ebpf.CollectionSpec.LoadAndAssign. type bpf_legacyPrograms struct { + KprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.Program `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.Program `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.Program `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.Program `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.Program `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.Program `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.Program `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.Program `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.Program `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.Program `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.Program `ebpf:"tc_egress"` TcIngress *ebpf.Program `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } func (p *bpf_legacyPrograms) Close() error { return _Bpf_legacyClose( + p.KprobeDevChangeNetNamespace, p.KprobeNfNatManipPkt, p.KprobeNfNatPacket, + p.KprobeRegisterNetdevice, p.KprobeSecuritySkClassifyFlow, p.KprobeTcpSendmsg, p.KprobeUdpSendSkb, p.KprobeUdpSendmsg, + p.KretprobeDevChangeNetNamespace, + p.KretprobeDevGetByIndex, + p.KretprobeRegisterNetdevice, p.RawTracepointSchedProcessExec, p.RawTracepointSchedProcessExit, p.RawTracepointSchedProcessFork, p.TcEgress, p.TcIngress, + p.TracepointSyscallsSysEnterMount, + p.TracepointSyscallsSysExitMount, p.UprobeGoBuiltinTlsWriteKeyLog, p.UprobeGoBuiltinTlsWriteKeyLogRet, ) diff --git a/bpf/bpf_legacy_x86_bpfel.o b/bpf/bpf_legacy_x86_bpfel.o index a835212f..208574e2 100644 Binary files a/bpf/bpf_legacy_x86_bpfel.o and b/bpf/bpf_legacy_x86_bpfel.o differ diff --git a/bpf/bpf_x86_bpfel.go b/bpf/bpf_x86_bpfel.go index 18961351..88cb8958 100644 --- a/bpf/bpf_x86_bpfel.go +++ b/bpf/bpf_x86_bpfel.go @@ -12,6 +12,12 @@ import ( "github.com/cilium/ebpf" ) +type BpfEnterMountBufT struct { + Fs uint64 + Src uint64 + Dest uint64 +} + type BpfExecEventT struct { Meta BpfProcessMetaT FilenameTruncated uint8 @@ -57,6 +63,12 @@ type BpfGoKeylogEventT struct { SecretLen uint8 } +type BpfMountEventT struct { + Fs [8]int8 + Src [4096]int8 + Dest [4096]int8 +} + type BpfNatFlowT struct { Saddr [2]uint64 Daddr [2]uint64 @@ -65,6 +77,24 @@ type BpfNatFlowT struct { _ [4]byte } +type BpfNetdeviceBufT struct { + Dev uint64 + Net uint64 +} + +type BpfNetdeviceChangeEventT struct { + OldDevice BpfNetdeviceT + NewDevice BpfNetdeviceT +} + +type BpfNetdeviceT struct { + NetnsId uint32 + Ifindex uint32 + Name [16]int8 +} + +type BpfNewNetdeviceEventT struct{ Dev BpfNetdeviceT } + type BpfPacketEventMetaT struct { Timestamp uint64 PacketType uint8 @@ -130,17 +160,24 @@ type BpfSpecs struct { type BpfProgramSpecs struct { CgroupSockCreate *ebpf.ProgramSpec `ebpf:"cgroup__sock_create"` CgroupSockRelease *ebpf.ProgramSpec `ebpf:"cgroup__sock_release"` + KprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.ProgramSpec `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.ProgramSpec `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.ProgramSpec `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.ProgramSpec `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.ProgramSpec `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.ProgramSpec `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.ProgramSpec `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.ProgramSpec `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.ProgramSpec `ebpf:"tc_egress"` TcIngress *ebpf.ProgramSpec `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.ProgramSpec `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.ProgramSpec `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } @@ -149,22 +186,29 @@ type BpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type BpfMapSpecs struct { - ConfigMap *ebpf.MapSpec `ebpf:"config_map"` - ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` - ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` - ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` - FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` - PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` - SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.MapSpec `ebpf:"config_map"` + EnterMountBufs *ebpf.MapSpec `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.MapSpec `ebpf:"exec_event_stack"` + ExecEvents *ebpf.MapSpec `ebpf:"exec_events"` + ExitEvents *ebpf.MapSpec `ebpf:"exit_events"` + FilterByKernelCount *ebpf.MapSpec `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.MapSpec `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.MapSpec `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.MapSpec `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.MapSpec `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.MapSpec `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.MapSpec `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.MapSpec `ebpf:"go_keylog_events"` + MountEventStack *ebpf.MapSpec `ebpf:"mount_event_stack"` + MountEvents *ebpf.MapSpec `ebpf:"mount_events"` + NatFlowMap *ebpf.MapSpec `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.MapSpec `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.MapSpec `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.MapSpec `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.MapSpec `ebpf:"packet_event_stack"` + PacketEvents *ebpf.MapSpec `ebpf:"packet_events"` + SockCookiePidMap *ebpf.MapSpec `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.MapSpec `ebpf:"tid_netdevice_map"` } // BpfObjects contains all objects after they have been loaded into the kernel. @@ -186,27 +230,35 @@ func (o *BpfObjects) Close() error { // // It can be passed to LoadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type BpfMaps struct { - ConfigMap *ebpf.Map `ebpf:"config_map"` - ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` - ExecEvents *ebpf.Map `ebpf:"exec_events"` - ExitEvents *ebpf.Map `ebpf:"exit_events"` - FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` - FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` - FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` - FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` - FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` - FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` - GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` - GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` - NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` - PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` - PacketEvents *ebpf.Map `ebpf:"packet_events"` - SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + ConfigMap *ebpf.Map `ebpf:"config_map"` + EnterMountBufs *ebpf.Map `ebpf:"enter_mount_bufs"` + ExecEventStack *ebpf.Map `ebpf:"exec_event_stack"` + ExecEvents *ebpf.Map `ebpf:"exec_events"` + ExitEvents *ebpf.Map `ebpf:"exit_events"` + FilterByKernelCount *ebpf.Map `ebpf:"filter_by_kernel_count"` + FilterMntnsMap *ebpf.Map `ebpf:"filter_mntns_map"` + FilterNetnsMap *ebpf.Map `ebpf:"filter_netns_map"` + FilterPidMap *ebpf.Map `ebpf:"filter_pid_map"` + FilterPidnsMap *ebpf.Map `ebpf:"filter_pidns_map"` + FlowPidMap *ebpf.Map `ebpf:"flow_pid_map"` + GoKeylogBufStorage *ebpf.Map `ebpf:"go_keylog_buf_storage"` + GoKeylogEvents *ebpf.Map `ebpf:"go_keylog_events"` + MountEventStack *ebpf.Map `ebpf:"mount_event_stack"` + MountEvents *ebpf.Map `ebpf:"mount_events"` + NatFlowMap *ebpf.Map `ebpf:"nat_flow_map"` + NetdeviceBufs *ebpf.Map `ebpf:"netdevice_bufs"` + NetdeviceChangeEvents *ebpf.Map `ebpf:"netdevice_change_events"` + NewNetdeviceEvents *ebpf.Map `ebpf:"new_netdevice_events"` + PacketEventStack *ebpf.Map `ebpf:"packet_event_stack"` + PacketEvents *ebpf.Map `ebpf:"packet_events"` + SockCookiePidMap *ebpf.Map `ebpf:"sock_cookie_pid_map"` + TidNetdeviceMap *ebpf.Map `ebpf:"tid_netdevice_map"` } func (m *BpfMaps) Close() error { return _BpfClose( m.ConfigMap, + m.EnterMountBufs, m.ExecEventStack, m.ExecEvents, m.ExitEvents, @@ -218,10 +270,16 @@ func (m *BpfMaps) Close() error { m.FlowPidMap, m.GoKeylogBufStorage, m.GoKeylogEvents, + m.MountEventStack, + m.MountEvents, m.NatFlowMap, + m.NetdeviceBufs, + m.NetdeviceChangeEvents, + m.NewNetdeviceEvents, m.PacketEventStack, m.PacketEvents, m.SockCookiePidMap, + m.TidNetdeviceMap, ) } @@ -231,17 +289,24 @@ func (m *BpfMaps) Close() error { type BpfPrograms struct { CgroupSockCreate *ebpf.Program `ebpf:"cgroup__sock_create"` CgroupSockRelease *ebpf.Program `ebpf:"cgroup__sock_release"` + KprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace"` KprobeNfNatManipPkt *ebpf.Program `ebpf:"kprobe__nf_nat_manip_pkt"` KprobeNfNatPacket *ebpf.Program `ebpf:"kprobe__nf_nat_packet"` + KprobeRegisterNetdevice *ebpf.Program `ebpf:"kprobe__register_netdevice"` KprobeSecuritySkClassifyFlow *ebpf.Program `ebpf:"kprobe__security_sk_classify_flow"` KprobeTcpSendmsg *ebpf.Program `ebpf:"kprobe__tcp_sendmsg"` KprobeUdpSendSkb *ebpf.Program `ebpf:"kprobe__udp_send_skb"` KprobeUdpSendmsg *ebpf.Program `ebpf:"kprobe__udp_sendmsg"` + KretprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kretprobe__dev_change_net_namespace"` + KretprobeDevGetByIndex *ebpf.Program `ebpf:"kretprobe__dev_get_by_index"` + KretprobeRegisterNetdevice *ebpf.Program `ebpf:"kretprobe__register_netdevice"` RawTracepointSchedProcessExec *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exec"` RawTracepointSchedProcessExit *ebpf.Program `ebpf:"raw_tracepoint__sched_process_exit"` RawTracepointSchedProcessFork *ebpf.Program `ebpf:"raw_tracepoint__sched_process_fork"` TcEgress *ebpf.Program `ebpf:"tc_egress"` TcIngress *ebpf.Program `ebpf:"tc_ingress"` + TracepointSyscallsSysEnterMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_enter_mount"` + TracepointSyscallsSysExitMount *ebpf.Program `ebpf:"tracepoint__syscalls__sys_exit_mount"` UprobeGoBuiltinTlsWriteKeyLog *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log"` UprobeGoBuiltinTlsWriteKeyLogRet *ebpf.Program `ebpf:"uprobe__go_builtin__tls__write_key_log__ret"` } @@ -250,17 +315,24 @@ func (p *BpfPrograms) Close() error { return _BpfClose( p.CgroupSockCreate, p.CgroupSockRelease, + p.KprobeDevChangeNetNamespace, p.KprobeNfNatManipPkt, p.KprobeNfNatPacket, + p.KprobeRegisterNetdevice, p.KprobeSecuritySkClassifyFlow, p.KprobeTcpSendmsg, p.KprobeUdpSendSkb, p.KprobeUdpSendmsg, + p.KretprobeDevChangeNetNamespace, + p.KretprobeDevGetByIndex, + p.KretprobeRegisterNetdevice, p.RawTracepointSchedProcessExec, p.RawTracepointSchedProcessExit, p.RawTracepointSchedProcessFork, p.TcEgress, p.TcIngress, + p.TracepointSyscallsSysEnterMount, + p.TracepointSyscallsSysExitMount, p.UprobeGoBuiltinTlsWriteKeyLog, p.UprobeGoBuiltinTlsWriteKeyLogRet, ) diff --git a/bpf/bpf_x86_bpfel.o b/bpf/bpf_x86_bpfel.o index 68bc35d7..d232b7e7 100644 Binary files a/bpf/bpf_x86_bpfel.o and b/bpf/bpf_x86_bpfel.o differ diff --git a/bpf/event.go b/bpf/event.go index 6137548a..2287f437 100644 --- a/bpf/event.go +++ b/bpf/event.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/mozillazg/ptcpdump/internal/utils" "io" "os" "unsafe" @@ -146,7 +147,7 @@ func (b *BPF) PullGoKeyLogEvents(ctx context.Context, chanSize int) (<-chan BpfG pageSize := os.Getpagesize() log.Infof("pagesize is %d", pageSize) perCPUBuffer := pageSize * 4 - eventSize := int(unsafe.Sizeof(BpfExecEventT{})) + eventSize := int(unsafe.Sizeof(BpfGoKeylogEventT{})) if eventSize >= perCPUBuffer { perCPUBuffer = perCPUBuffer * (1 + (eventSize / perCPUBuffer)) } @@ -198,6 +199,208 @@ func (b *BPF) handleGoKeyLogEvents(ctx context.Context, reader *perf.Reader, ch } } +func (b *BPF) PullNewNetDeviceEvents(ctx context.Context, chanSize int) (<-chan BpfNewNetdeviceEventT, error) { + pageSize := os.Getpagesize() + log.Infof("pagesize is %d", pageSize) + perCPUBuffer := pageSize * 1 + eventSize := int(unsafe.Sizeof(BpfNewNetdeviceEventT{})) + if eventSize >= perCPUBuffer { + perCPUBuffer = perCPUBuffer * (1 + (eventSize / perCPUBuffer)) + } + log.Infof("use %d as perCPUBuffer", perCPUBuffer) + + reader, err := perf.NewReader(b.objs.NewNetdeviceEvents, perCPUBuffer) + if err != nil { + return nil, fmt.Errorf(": %w", err) + } + ch := make(chan BpfNewNetdeviceEventT, chanSize) + go func() { + defer close(ch) + defer reader.Close() + b.handleNetNetDeviceEvents(ctx, reader, ch) + }() + + return ch, nil +} + +func (b *BPF) handleNetNetDeviceEvents(ctx context.Context, reader *perf.Reader, ch chan<- BpfNewNetdeviceEventT) { + for { + select { + case <-ctx.Done(): + return + default: + } + + record, err := reader.Read() + if err != nil { + if errors.Is(err, perf.ErrClosed) { + return + } + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + log.Infof("got EOF error: %s", err) + continue + } + log.Errorf("read go tls keylog event failed: %s", err) + continue + } + event, err := parseNewNetDeviceEvent(record.RawSample) + if err != nil { + log.Errorf("parse go tls keylog event failed: %s", err) + } else { + ch <- *event + dev := event.Dev + log.Infof("new BpfNewNetdeviceEventT: name %s, ifindex %d, netns, %d", + utils.GoString(dev.Name[:]), dev.Ifindex, dev.NetnsId) + } + if record.LostSamples > 0 { + // TODO: XXX + } + } +} + +func parseNewNetDeviceEvent(rawSample []byte) (*BpfNewNetdeviceEventT, error) { + event := BpfNewNetdeviceEventT{} + if err := binary.Read(bytes.NewBuffer(rawSample), binary.LittleEndian, &event); err != nil { + return nil, fmt.Errorf("parse event: %w", err) + } + return &event, nil +} + +func (b *BPF) PullNetDeviceChangeEvents(ctx context.Context, chanSize int) (<-chan BpfNetdeviceChangeEventT, error) { + pageSize := os.Getpagesize() + log.Infof("pagesize is %d", pageSize) + perCPUBuffer := pageSize * 1 + eventSize := int(unsafe.Sizeof(BpfNetdeviceChangeEventT{})) + if eventSize >= perCPUBuffer { + perCPUBuffer = perCPUBuffer * (1 + (eventSize / perCPUBuffer)) + } + log.Infof("use %d as perCPUBuffer", perCPUBuffer) + + reader, err := perf.NewReader(b.objs.NetdeviceChangeEvents, perCPUBuffer) + if err != nil { + return nil, fmt.Errorf(": %w", err) + } + ch := make(chan BpfNetdeviceChangeEventT, chanSize) + go func() { + defer close(ch) + defer reader.Close() + b.handleNetDeviceChangeEvents(ctx, reader, ch) + }() + + return ch, nil +} + +func (b *BPF) handleNetDeviceChangeEvents(ctx context.Context, reader *perf.Reader, ch chan<- BpfNetdeviceChangeEventT) { + for { + select { + case <-ctx.Done(): + return + default: + } + + record, err := reader.Read() + if err != nil { + if errors.Is(err, perf.ErrClosed) { + return + } + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + log.Infof("got EOF error: %s", err) + continue + } + log.Errorf("read go tls keylog event failed: %s", err) + continue + } + event, err := parseNetDeviceChangeEvent(record.RawSample) + if err != nil { + log.Errorf("parse go tls keylog event failed: %s", err) + } else { + ch <- *event + oldDev := event.OldDevice + newDev := event.NewDevice + log.Infof("new BpfNetdeviceChangeEventT: (name %s, ifindex %d, netns, %d) -> (name %s, ifindex %d, netns, %d)", + utils.GoString(oldDev.Name[:]), oldDev.Ifindex, oldDev.NetnsId, + utils.GoString(newDev.Name[:]), newDev.Ifindex, newDev.NetnsId) + } + if record.LostSamples > 0 { + // TODO: XXX + } + } +} + +func parseNetDeviceChangeEvent(rawSample []byte) (*BpfNetdeviceChangeEventT, error) { + event := BpfNetdeviceChangeEventT{} + if err := binary.Read(bytes.NewBuffer(rawSample), binary.LittleEndian, &event); err != nil { + return nil, fmt.Errorf("parse event: %w", err) + } + return &event, nil +} + +func (b *BPF) PullMountEventEvents(ctx context.Context, chanSize int) (<-chan BpfMountEventT, error) { + pageSize := os.Getpagesize() + log.Infof("pagesize is %d", pageSize) + perCPUBuffer := pageSize * 1 + eventSize := int(unsafe.Sizeof(BpfMountEventT{})) + if eventSize >= perCPUBuffer { + perCPUBuffer = perCPUBuffer * (1 + (eventSize / perCPUBuffer)) + } + log.Infof("use %d as perCPUBuffer", perCPUBuffer) + + reader, err := perf.NewReader(b.objs.MountEvents, perCPUBuffer) + if err != nil { + return nil, fmt.Errorf(": %w", err) + } + ch := make(chan BpfMountEventT, chanSize) + go func() { + defer close(ch) + defer reader.Close() + b.handleMountEvents(ctx, reader, ch) + }() + + return ch, nil +} + +func (b *BPF) handleMountEvents(ctx context.Context, reader *perf.Reader, ch chan<- BpfMountEventT) { + for { + select { + case <-ctx.Done(): + return + default: + } + + record, err := reader.Read() + if err != nil { + if errors.Is(err, perf.ErrClosed) { + return + } + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + log.Infof("got EOF error: %s", err) + continue + } + log.Errorf("read go tls keylog event failed: %s", err) + continue + } + event, err := parseMountEvent(record.RawSample) + if err != nil { + log.Errorf("parse go tls keylog event failed: %s", err) + } else { + ch <- *event + log.Infof("new BpfMountEventT: (source %s, dest %s, fstype, %s)", + utils.GoString(event.Src[:]), utils.GoString(event.Dest[:]), utils.GoString(event.Fs[:])) + } + if record.LostSamples > 0 { + // TODO: XXX + } + } +} + +func parseMountEvent(rawSample []byte) (*BpfMountEventT, error) { + event := BpfMountEventT{} + if err := binary.Read(bytes.NewBuffer(rawSample), binary.LittleEndian, &event); err != nil { + return nil, fmt.Errorf("parse event: %w", err) + } + return &event, nil +} + func parseGoKeyLogEvent(rawSample []byte) (*BpfGoKeylogEventT, error) { event := BpfGoKeylogEventT{} if err := binary.Read(bytes.NewBuffer(rawSample), binary.LittleEndian, &event); err != nil { diff --git a/bpf/headers/compat.h b/bpf/headers/compat.h new file mode 100644 index 00000000..0ec1e133 --- /dev/null +++ b/bpf/headers/compat.h @@ -0,0 +1,41 @@ +#ifndef __PTCPDUMP_COMPAT_H__ +#define __PTCPDUMP_COMPAT_H__ + +#include "vmlinux.h" + +#define TASK_COMM_LEN 16 + +struct gconfig_t { + u8 have_filter; + u8 filter_follow_forks; + char filter_comm[TASK_COMM_LEN]; + u8 filter_comm_enable; + u32 max_payload_size; +}; + +#ifndef LEGACY_KERNEL +static volatile const struct gconfig_t g = {0}; +static const u8 u8_zero = 0; +static const u32 u32_zero = 0; +#endif + +#ifdef LEGACY_KERNEL +#define debug_log(fmt, ...) \ + ({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \ + }) +#define GET_CONFIG() \ + struct gconfig_t g = {0}; \ + u32 configk = 0; \ + struct gconfig_t *configv = bpf_map_lookup_elem(&config_map, &configk); \ + if (configv) { \ + g = *configv; \ + } +#else + +#define debug_log(fmt, ...) ({ bpf_printk(fmt, ##__VA_ARGS__); }) +#define GET_CONFIG() +#endif + +#endif /* __PTCPDUMP_COMPAT_H__ */ diff --git a/bpf/net_dev.h b/bpf/net_dev.h new file mode 100644 index 00000000..182e3aa8 --- /dev/null +++ b/bpf/net_dev.h @@ -0,0 +1,266 @@ +#ifndef __PTCPDUMP_NET_DEV_H__ +#define __PTCPDUMP_NET_DEV_H__ + +#include "compat.h" +#include "vmlinux.h" +#include +#include +#include +#include + +#define IFNAMSIZ 16 +#define FS_NAME_LEN 8 +#define PATH_MAX 4096 + +struct netdevice_buf_t { + u64 dev; + u64 net; +}; + +struct netdevice_t { + u32 netns_id; + u32 ifindex; + char name[IFNAMSIZ]; +}; + +struct new_netdevice_event_t { + struct netdevice_t dev; +}; + +struct netdevice_change_event_t { + struct netdevice_t old_device; + struct netdevice_t new_device; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 100); + __type(key, u64); + __type(value, struct netdevice_buf_t); +} netdevice_bufs SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 100); + __type(key, u64); + __type(value, struct netdevice_t); +} tid_netdevice_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(u32)); +} new_netdevice_events SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(u32)); +} netdevice_change_events SEC(".maps"); + +struct enter_mount_buf_t { + u64 fs; + u64 src; + u64 dest; +}; + +struct mount_event_t { + char fs[FS_NAME_LEN]; + char src[PATH_MAX]; + char dest[PATH_MAX]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 100); + __type(key, u64); + __type(value, struct enter_mount_buf_t); +} enter_mount_bufs SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, struct mount_event_t); +} mount_event_stack SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(u32)); +} mount_events SEC(".maps"); + +const struct new_netdevice_event_t *unused10 __attribute__((unused)); +const struct netdevice_change_event_t *unused11 __attribute__((unused)); + +SEC("tracepoint/syscalls/sys_enter_mount") +int tracepoint__syscalls__sys_enter_mount(struct trace_event_raw_sys_enter *ctx) { + u64 tid = bpf_get_current_pid_tgid(); + struct enter_mount_buf_t val = {0}; + val.src = (u64)BPF_CORE_READ(ctx, args[0]); + val.dest = (u64)BPF_CORE_READ(ctx, args[1]); + val.fs = (u64)BPF_CORE_READ(ctx, args[2]); + bpf_map_update_elem(&enter_mount_bufs, &tid, &val, BPF_ANY); + return 0; +} + +SEC("tracepoint/syscalls/sys_exit_mount") +int tracepoint__syscalls__sys_exit_mount(struct trace_event_raw_sys_exit *ctx) { + if (ctx->ret != 0) { + goto out; + } + + u64 tid = bpf_get_current_pid_tgid(); + struct enter_mount_buf_t *buf; + buf = bpf_map_lookup_elem(&enter_mount_bufs, &tid); + if (!buf) { + goto out; + } + bpf_map_delete_elem(&enter_mount_bufs, &tid); + + struct mount_event_t *event; +#ifdef LEGACY_KERNEL + u32 u32_zero = 0; +#endif + event = bpf_map_lookup_elem(&mount_event_stack, &u32_zero); + if (!event) { + debug_log("[ptcpdump] loopup mount_event_stack failed\n"); + goto out; + } + + bpf_probe_read_user_str(&event->src, sizeof(event->src), (void *)buf->src); + bpf_probe_read_user_str(&event->dest, sizeof(event->dest), (void *)buf->dest); + bpf_probe_read_user_str(&event->fs, sizeof(event->fs), (void *)buf->fs); + + // debug_log("new mount, src: %s, dest: %s, fs: %s\n", event->src, event->dest, event->fs); + int event_ret = bpf_perf_event_output(ctx, &mount_events, BPF_F_CURRENT_CPU, event, sizeof(*event)); + if (event_ret != 0) { + debug_log("[ptcpdump] bpf_perf_event_output mount_events failed: %d\n", event_ret); + } + +out: + return 0; +} + +SEC("kprobe/register_netdevice") +int BPF_KPROBE(kprobe__register_netdevice, struct net_device *dev) { + u64 tid = bpf_get_current_pid_tgid(); + struct netdevice_buf_t val = {0}; + val.dev = (u64)dev; + bpf_map_update_elem(&netdevice_bufs, &tid, &val, BPF_ANY); + + return 0; +} + +static __always_inline void parse_net_device(struct net_device *dev, struct netdevice_t *target) { + BPF_CORE_READ_INTO(&target->ifindex, dev, ifindex); + BPF_CORE_READ_STR_INTO(&target->name, dev, name); + possible_net_t nd_net = BPF_CORE_READ(dev, nd_net); + BPF_CORE_READ_INTO(&target->netns_id, &nd_net, net, ns.inum); +} + +SEC("kretprobe/register_netdevice") +int BPF_KRETPROBE(kretprobe__register_netdevice, long ret) { + if (ret != 0) { + goto out; + } + + u64 tid = bpf_get_current_pid_tgid(); + struct netdevice_buf_t *buf; + buf = bpf_map_lookup_elem(&netdevice_bufs, &tid); + if (!buf) { + goto out; + } + bpf_map_delete_elem(&netdevice_bufs, &tid); + struct net_device *dev = (struct net_device *)buf->dev; + + struct new_netdevice_event_t event = {0}; + struct netdevice_t device = {0}; + parse_net_device(dev, &device); + event.dev = device; + // debug_log("new device: ifindex: %d, name: %s, netns_id: %lu\n", device.ifindex, device.name, device.netns_id); + + int event_ret = bpf_perf_event_output(ctx, &new_netdevice_events, BPF_F_CURRENT_CPU, &event, sizeof(event)); + if (event_ret != 0) { + debug_log("[ptcpdump] bpf_perf_event_output new_device_events failed: %d\n", event_ret); + } + +out: + return 0; +} + +SEC("kretprobe/__dev_get_by_index") +int BPF_KRETPROBE(kretprobe__dev_get_by_index, struct net_device *dev) { + if (!dev) { + goto out; + } + + u64 tid = bpf_get_current_pid_tgid(); + struct netdevice_t device = {0}; + parse_net_device(dev, &device); + bpf_map_update_elem(&tid_netdevice_map, &tid, &device, BPF_ANY); + // debug_log("get device: ifindex: %d, name: %s, netns_id: %lu\n", device.ifindex, device.name, device.netns_id); + +out: + return 0; +} + +SEC("kprobe/__dev_change_net_namespace") +int BPF_KPROBE(kprobe__dev_change_net_namespace, struct net_device *dev, struct net *net) { + u64 tid = bpf_get_current_pid_tgid(); + struct netdevice_buf_t buf = {0}; + buf.dev = (u64)dev; + buf.net = (u64)net; + bpf_map_update_elem(&netdevice_bufs, &tid, &buf, BPF_ANY); + return 0; +} + +static __always_inline void clone_netdevice(struct netdevice_t *origin, struct netdevice_t *target) { + target->ifindex = origin->ifindex; + target->netns_id = origin->netns_id; + __builtin_memcpy(&target->name, &origin->name, sizeof(origin->name)); +} + +SEC("kretprobe/__dev_change_net_namespace") +int BPF_KRETPROBE(kretprobe__dev_change_net_namespace, long ret) { + if (ret != 0) { + goto out; + } + + u64 tid = bpf_get_current_pid_tgid(); + struct netdevice_buf_t *buf; + buf = bpf_map_lookup_elem(&netdevice_bufs, &tid); + if (!buf) { + goto out; + } + struct netdevice_t *old_device; + old_device = bpf_map_lookup_elem(&tid_netdevice_map, &tid); + if (!old_device) { + goto out; + } + bpf_map_delete_elem(&tid_netdevice_map, &tid); + + struct net_device *new_device_buf = (struct net_device *)buf->dev; + struct netdevice_t new_device = {0}; + parse_net_device(new_device_buf, &new_device); + + // debug_log("device (ifindex: %d, netns_id: %lu) change netns to %lu\n", old_device->ifindex, + // old_device->netns_id, + // new_device.netns_id); + // debug_log("device (ifindex: %d, netns_id: %lu) change ifindex to %lu\n", old_device->ifindex, + // old_device->netns_id, + // new_device.ifindex); + + struct netdevice_change_event_t event = {0}; + clone_netdevice(old_device, &event.old_device); + clone_netdevice(&new_device, &event.new_device); + int event_ret = bpf_perf_event_output(ctx, &netdevice_change_events, BPF_F_CURRENT_CPU, &event, sizeof(event)); + if (event_ret != 0) { + debug_log("[ptcpdump] bpf_perf_event_output netdevice_change_events failed: %d\n", event_ret); + } + +out: + return 0; +} + +#endif /* __PTCPDUMP_NET_DEV_H__ */ diff --git a/bpf/ptcpdump.c b/bpf/ptcpdump.c index 9d72de9b..f3a18aa3 100644 --- a/bpf/ptcpdump.c +++ b/bpf/ptcpdump.c @@ -1,8 +1,10 @@ // go:build ignore // +build ignore +#include "compat.h" #include "custom.h" #include "gotls.h" +#include "net_dev.h" #include "vmlinux.h" #include #include @@ -30,20 +32,6 @@ char _license[] SEC("license") = "Dual MIT/GPL"; -struct gconfig_t { - u8 have_filter; - u8 filter_follow_forks; - char filter_comm[TASK_COMM_LEN]; - u8 filter_comm_enable; - u32 max_payload_size; -}; - -#ifndef LEGACY_KERNEL -static volatile const struct gconfig_t g = {0}; -static const u8 u8_zero = 0; -static const u32 u32_zero = 0; -#endif - struct l2_t { u16 h_protocol; }; @@ -223,25 +211,6 @@ const struct process_meta_t *unused4 __attribute__((unused)); const struct exit_event_t *unused5 __attribute__((unused)); const struct gconfig_t *unused6 __attribute__((unused)); -#ifdef LEGACY_KERNEL -#define debug_log(fmt, ...) \ - ({ \ - char ____fmt[] = fmt; \ - bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \ - }) -#define GET_CONFIG() \ - struct gconfig_t g = {0}; \ - u32 configk = 0; \ - struct gconfig_t *configv = bpf_map_lookup_elem(&config_map, &configk); \ - if (configv) { \ - g = *configv; \ - } -#else - -#define debug_log(fmt, ...) ({ bpf_printk(fmt, ##__VA_ARGS__); }) -#define GET_CONFIG() -#endif - static __always_inline int parse_skb_l2(struct __sk_buff *skb, struct l2_t *l2, u32 *offset) { if (bpf_skb_load_bytes(skb, *offset + offsetof(struct ethhdr, h_proto), &l2->h_protocol, sizeof(l2->h_protocol)) < 0) { diff --git a/cmd/capture.go b/cmd/capture.go index 7e20b293..137e2d88 100644 --- a/cmd/capture.go +++ b/cmd/capture.go @@ -3,32 +3,24 @@ package cmd import ( "context" "fmt" - "os" - "os/signal" - "strings" - "syscall" - "time" - "github.com/mozillazg/ptcpdump/bpf" - "github.com/mozillazg/ptcpdump/internal/btf" + "github.com/mozillazg/ptcpdump/internal/capturer" "github.com/mozillazg/ptcpdump/internal/consumer" "github.com/mozillazg/ptcpdump/internal/log" "github.com/mozillazg/ptcpdump/internal/metadata" "github.com/mozillazg/ptcpdump/internal/utils" + "os" + "os/signal" + "strings" + "syscall" ) func capture(ctx context.Context, stop context.CancelFunc, opts *Options) error { + log.Info("start get all devices") devices, err := opts.GetDevices() if err != nil { return err } - btfSpec, btfPath, err := btf.LoadBTFSpec(opts.btfPath) - if err != nil { - return err - } - if btfPath != btf.DefaultPath { - log.Warnf("use BTF specs from %s", btfPath) - } log.Info("start process and container cache") pcache := metadata.NewProcessCache() @@ -36,116 +28,93 @@ func capture(ctx context.Context, stop context.CancelFunc, opts *Options) error if cc != nil { pcache.WithContainerCache(cc) } + pcache.Start(ctx) + log.Info("start init writers") writers, fcloser, err := getWriters(opts, pcache) if err != nil { return err } defer func() { - for _, w := range writers { - w.Flush() - } if fcloser != nil { fcloser() } }() + + log.Info("start init gotls event consumer") gcr, err := getGoKeyLogEventConsumer(opts, writers) if err != nil { return err } - - var subProcessFinished <-chan struct{} - var subProcessLoaderPid int - if len(opts.subProgArgs) > 0 { - log.Info("start sub process loader") - subProcessLoaderPid, subProcessFinished, err = utils.StartSubProcessLoader(ctx, os.Args[0], opts.subProgArgs) - if err != nil { - return err - } - opts.pids = []uint{uint(subProcessLoaderPid)} - opts.followForks = true - } - - pcache.Start(ctx) + execConsumer := consumer.NewExecEventConsumer(pcache, int(opts.execEventsWorkerNumber)) + exitConsumer := consumer.NewExitEventConsumer(pcache, 10) + packetConsumer := consumer.NewPacketEventConsumer(writers, opts.deviceCache). + WithDelay(opts.delayBeforeHandlePacketEvents) log.Info("start get current connections") conns := getCurrentConnects(ctx, pcache, opts) - log.Info("start attach hooks") - bf, closers, err := attachHooks(btfSpec, conns, opts) - if err != nil { - runClosers(closers) + copts := opts.ToCapturerOptions() + copts.Connections = conns + copts.ProcessCache = pcache + copts.DeviceCache = opts.deviceCache + copts.NetNSCache = opts.netNSCache + copts.ExecConsumer = execConsumer + copts.ExitConsumer = exitConsumer + copts.PacketConsumer = packetConsumer + copts.Gcr = gcr + copts.Writers = writers + caper := capturer.NewCapturer(copts) + defer caper.Stop() + + if err := caper.StartSubProcessLoader(ctx, os.Args[0], opts.subProgArgs); err != nil { return err } - defer runClosers(closers) - packetEvensCh, err := bf.PullPacketEvents(ctx, int(opts.eventChanSize), int(opts.snapshotLength)) - if err != nil { + log.Info("start prepare capturer") + if err := caper.Prepare(); err != nil { return err } - execEvensCh, err := bf.PullExecEvents(ctx, int(opts.eventChanSize)) - if err != nil { + + log.Info("start attach hooks") + if err := caper.AttachTracingHooks(); err != nil { return err } - exitEvensCh, err := bf.PullExitEvents(ctx, int(opts.eventChanSize)) - if err != nil { + if err := attachGoTLSHooks(opts, caper.BPF()); err != nil { return err } - goTlsKeyLogEventsCh, err := bf.PullGoKeyLogEvents(ctx, int(opts.eventChanSize)) - if err != nil { + if err := caper.AttachTcHooksToDevs(devices.Devs()); err != nil { + return err + } + if err := caper.Start(ctx, stop); err != nil { return err } headerTips(opts) log.Info("capturing...") - execConsumer := consumer.NewExecEventConsumer(pcache, int(opts.execEventsWorkerNumber)) - go execConsumer.Start(ctx, execEvensCh) - exitConsumer := consumer.NewExitEventConsumer(pcache, 10) - go exitConsumer.Start(ctx, exitEvensCh) - go gcr.Start(ctx, goTlsKeyLogEventsCh) + go printCaptureCountBySignal(ctx, caper.BPF(), packetConsumer) - var stopByInternal bool - packetConsumer := consumer.NewPacketEventConsumer(writers, devices). - WithDelay(opts.delayBeforeHandlePacketEvents) - if subProcessLoaderPid > 0 { - go func() { - log.Infof("notify loader %d to start sub process", subProcessLoaderPid) - syscall.Kill(subProcessLoaderPid, syscall.SIGHUP) - <-subProcessFinished - log.Info("sub process exited") - time.Sleep(time.Second * 3) - stopByInternal = true - time.Sleep(opts.delayBeforeHandlePacketEvents) - stop() - }() - } - - go printCaptureCountBySignal(ctx, bf, packetConsumer) - packetConsumer.Start(ctx, packetEvensCh, opts.maxPacketCount) - defer func() { - packetConsumer.Stop() - execConsumer.Stop() - exitConsumer.Stop() - gcr.Stop() - }() + <-ctx.Done() - if !stopByInternal && ctx.Err() != nil { - fmt.Fprint(os.Stderr, "\n") + if !caper.StopByInternal() && ctx.Err() != nil { + utils.OutStderr("%s", "\n") } - counts := getCaptureCounts(bf, packetConsumer) - fmt.Fprintf(os.Stderr, "%s\n", strings.Join(counts, "\n")) + counts := getCaptureCounts(caper.BPF(), packetConsumer) + utils.OutStderr("%s\n", strings.Join(counts, "\n")) return nil } func headerTips(opts *Options) { - interfaces := opts.ifaces[0] - if len(opts.ifaces) > 1 { + interfaces := "any" + if len(opts.ifaces) > 0 { interfaces = fmt.Sprintf("[%s]", strings.Join(opts.ifaces, ", ")) } + msg := fmt.Sprintf("capturing on %s, link-type EN10MB (Ethernet), snapshot length %d bytes", interfaces, opts.snapshotLength) + if opts.verbose < 1 { log.Warn("ptcpdump: verbose output suppressed, use -v[v]... for verbose output") log.Warn(msg) @@ -163,7 +132,7 @@ func printCaptureCountBySignal(ctx context.Context, bf *bpf.BPF, c *consumer.Pac return case <-ch: counts := getCaptureCounts(bf, c) - fmt.Fprintf(os.Stderr, fmt.Sprintf("ptcpdump: %s\n", strings.Join(counts, ", "))) + utils.OutStderr("ptcpdump: %s\n", strings.Join(counts, ", ")) } } } diff --git a/cmd/ebpf.go b/cmd/ebpf.go index 08698a97..1d619dd0 100644 --- a/cmd/ebpf.go +++ b/cmd/ebpf.go @@ -1,156 +1 @@ package cmd - -import ( - "encoding/binary" - "fmt" - "net/netip" - "strings" - - btftype "github.com/cilium/ebpf/btf" - "github.com/cilium/ebpf/rlimit" - "github.com/mozillazg/ptcpdump/bpf" - "github.com/mozillazg/ptcpdump/internal/log" - "github.com/mozillazg/ptcpdump/internal/metadata" - "github.com/mozillazg/ptcpdump/internal/utils" -) - -func attachHooks(btfSpec *btftype.Spec, currentConns []metadata.Connection, opts *Options) (*bpf.BPF, []func(), error) { - devices, err := opts.GetDevices() - if err != nil { - return nil, nil, err - } - if err := rlimit.RemoveMemlock(); err != nil { - return nil, nil, err - } - bf, err := bpf.NewBPF() - if err != nil { - return nil, nil, err - } - bpfopts := &bpf.Options{} - bpfopts = bpfopts.WithPids(opts.pids). - WithComm(opts.comm). - WithFollowFork(opts.followForks). - WithPidNsIds(opts.pidnsIds). - WithMntNsIds(opts.mntnsIds). - WithNetNsIds(opts.netnsIds). - WithMaxPayloadSize(opts.snapshotLength). - WithPcapFilter(opts.pcapFilter). - WithKernelTypes(btfSpec) - - if err := bf.Load(*bpfopts); err != nil { - return nil, nil, err - } - var finalCloseFuncs []func() - finalCloseFuncs = append(finalCloseFuncs, bf.Close) - - if len(currentConns) > 0 { - if err := updateFlowPidMapValues(bf, currentConns); err != nil { - return bf, finalCloseFuncs, err - } - } - - cgroupPath, err := utils.GetCgroupV2RootDir() - if err != nil { - log.Warnf("skip attach cgroup due to get cgroup v2 root dir failed: %s", err) - } - if cgroupPath != "" { - if err := bf.AttachCgroups(cgroupPath); err != nil { - return bf, finalCloseFuncs, err - } - } - - if err := attachGoTLSHooks(opts, bf); err != nil { - return bf, finalCloseFuncs, err - } - if err := bf.AttachKprobes(); err != nil { - return bf, finalCloseFuncs, err - } - if err := bf.AttachTracepoints(); err != nil { - return bf, finalCloseFuncs, err - } - - for _, iface := range devices.Devs() { - var finalErr error - log.Infof("start to attach tc hook to %s in netns %s", iface.Name, iface.NetNs) - err := iface.NetNs.Do(func() { - closeFuncs, err := bf.AttachTcHooks(iface.Ifindex, opts.DirectionOut(), opts.DirectionIn()) - if err != nil { - runClosers(closeFuncs) - // TODO: use errors.Is(xxx) or == - if strings.Contains(err.Error(), "netlink receive: no such file or directory") || - strings.Contains(err.Error(), "netlink receive: no such device") { - log.Warnf("skip interface %s due to %s", iface.Name, err) - return - } - finalErr = err - } else { - finalCloseFuncs = append(finalCloseFuncs, func() { - iface.NetNs.Do(func() { - runClosers(closeFuncs) - }) - }) - } - }) - if finalErr == nil { - finalErr = err - } - if finalErr != nil { - return bf, finalCloseFuncs, fmt.Errorf("attach tc hooks for interface %d.%s: %w", - iface.Ifindex, iface.Name, finalErr) - } - - } - - return bf, finalCloseFuncs, nil -} - -func runClosers(funcs []func()) { - for i := len(funcs) - 1; i >= 0; i-- { - f := funcs[i] - if f != nil { - f() - } - } -} - -func updateFlowPidMapValues(bf *bpf.BPF, conns []metadata.Connection) error { - data := map[*bpf.BpfFlowPidKeyT]bpf.BpfProcessMetaT{} - for _, conn := range conns { - if conn.Pid == 0 { - continue - } - k := bpf.BpfFlowPidKeyT{ - Saddr: addrTo128(conn.LocalIP), - Sport: uint16(conn.LocalPort), - } - v := bpf.BpfProcessMetaT{ - Pid: uint32(conn.Pid), - MntnsId: uint32(conn.MntNs), - NetnsId: uint32(conn.NetNs), - } - data[&k] = v - } - if err := bf.UpdateFlowPidMapValues(data); err != nil { - return fmt.Errorf(": %w", err) - } - return nil -} - -func addrTo128(addr netip.Addr) [2]uint64 { - ret := [2]uint64{} - addr = addr.Unmap() - switch { - case addr.Is4(): - a4 := addr.As4() - tmp := [4]byte{} - ip := append([]byte{}, a4[:]...) - ip = append(ip, tmp[:]...) - ret[0] = binary.LittleEndian.Uint64(ip[:]) - break - default: - ip := addr.As16() - ret[0] = binary.LittleEndian.Uint64(ip[:8]) - ret[1] = binary.LittleEndian.Uint64(ip[8:16]) - } - return ret -} diff --git a/cmd/list.go b/cmd/list.go index 5a4d484f..c32c9904 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -15,7 +15,7 @@ func listInterfaces(opts Options) error { outputs := []string{} for _, d := range interfaces { - outputs = append(outputs, fmt.Sprintf("%d.%s", d.Ifindex, d.Name)) + outputs = append(outputs, fmt.Sprintf("%d.%s, netns %d", d.Ifindex, d.Name, d.NetNs.Inode())) } fmt.Printf("%s\n", strings.Join(outputs, "\n")) diff --git a/cmd/options.go b/cmd/options.go index 78ff8c2a..c84fbe0b 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -1,13 +1,16 @@ package cmd import ( + "context" "fmt" - "github.com/mozillazg/ptcpdump/internal/dev" + "github.com/mozillazg/ptcpdump/internal/capturer" "github.com/mozillazg/ptcpdump/internal/log" - "github.com/mozillazg/ptcpdump/internal/utils" + "github.com/mozillazg/ptcpdump/internal/metadata" + "github.com/mozillazg/ptcpdump/internal/types" "github.com/mozillazg/ptcpdump/internal/writer" "github.com/x-way/pktdump" "os" + "path" "strings" "time" @@ -73,8 +76,13 @@ type Options struct { netnsIds []uint32 pidnsIds []uint32 - netNsPaths []string - devices *dev.Interfaces + netNsPaths []string + devices *types.Interfaces + netNSCache *metadata.NetNsCache + deviceCache *metadata.DeviceCache + allDev bool + allNetNs bool + allNewlyNetNs bool } func (o Options) filterByContainer() bool { @@ -201,38 +209,95 @@ func (o Options) getWriteTLSKeyLogPath() string { return os.Getenv("SSLKEYLOGFILE") } -func (o *Options) GetDevices() (*dev.Interfaces, error) { +func (o *Options) GetDevices() (*types.Interfaces, error) { if o.devices != nil { return o.devices, nil } - if len(o.netNsPaths) == 0 { - o.netNsPaths = append(o.netNsPaths, "") + if len(o.netNsPaths) > 0 { + if o.netNsPaths[0] == "any" { + o.allNetNs = true + } else if o.netNsPaths[0] == "newly" { + o.allNewlyNetNs = true + } + o.netNsPaths = []string{} } - if o.netNsPaths[0] == "any" { - o.netNsPaths = []string{""} - ps, err := utils.GetAllNamedNetNsName() - if err != nil { - return nil, err + for i, p := range o.netNsPaths { + if !strings.Contains(p, "/") { + o.netNsPaths[i] = path.Join("/run/netns", p) } - o.netNsPaths = append(o.netNsPaths, ps...) } - log.Infof("o.netNsPaths=%v", o.netNsPaths) + if len(o.ifaces) > 0 && o.ifaces[0] == "any" { + o.allDev = true + o.ifaces = []string{} + } + + log.Infof("o.netNsPaths=%v, o.ifaces=%v", o.netNsPaths, o.ifaces) - devices := dev.NewInterfaces() - ifaces := o.ifaces - if len(ifaces) > 0 && ifaces[0] == "any" { - ifaces = nil + o.netNSCache = metadata.NewNetNsCache() + o.deviceCache = metadata.NewDeviceCache(o.netNSCache) + if err := o.netNSCache.Start(context.TODO()); err != nil { + return nil, err + } + if err := o.deviceCache.Start(context.TODO()); err != nil { + return nil, err } - for _, p := range o.netNsPaths { - devs, err := dev.GetDevices(ifaces, p) - if err != nil { - return nil, err - } - devices.Merge(devs) + if o.allNewlyNetNs { + o.devices = types.NewInterfaces() + return o.devices, nil + } + + var err error + o.devices, err = o.deviceCache.GetDevices(o.ifaces, o.netNsPaths) + if err != nil { + return nil, err } - o.devices = devices return o.devices, nil } + +func (o *Options) ToCapturerOptions() *capturer.Options { + copts := &capturer.Options{ + Pids: o.pids, + Comm: o.comm, + FollowForks: o.followForks, + PcapFilter: o.pcapFilter, + MaxPacketCount: o.maxPacketCount, + DirectionIn: o.DirectionIn(), + DirectionOut: o.DirectionOut(), + OneLine: o.oneLine, + PrintPacketNumber: o.printPacketNumber, + DontPrintTimestamp: o.dontPrintTimestamp, + OnlyPrintCount: o.onlyPrintCount, + PrintDataAsHex: o.printDataAsHex, + PrintDataAsHexASCII: o.printDataAsHexASCII, + PrintDataAsASCII: o.printDataAsASCII, + TimeStampPrecision: o.timeStampPrecision, + TimeStampMicro: o.timeStampMicro, + TimeStampNano: o.timeStampNano, + DontConvertAddr: o.dontConvertAddr, + ContainerId: o.containerId, + ContainerName: o.containerName, + PodName: o.podName, + PodNamespace: o.podNamespace, + EventChanSize: o.eventChanSize, + DelayBeforeHandlePacketEvents: o.delayBeforeHandlePacketEvents, + ExecEventsWorkerNumber: o.execEventsWorkerNumber, + SnapshotLength: o.snapshotLength, + DockerEndpoint: o.dockerEndpoint, + ContainerdEndpoint: o.containerdEndpoint, + CriRuntimeEndpoint: o.criRuntimeEndpoint, + WriteTLSKeyLogPath: o.writeTLSKeyLogPath, + EmbedTLSKeyLogToPcapng: o.embedTLSKeyLogToPcapng, + SubProgArgs: o.subProgArgs, + MntnsIds: o.mntnsIds, + NetnsIds: o.netnsIds, + PidnsIds: o.pidnsIds, + BTFPath: o.btfPath, + AllDev: o.allDev, + AllNetNs: o.allNetNs, + AllNewlyNetNs: o.allNewlyNetNs, + } + return copts +} diff --git a/cmd/root.go b/cmd/root.go index e7b6f09f..0098e83c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -123,7 +123,7 @@ func init() { rootCmd.Flags().BoolVar(&opts.embedTLSKeyLogToPcapng, "embed-keylog-to-pcapng", false, "Write TLS Key Log file to this path (experimental: only support unstripped Go binary and must combined with `-- CMD [ARGS]`)") - rootCmd.Flags().StringSliceVar(&opts.netNsPaths, "netns", []string{}, + rootCmd.Flags().StringSliceVar(&opts.netNsPaths, "netns", []string{"/proc/self/ns/net"}, "Path to an network namespace file or name") silenceKlog() diff --git a/internal/capturer/capturer.go b/internal/capturer/capturer.go new file mode 100644 index 00000000..d15cb936 --- /dev/null +++ b/internal/capturer/capturer.go @@ -0,0 +1,414 @@ +package capturer + +import ( + "context" + "fmt" + ebpfbtf "github.com/cilium/ebpf/btf" + "github.com/cilium/ebpf/rlimit" + "github.com/mozillazg/ptcpdump/bpf" + "github.com/mozillazg/ptcpdump/internal/btf" + "github.com/mozillazg/ptcpdump/internal/consumer" + "github.com/mozillazg/ptcpdump/internal/log" + "github.com/mozillazg/ptcpdump/internal/metadata" + "github.com/mozillazg/ptcpdump/internal/types" + "github.com/mozillazg/ptcpdump/internal/utils" + "github.com/mozillazg/ptcpdump/internal/writer" + "strings" + "syscall" + "time" +) + +type Capturer struct { + opts *Options + + btfSpec *ebpfbtf.Spec + bpf *bpf.BPF + + subProcessFinished <-chan struct{} + subProcessLoaderPid int + + packetEvensCh <-chan bpf.BpfPacketEventWithPayloadT + execEvensCh <-chan bpf.BpfExecEventT + exitEvensCh <-chan bpf.BpfExitEventT + goTlsKeyLogEventsCh <-chan bpf.BpfGoKeylogEventT + newDevCh <-chan bpf.BpfNewNetdeviceEventT + devChangeCh <-chan bpf.BpfNetdeviceChangeEventT + mountCh <-chan bpf.BpfMountEventT + + stopByInternal bool + closeFuncs []func() +} + +type Options struct { + Pids []uint + Comm string + FollowForks bool + WriteFilePath string + ReadFilePath string + PcapFilter string + MaxPacketCount uint + DirectionOut bool + DirectionIn bool + OneLine bool + + PrintPacketNumber bool + DontPrintTimestamp bool + OnlyPrintCount bool + PrintDataAsHex int + PrintDataAsHexASCII int + PrintDataAsASCII bool + + TimeStampPrecision string + TimeStampMicro bool + TimeStampNano bool + + DontConvertAddr int + + ContainerId string + ContainerName string + PodName string + PodNamespace string + + EventChanSize uint + DelayBeforeHandlePacketEvents time.Duration + ExecEventsWorkerNumber uint + SnapshotLength uint32 + + DockerEndpoint string + ContainerdEndpoint string + CriRuntimeEndpoint string + + WriteTLSKeyLogPath string + EmbedTLSKeyLogToPcapng bool + + SubProgArgs []string + + MntnsIds []uint32 + NetnsIds []uint32 + PidnsIds []uint32 + + BTFPath string + + Connections []metadata.Connection + ProcessCache *metadata.ProcessCache + NetNSCache *metadata.NetNsCache + DeviceCache *metadata.DeviceCache + + Writers []writer.PacketWriter + + Gcr *consumer.GoKeyLogEventConsumer + ExecConsumer *consumer.ExecEventConsumer + ExitConsumer *consumer.ExitEventConsumer + PacketConsumer *consumer.PacketEventConsumer + + AllDev bool + AllNetNs bool + AllNewlyNetNs bool +} + +func NewCapturer(opts *Options) *Capturer { + return &Capturer{opts: opts} +} + +func (c *Capturer) StartSubProcessLoader(ctx context.Context, program string, subProgramArgs []string) error { + if len(subProgramArgs) == 0 { + return nil + } + + log.Info("start sub process loader") + var err error + c.subProcessLoaderPid, c.subProcessFinished, err = utils.StartSubProcessLoader(ctx, program, subProgramArgs) + if err != nil { + return err + } + c.opts.Pids = []uint{uint(c.subProcessLoaderPid)} + c.opts.FollowForks = true + + return nil +} + +func (c *Capturer) Prepare() error { + if err := rlimit.RemoveMemlock(); err != nil { + return err + } + + btfspec, err := c.loadBTF() + if err != nil { + return err + } + c.btfSpec = btfspec + + return nil +} + +func (c *Capturer) AttachTracingHooks() error { + bf, err := bpf.NewBPF() + if err != nil { + return err + } + + bpfopts := &bpf.Options{} + bpfopts = bpfopts.WithPids(c.opts.Pids). + WithComm(c.opts.Comm). + WithFollowFork(c.opts.FollowForks). + WithPidNsIds(c.opts.PidnsIds). + WithMntNsIds(c.opts.MntnsIds). + WithNetNsIds(c.opts.NetnsIds). + WithMaxPayloadSize(c.opts.SnapshotLength). + WithHookMount(c.opts.AllNetNs || c.opts.AllNewlyNetNs). + WithHookNetDev(c.opts.AllNetNs || c.opts.AllNewlyNetNs || c.opts.AllDev). + WithPcapFilter(c.opts.PcapFilter). + WithKernelTypes(c.btfSpec) + + if err := bf.Load(*bpfopts); err != nil { + return err + } + c.bpf = bf + c.closeFuncs = append(c.closeFuncs, bf.Close) + + if len(c.opts.Connections) > 0 { + if err := updateFlowPidMapValues(bf, c.opts.Connections); err != nil { + return err + } + } + + cgroupPath, err := utils.GetCgroupV2RootDir() + if err != nil { + log.Warnf("skip attach cgroup due to get cgroup v2 root dir failed: %s", err) + } + if cgroupPath != "" { + if err := bf.AttachCgroups(cgroupPath); err != nil { + return err + } + } + + if err := bf.AttachKprobes(); err != nil { + return err + } + if err := bf.AttachTracepoints(); err != nil { + return err + } + + return nil +} + +func (c *Capturer) AttachTcHooksToDevs(devs []types.Device) error { + for _, iface := range devs { + err := c.attachTcHooks(iface) + if err != nil { + return fmt.Errorf("attach tc hooks for interface %d.%s: %w", + iface.Ifindex, iface.Name, err) + } + } + + return nil +} + +func (c *Capturer) Start(ctx context.Context, stop context.CancelFunc) error { + if err := c.pullEvents(ctx); err != nil { + return err + } + + go c.opts.ExecConsumer.Start(ctx, c.execEvensCh) + go c.opts.ExitConsumer.Start(ctx, c.exitEvensCh) + go c.opts.Gcr.Start(ctx, c.goTlsKeyLogEventsCh) + go c.opts.PacketConsumer.Start(ctx, c.packetEvensCh, c.opts.MaxPacketCount) + + go c.handleMountEvents() + go c.handleNewDevEvents() + go c.handleDevChangeEvents() + + go c.startSubProcess(stop) + + return nil +} + +func (c *Capturer) Stop() { + for _, w := range c.opts.Writers { + w.Flush() + } + + c.opts.PacketConsumer.Stop() + c.opts.ExecConsumer.Stop() + c.opts.ExitConsumer.Stop() + c.opts.Gcr.Stop() + + runClosers(c.closeFuncs) +} + +func (c *Capturer) pullEvents(ctx context.Context) error { + var err error + + c.packetEvensCh, err = c.bpf.PullPacketEvents(ctx, int(c.opts.EventChanSize), int(c.opts.SnapshotLength)) + if err != nil { + return err + } + c.execEvensCh, err = c.bpf.PullExecEvents(ctx, int(c.opts.EventChanSize)) + if err != nil { + return err + } + c.exitEvensCh, err = c.bpf.PullExitEvents(ctx, int(c.opts.EventChanSize)) + if err != nil { + return err + } + c.goTlsKeyLogEventsCh, err = c.bpf.PullGoKeyLogEvents(ctx, int(c.opts.EventChanSize)) + if err != nil { + return err + } + + if c.opts.AllNetNs || c.opts.AllNewlyNetNs { + c.newDevCh, err = c.bpf.PullNewNetDeviceEvents(ctx, int(c.opts.EventChanSize)) + if err != nil { + return err + } + c.devChangeCh, err = c.bpf.PullNetDeviceChangeEvents(ctx, int(c.opts.EventChanSize)) + if err != nil { + return err + } + c.mountCh, err = c.bpf.PullMountEventEvents(ctx, int(c.opts.EventChanSize)) + if err != nil { + return err + } + } + + return nil +} + +func (c *Capturer) BPF() *bpf.BPF { + return c.bpf +} + +func (c *Capturer) StopByInternal() bool { + return c.stopByInternal +} + +func (c *Capturer) startSubProcess(stop context.CancelFunc) { + if c.subProcessLoaderPid == 0 { + return + } + + log.Infof("notify loader %d to start sub process", c.subProcessLoaderPid) + syscall.Kill(c.subProcessLoaderPid, syscall.SIGHUP) + + <-c.subProcessFinished + + log.Info("sub process exited") + time.Sleep(time.Second * 3) + + c.stopByInternal = true + time.Sleep(c.opts.DelayBeforeHandlePacketEvents) + stop() +} + +func (c *Capturer) attachTcHooks(iface types.Device) error { + var finalErr error + log.Infof("start to attach tc hook to %s in netns %s", iface.Name, iface.NetNs) + + err := iface.NetNs.Do(func() { + closeFuncs, err := c.bpf.AttachTcHooks(iface.Ifindex, c.opts.DirectionOut, c.opts.DirectionIn) + if err != nil { + runClosers(closeFuncs) + // TODO: use errors.Is(xxx) or == + if strings.Contains(err.Error(), "netlink receive: no such file or directory") || + strings.Contains(err.Error(), "netlink receive: no such device") { + log.Warnf("skip interface %s due to %s", iface.Name, err) + return + } + finalErr = err + } else { + c.closeFuncs = append(c.closeFuncs, func() { + iface.NetNs.Do(func() { + runClosers(closeFuncs) + }) + }) + } + }) + + if finalErr == nil { + finalErr = err + } + + return finalErr +} + +func (c *Capturer) loadBTF() (*ebpfbtf.Spec, error) { + log.Info("start load BTF spec") + btfSpec, btfPath, err := btf.LoadBTFSpec(c.opts.BTFPath) + if err != nil { + return btfSpec, err + } + if btfPath != btf.DefaultPath { + log.Warnf("use BTF specs from %s", btfPath) + } + + return btfSpec, nil +} + +func (c *Capturer) handleMountEvents() { + if c.mountCh == nil { + return + } + for event := range c.mountCh { + dest := utils.GoString(event.Dest[:]) + if !strings.HasPrefix(dest, "/run/netns/") { + continue + } + _, err := c.opts.NetNSCache.GetOrFetchByPath(dest) + if err != nil { + log.Errorf("error: %+v", err) + } + } +} + +func (c *Capturer) handleNewDevEvents() { + if c.newDevCh == nil { + return + } + for event := range c.newDevCh { + dev := event.Dev + c.opts.DeviceCache.Add(dev.NetnsId, dev.Ifindex, utils.GoString(dev.Name[:])) + + device, _ := c.opts.DeviceCache.GetByIfindex(int(dev.Ifindex), dev.NetnsId) + if err := c.attachTcHooks(device); err != nil { + log.Errorf("attach tc hooks failed: %s", err) + } + } +} + +func (c *Capturer) handleDevChangeEvents() { + if c.devChangeCh == nil { + return + } + for event := range c.devChangeCh { + dev := event.NewDevice + c.opts.DeviceCache.Add(dev.NetnsId, dev.Ifindex, utils.GoString(dev.Name[:])) + + device, _ := c.opts.DeviceCache.GetByIfindex(int(dev.Ifindex), dev.NetnsId) + if err := c.attachTcHooks(device); err != nil { + log.Errorf("attach tc hooks failed: %s", err) + } + } +} + +func updateFlowPidMapValues(bf *bpf.BPF, conns []metadata.Connection) error { + data := map[*bpf.BpfFlowPidKeyT]bpf.BpfProcessMetaT{} + for _, conn := range conns { + if conn.Pid == 0 { + continue + } + k := bpf.BpfFlowPidKeyT{ + Saddr: addrTo128(conn.LocalIP), + Sport: uint16(conn.LocalPort), + } + v := bpf.BpfProcessMetaT{ + Pid: uint32(conn.Pid), + MntnsId: uint32(conn.MntNs), + NetnsId: uint32(conn.NetNs), + } + data[&k] = v + } + if err := bf.UpdateFlowPidMapValues(data); err != nil { + return fmt.Errorf(": %w", err) + } + return nil +} diff --git a/internal/capturer/utils.go b/internal/capturer/utils.go new file mode 100644 index 00000000..97d72594 --- /dev/null +++ b/internal/capturer/utils.go @@ -0,0 +1,34 @@ +package capturer + +import ( + "encoding/binary" + "net/netip" +) + +func addrTo128(addr netip.Addr) [2]uint64 { + ret := [2]uint64{} + addr = addr.Unmap() + switch { + case addr.Is4(): + a4 := addr.As4() + tmp := [4]byte{} + ip := append([]byte{}, a4[:]...) + ip = append(ip, tmp[:]...) + ret[0] = binary.LittleEndian.Uint64(ip[:]) + break + default: + ip := addr.As16() + ret[0] = binary.LittleEndian.Uint64(ip[:8]) + ret[1] = binary.LittleEndian.Uint64(ip[8:16]) + } + return ret +} + +func runClosers(funcs []func()) { + for i := len(funcs) - 1; i >= 0; i-- { + f := funcs[i] + if f != nil { + f() + } + } +} diff --git a/internal/consumer/net.go b/internal/consumer/net.go index eaa845c5..d5c81df0 100644 --- a/internal/consumer/net.go +++ b/internal/consumer/net.go @@ -3,25 +3,25 @@ package consumer import ( "context" "github.com/mozillazg/ptcpdump/bpf" - "github.com/mozillazg/ptcpdump/internal/dev" "github.com/mozillazg/ptcpdump/internal/event" "github.com/mozillazg/ptcpdump/internal/log" + "github.com/mozillazg/ptcpdump/internal/metadata" "github.com/mozillazg/ptcpdump/internal/writer" "time" ) type PacketEventConsumer struct { writers []writer.PacketWriter - devices *dev.Interfaces + deviceCache *metadata.DeviceCache processedCount int delay time.Duration } -func NewPacketEventConsumer(writers []writer.PacketWriter, devices *dev.Interfaces) *PacketEventConsumer { +func NewPacketEventConsumer(writers []writer.PacketWriter, deviceCache *metadata.DeviceCache) *PacketEventConsumer { return &PacketEventConsumer{ - writers: writers, - devices: devices, + writers: writers, + deviceCache: deviceCache, } } @@ -49,7 +49,7 @@ func (c *PacketEventConsumer) Start(ctx context.Context, ch <-chan bpf.BpfPacket } func (c *PacketEventConsumer) handlePacketEvent(pt bpf.BpfPacketEventWithPayloadT) { - pevent, err := event.ParsePacketEvent(c.devices, pt) + pevent, err := event.ParsePacketEvent(c.deviceCache, pt) if err != nil { log.Errorf("[PacketEventConsumer] parse event failed: %s", err) return diff --git a/internal/dev/dev.go b/internal/dev/dev.go index 4a273d29..89455dfe 100644 --- a/internal/dev/dev.go +++ b/internal/dev/dev.go @@ -1,140 +1 @@ package dev - -import ( - "fmt" - "github.com/mozillazg/ptcpdump/internal/log" - "github.com/mozillazg/ptcpdump/internal/utils" - "net" - "sort" - "strconv" -) - -var allLinks = map[uint32][]net.Interface{} - -type Device struct { - Name string - Ifindex int - NetNs *utils.NetNs -} - -type Interfaces struct { - devs map[string]Device -} - -func NewInterfaces() *Interfaces { - return &Interfaces{devs: make(map[string]Device)} -} - -func getAllLinks(inode uint32) ([]net.Interface, error) { - if allLinks[inode] == nil { - links, err := net.Interfaces() - if err != nil { - return nil, fmt.Errorf("error getting interfaces: %w", err) - } - allLinks[inode] = links - } - - return allLinks[inode], nil -} - -func getDevicesFromNetNs(netNsPath string) (devices map[int]Device, err error) { - ns, err := utils.NewNetNs(netNsPath) - if err != nil { - return nil, fmt.Errorf("error getting net ns: %w", err) - } - - var finalErr error - devices = map[int]Device{} - - err = ns.Do(func() { - interfaces, err := getAllLinks(ns.Inode()) - if err != nil { - finalErr = fmt.Errorf("error getting interfaces: %w", err) - return - } - for _, interf := range interfaces { - devices[interf.Index] = Device{ - Name: interf.Name, - Ifindex: interf.Index, - NetNs: ns, - } - } - return - }) - if finalErr != nil { - return nil, finalErr - } - - log.Infof("got devices from %s: %v", netNsPath, devices) - - return devices, err -} - -func GetDevices(names []string, netNsPatch string) (*Interfaces, error) { - ifindexMap := make(map[int]Device) - interfaces := NewInterfaces() - - devices, err := getDevicesFromNetNs(netNsPatch) - if err != nil { - return nil, fmt.Errorf(": %w", err) - } - if len(names) == 0 { - for _, v := range devices { - v := v - interfaces.Add(v) - } - return interfaces, nil - } - - for _, name := range names { - for _, lk := range devices { - if lk.Name == name || strconv.Itoa(lk.Ifindex) == name { - log.Infof("found interface %s", name) - ifindexMap[lk.Ifindex] = lk - continue - } - } - } - - for _, v := range ifindexMap { - v := v - interfaces.Add(v) - } - return interfaces, nil -} - -func (i *Interfaces) Add(dev Device) { - k := i.key(dev) - i.devs[k] = dev -} - -func (i *Interfaces) Merge(b *Interfaces) { - for _, v := range b.devs { - v := v - i.Add(v) - } -} - -func (i *Interfaces) Devs() []Device { - var devs []Device - for _, v := range i.devs { - devs = append(devs, v) - } - sort.Slice(devs, func(i, j int) bool { - return devs[i].Ifindex < devs[j].Ifindex - }) - return devs -} - -func (i *Interfaces) key(dev Device) string { - return fmt.Sprintf("%d.%d", dev.NetNs.Inode(), dev.Ifindex) -} - -func (i *Interfaces) GetByIfindex(index int) Device { - for _, v := range i.devs { - if v.Ifindex == index { - return v - } - } - return Device{} -} diff --git a/internal/event/net.go b/internal/event/net.go index 57e86276..8739a924 100644 --- a/internal/event/net.go +++ b/internal/event/net.go @@ -1,11 +1,12 @@ package event import ( + "github.com/mozillazg/ptcpdump/internal/metadata" + "github.com/mozillazg/ptcpdump/internal/types" "time" "github.com/gopacket/gopacket" "github.com/mozillazg/ptcpdump/bpf" - "github.com/mozillazg/ptcpdump/internal/dev" "github.com/mozillazg/ptcpdump/internal/host" "github.com/mozillazg/ptcpdump/internal/log" "github.com/mozillazg/ptcpdump/internal/utils" @@ -21,7 +22,7 @@ const ( type Packet struct { Time time.Time Type packetType - Device dev.Device + Device types.Device Pid int MntNs int NetNs int @@ -33,7 +34,7 @@ type Packet struct { CgroupName string } -func ParsePacketEvent(devices *dev.Interfaces, event bpf.BpfPacketEventWithPayloadT) (*Packet, error) { +func ParsePacketEvent(deviceCache *metadata.DeviceCache, event bpf.BpfPacketEventWithPayloadT) (*Packet, error) { var p Packet if t, err := convertBpfKTimeNs(event.Meta.Timestamp); err != nil { log.Errorf("convert bpf time failed: %s", err) @@ -45,7 +46,7 @@ func ParsePacketEvent(devices *dev.Interfaces, event bpf.BpfPacketEventWithPaylo p.MntNs = int(event.Meta.Process.MntnsId) p.NetNs = int(event.Meta.Process.NetnsId) p.CgroupName = utils.GoString(event.Meta.Process.CgroupName[:]) - p.Device = devices.GetByIfindex(int(event.Meta.Ifindex)) + p.Device, _ = deviceCache.GetByIfindex(int(event.Meta.Ifindex), event.Meta.Process.NetnsId) log.Infof("new packet event, pid: %d mntns: %d, netns: %d, cgroupName: %s", p.Pid, p.MntNs, p.NetNs, p.CgroupName) @@ -67,7 +68,7 @@ func FromPacket(ci gopacket.CaptureInfo, data []byte) (*Packet, error) { p := Packet{ Time: ci.Timestamp, Type: -1, - Device: dev.Device{}, + Device: types.Device{}, Pid: 0, Truncated: false, Len: ci.Length, diff --git a/internal/event/process.go b/internal/event/process.go index 8f951d4a..7135d2d1 100644 --- a/internal/event/process.go +++ b/internal/event/process.go @@ -1,7 +1,6 @@ package event import ( - "path/filepath" "strings" "github.com/gopacket/gopacket/pcapgo" @@ -11,24 +10,8 @@ import ( "github.com/mozillazg/ptcpdump/internal/utils" ) -type ProcessExec struct { - PPid int - Pid int - - Filename string - FilenameTruncated bool - - Args []string - ArgsTruncated bool - - PidNs int64 - MntNs int64 - Netns int64 - CgroupName string -} - -func ParseProcessExecEvent(event bpf.BpfExecEventT) (*ProcessExec, error) { - var p ProcessExec +func ParseProcessExecEvent(event bpf.BpfExecEventT) (*types.ProcessExec, error) { + var p types.ProcessExec if event.ArgsTruncated == 1 { p.ArgsTruncated = true } @@ -59,8 +42,8 @@ func ParseProcessExecEvent(event bpf.BpfExecEventT) (*ProcessExec, error) { return &p, nil } -func FromPacketOptions(opts pcapgo.NgPacketOptions) (ProcessExec, types.PacketContext) { - p := &ProcessExec{} +func FromPacketOptions(opts pcapgo.NgPacketOptions) (types.ProcessExec, types.PacketContext) { + p := &types.ProcessExec{} pctx := &types.PacketContext{} pctx.FromPacketComments(opts.Comments) @@ -75,27 +58,3 @@ func FromPacketOptions(opts pcapgo.NgPacketOptions) (ProcessExec, types.PacketCo return *p, *pctx } - -func (p ProcessExec) FilenameStr() string { - s := string(p.Filename) - if p.FilenameTruncated { - s += "..." - } - return s -} - -func (p ProcessExec) ArgsStr() string { - s := strings.Join(p.Args, " ") - if p.ArgsTruncated { - s += "..." - } - return s -} - -func (p ProcessExec) MatchComm(name string) bool { - filename := filepath.Base(p.Filename) - if len(filename) > 15 { - filename = filename[:15] - } - return name == filename -} diff --git a/internal/metadata/device.go b/internal/metadata/device.go new file mode 100644 index 00000000..76e08646 --- /dev/null +++ b/internal/metadata/device.go @@ -0,0 +1,190 @@ +package metadata + +import ( + "context" + "errors" + "fmt" + "github.com/mozillazg/ptcpdump/internal/log" + "github.com/mozillazg/ptcpdump/internal/types" + "net" + "sync" +) + +type DeviceCache struct { + nscache *NetNsCache + allLinks map[uint32][]net.Interface + lock sync.RWMutex +} + +func NewDeviceCache(nscache *NetNsCache) *DeviceCache { + return &DeviceCache{ + nscache: nscache, + allLinks: make(map[uint32][]net.Interface), + lock: sync.RWMutex{}, + } +} + +func (d *DeviceCache) Start(ctx context.Context) error { + if err := d.init(); err != nil { + return err + } + return nil +} + +func (d *DeviceCache) init() error { + for _, ns := range d.nscache.nsStore { + _, err := d.getDevicesFromNetNs(ns) + if err != nil { + return err + } + } + return nil +} + +func (d *DeviceCache) GetDevices(devNames []string, netNsPaths []string) (*types.Interfaces, error) { + var nsList []*types.NetNs + interfaces := types.NewInterfaces() + + if len(netNsPaths) > 0 { + for _, p := range netNsPaths { + ns, err := d.nscache.GetOrFetchByPath(p) + if err != nil { + return nil, err + } + nsList = append(nsList, ns) + } + } else { + for _, ns := range d.nscache.nsStore { + nsList = append(nsList, ns) + } + } + + if len(devNames) > 0 { + for _, ns := range nsList { + for _, name := range devNames { + dev, err := d.getDeviceFromNetNs(name, ns) + if err != nil && !errors.Is(err, types.ErrDeviceNotFound) { + return nil, err + } + interfaces.Add(*dev) + } + } + } else { + for _, ns := range nsList { + devices, err := d.getDevicesFromNetNs(ns) + if err != nil { + return nil, err + } + for _, dev := range devices { + interfaces.Add(dev) + } + } + } + + return interfaces, nil +} + +func (d *DeviceCache) Add(netNsInode uint32, ifindex uint32, name string) { + _, ok := d.GetByIfindex(int(ifindex), netNsInode) + if ok { + return + } + + d.lock.Lock() + defer d.lock.Unlock() + + d.allLinks[netNsInode] = append(d.allLinks[netNsInode], net.Interface{ + Index: int(ifindex), + Name: name, + }) +} + +func (d *DeviceCache) GetByIfindex(ifindex int, netNsInode uint32) (types.Device, bool) { + d.lock.RLock() + defer d.lock.RUnlock() + + ns, err := d.nscache.Get(netNsInode) + if err != nil { + ns = types.NewNetNsWithInode(netNsInode) + } + + devs, ok := d.allLinks[netNsInode] + if !ok { + for _, links := range d.allLinks { + for _, dev := range links { + devs = append(devs, dev) + } + } + } + for _, dev := range devs { + if dev.Index == ifindex { + return types.Device{ + Name: dev.Name, + Ifindex: ifindex, + NetNs: ns, + }, true + } + } + + return types.Device{ + Name: fmt.Sprintf("dummy-%d", ifindex), + Ifindex: ifindex, + NetNs: ns, + }, false +} + +func (d *DeviceCache) getDeviceFromNetNs(name string, ns *types.NetNs) (*types.Device, error) { + devices, err := d.getDevicesFromNetNs(ns) + if err != nil { + return nil, err + } + for _, device := range devices { + if device.Name == name { + return &device, nil + } + } + return nil, types.ErrDeviceNotFound +} + +func (d *DeviceCache) getDevicesFromNetNs(ns *types.NetNs) (devices map[int]types.Device, err error) { + var finalErr error + devices = map[int]types.Device{} + + err = ns.Do(func() { + interfaces, err := d.getAllLinks(ns.Inode()) + if err != nil { + finalErr = fmt.Errorf("error getting interfaces: %w", err) + return + } + for _, interf := range interfaces { + devices[interf.Index] = types.Device{ + Name: interf.Name, + Ifindex: interf.Index, + NetNs: ns, + } + } + return + }) + if finalErr != nil { + return nil, finalErr + } + + log.Infof("got devices from %s: %v", ns, devices) + + return devices, err +} + +func (d *DeviceCache) getAllLinks(inode uint32) ([]net.Interface, error) { + d.lock.Lock() + defer d.lock.Unlock() + + if d.allLinks[inode] == nil { + links, err := net.Interfaces() + if err != nil { + return nil, fmt.Errorf("error getting interfaces: %w", err) + } + d.allLinks[inode] = links + } + + return d.allLinks[inode], nil +} diff --git a/internal/metadata/netns.go b/internal/metadata/netns.go new file mode 100644 index 00000000..14fecc1d --- /dev/null +++ b/internal/metadata/netns.go @@ -0,0 +1,115 @@ +package metadata + +import ( + "context" + "errors" + "fmt" + "github.com/mozillazg/ptcpdump/internal/log" + "github.com/mozillazg/ptcpdump/internal/types" + "io/fs" + "os" + "path" + "sync" +) + +type NetNsCache struct { + nsStore map[uint32]*types.NetNs + lock sync.RWMutex +} + +const netnsMountPath = "/run/netns" + +func NewNetNsCache() *NetNsCache { + return &NetNsCache{ + nsStore: make(map[uint32]*types.NetNs), + lock: sync.RWMutex{}, + } +} + +func (n *NetNsCache) Start(ctx context.Context) error { + if err := n.init(); err != nil { + return err + } + return nil +} + +func (n *NetNsCache) init() error { + return n.refresh() +} + +func (n *NetNsCache) refresh() error { + pathList, err := getAllNamedNetNsPath() + if err != nil { + return err + } + for _, p := range pathList { + if _, err := n.GetOrFetchByPath(p); err != nil { + return err + } + } + return nil +} + +func (n *NetNsCache) Get(inodeId uint32) (*types.NetNs, error) { + n.lock.RLock() + defer n.lock.RUnlock() + + ns, ok := n.nsStore[inodeId] + if !ok { + return nil, fmt.Errorf("inode %d not found", inodeId) + } + return ns, nil +} + +func (n *NetNsCache) set(ns *types.NetNs) { + n.lock.Lock() + defer n.lock.Unlock() + + n.nsStore[ns.Inode()] = ns +} + +func (n *NetNsCache) getByPath(p string) (*types.NetNs, error) { + n.lock.RLock() + defer n.lock.RUnlock() + + for _, n := range n.nsStore { + if n.Path() == p { + return n, nil + } + } + return nil, fmt.Errorf("netns path %s not found", p) +} + +func (n *NetNsCache) GetOrFetchByPath(p string) (*types.NetNs, error) { + ns, err := n.getByPath(p) + if err == nil { + return ns, nil + } + + ns, err = types.NewNetNs(p) + if err != nil { + return nil, err + } + log.Infof("add new netns %d with path: %s", ns.Inode(), p) + n.set(ns) + + return ns, nil +} + +func getAllNamedNetNsPath() ([]string, error) { + ps := []string{"/proc/self/ns/net"} + dirEntry, err := os.ReadDir(netnsMountPath) + if err != nil { + if os.IsNotExist(err) || errors.Is(err, fs.ErrNotExist) { + return ps, nil + } + return nil, err + } + for _, fp := range dirEntry { + if fp.IsDir() { + continue + } + ps = append(ps, path.Join(netnsMountPath, fp.Name())) + } + return ps, nil +} diff --git a/internal/metadata/process.go b/internal/metadata/process.go index 2d129e9a..9cf6e2bd 100644 --- a/internal/metadata/process.go +++ b/internal/metadata/process.go @@ -8,7 +8,6 @@ import ( "sync" "time" - "github.com/mozillazg/ptcpdump/internal/event" "github.com/mozillazg/ptcpdump/internal/log" "github.com/mozillazg/ptcpdump/internal/types" "github.com/mozillazg/ptcpdump/internal/utils" @@ -92,7 +91,7 @@ func (c *ProcessCache) fillRunningProcesses(ctx context.Context) error { filename, _ = p.Name() } args, _ := p.CmdlineSlice() - e := event.ProcessExec{ + e := types.ProcessExec{ PPid: ppid, Pid: int(p.Pid), Filename: filename, @@ -111,7 +110,7 @@ func (c *ProcessCache) fillRunningProcesses(ctx context.Context) error { return nil } -func (c *ProcessCache) AddItem(exec event.ProcessExec) { +func (c *ProcessCache) AddItem(exec types.ProcessExec) { c.AddItemWithContext(exec, types.PacketContext{}) } @@ -159,7 +158,7 @@ func (c *ProcessCache) jobCleanDead() { log.Debugf("cleaned %d dead pids", len(pids)) } -func (c *ProcessCache) AddItemWithContext(exec event.ProcessExec, rawCtx types.PacketContext) { +func (c *ProcessCache) AddItemWithContext(exec types.ProcessExec, rawCtx types.PacketContext) { pid := exec.Pid if pid == 0 { return diff --git a/internal/types/device.go b/internal/types/device.go new file mode 100644 index 00000000..f968012d --- /dev/null +++ b/internal/types/device.go @@ -0,0 +1,59 @@ +package types + +import ( + "fmt" + "sort" +) + +type Device struct { + Name string + Ifindex int + NetNs *NetNs +} + +type Interfaces struct { + devs map[string]Device +} + +func NewInterfaces() *Interfaces { + return &Interfaces{devs: make(map[string]Device)} +} + +func (i *Interfaces) Add(dev Device) { + k := i.key(dev) + i.devs[k] = dev +} + +func (i *Interfaces) Merge(b *Interfaces) { + for _, v := range b.devs { + v := v + i.Add(v) + } +} + +func (i *Interfaces) Devs() []Device { + var devs []Device + for _, v := range i.devs { + devs = append(devs, v) + } + sort.Slice(devs, func(i, j int) bool { + if devs[i].NetNs.Inode() < devs[j].NetNs.Inode() { + return true + } + return devs[i].Ifindex < devs[j].Ifindex + }) + return devs +} + +func (i *Interfaces) key(dev Device) string { + return fmt.Sprintf("%d.%d", dev.NetNs.Inode(), dev.Ifindex) +} + +func (i *Interfaces) GetByIfindex(index int) Device { + for _, v := range i.devs { + if v.Ifindex == index { + return v + } + } + return Device{} +} diff --git a/internal/types/errors.go b/internal/types/errors.go new file mode 100644 index 00000000..3fe8bfb0 --- /dev/null +++ b/internal/types/errors.go @@ -0,0 +1,5 @@ +package types + +import "errors" + +var ErrDeviceNotFound = errors.New("device not found") diff --git a/internal/types/netns.go b/internal/types/netns.go new file mode 100644 index 00000000..ab42297b --- /dev/null +++ b/internal/types/netns.go @@ -0,0 +1,70 @@ +package types + +import ( + "fmt" + "github.com/vishvananda/netns" + "golang.org/x/sys/unix" + "runtime" +) + +type NetNs struct { + handle netns.NsHandle + path string + + inode uint32 +} + +func NewNetNs(netNsPath string) (*NetNs, error) { + handle, err := netns.GetFromPath(netNsPath) + if err != nil { + return nil, fmt.Errorf("error getting netns handle from path %s: %w", netNsPath, err) + } + + var stat unix.Stat_t + if err := unix.Fstat(int(handle), &stat); err != nil { + return nil, fmt.Errorf("error getting stats for netns %s: %w", netNsPath, err) + } + + return &NetNs{handle: handle, path: netNsPath, inode: uint32(stat.Ino)}, nil +} + +func NewNetNsWithInode(inode uint32) *NetNs { + return &NetNs{inode: inode} +} + +func (n *NetNs) Do(f func()) (err error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, getErr := netns.Get() + if getErr != nil { + return fmt.Errorf("error getting netns: %w", getErr) + } + defer func() { + err = netns.Set(origns) + if err == nil { + err = origns.Close() + } + }() + + err = netns.Set(n.handle) + if err != nil { + return fmt.Errorf("error setting netns handle: %w", err) + } + + f() + + return +} + +func (n *NetNs) String() string { + return fmt.Sprintf("{NetNs inode: %d, path: %s}", n.inode, n.path) +} + +func (n *NetNs) Path() string { + return n.path +} + +func (n *NetNs) Inode() uint32 { + return n.inode +} diff --git a/internal/types/process.go b/internal/types/process.go index 569e269f..9d7441ee 100644 --- a/internal/types/process.go +++ b/internal/types/process.go @@ -27,6 +27,22 @@ type Process struct { ProcessNamespace } +type ProcessExec struct { + PPid int + Pid int + + Filename string + FilenameTruncated bool + + Args []string + ArgsTruncated bool + + PidNs int64 + MntNs int64 + Netns int64 + CgroupName string +} + func (p ProcessBase) MatchComm(name string) bool { filename := p.Comm() if len(filename) > 15 { @@ -46,3 +62,27 @@ func (p ProcessBase) FormatArgs() string { func (p ProcessBase) Comm() string { return filepath.Base(p.Cmd) } + +func (p ProcessExec) FilenameStr() string { + s := string(p.Filename) + if p.FilenameTruncated { + s += "..." + } + return s +} + +func (p ProcessExec) ArgsStr() string { + s := strings.Join(p.Args, " ") + if p.ArgsTruncated { + s += "..." + } + return s +} + +func (p ProcessExec) MatchComm(name string) bool { + filename := filepath.Base(p.Filename) + if len(filename) > 15 { + filename = filename[:15] + } + return name == filename +} diff --git a/internal/utils/fmt.go b/internal/utils/fmt.go new file mode 100644 index 00000000..37921118 --- /dev/null +++ b/internal/utils/fmt.go @@ -0,0 +1,10 @@ +package utils + +import ( + "fmt" + "os" +) + +func OutStderr(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, format, a...) +} diff --git a/internal/utils/netns.go b/internal/utils/netns.go index 5f4de06c..12e717f7 100644 --- a/internal/utils/netns.go +++ b/internal/utils/netns.go @@ -19,7 +19,7 @@ type NetNs struct { inode uint32 } -const netnsMountPath = "/var/run/netns" +const netnsMountPath = "/run/netns" func GetAllNamedNetNsName() ([]string, error) { var ps []string