diff --git a/bpf/bpf.go b/bpf/bpf.go index f14a4f5f..be562c37 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/mdlayher/netlink" "strings" "unsafe" @@ -198,34 +199,6 @@ func (b *BPF) UpdateFlowPidMapValues(data map[*BpfFlowPidKeyT]BpfProcessMetaT) e return nil } -func (b *BPF) AttachCgroups(cgroupPath string) error { - if b.skipAttachCgroup { - return nil - } - - lk, err := link.AttachCgroup(link.CgroupOptions{ - Path: cgroupPath, - Attach: ebpf.AttachCGroupInetSockCreate, - Program: b.objs.CgroupSockCreate, - }) - if err != nil { - return fmt.Errorf("attach cgroup/sock_create: %w", err) - } - b.links = append(b.links, lk) - - lk, err = link.AttachCgroup(link.CgroupOptions{ - Path: cgroupPath, - Attach: ebpf.AttachCgroupInetSockRelease, - Program: b.objs.CgroupSockRelease, - }) - if err != nil { - return fmt.Errorf("attach cgroup/sock_release: %w", err) - } - b.links = append(b.links, lk) - - return nil -} - func (b *BPF) AttachKprobes() error { err := b.attachFentryOrKprobe("security_sk_classify_flow", b.objs.FentrySecuritySkClassifyFlow, b.objs.KprobeSecuritySkClassifyFlow) @@ -233,108 +206,32 @@ func (b *BPF) AttachKprobes() error { return fmt.Errorf(": %w", err) } - err = b.attachFentryOrKprobe("tcp_sendmsg", - b.objs.FentryTcpSendmsg, b.objs.KprobeTcpSendmsg) - if err != nil { - return fmt.Errorf(": %w", err) - } - - err = b.attachFentryOrKprobe("udp_send_skb", b.objs.FentryUdpSendSkb, b.objs.KprobeUdpSendSkb) - if err != nil { - log.Infof("%+v", err) - if strings.Contains(err.Error(), "no such file or directory") { - err = b.attachFentryOrKprobe("udp_sendmsg", b.objs.FentryUdpSendmsg, b.objs.KprobeUdpSendmsg) - if err != nil { - return fmt.Errorf(": %w", err) - } - } else { - return fmt.Errorf(": %w", err) - } - } - - err = b.attachFentryOrKprobe("nf_nat_packet", - b.objs.FentryNfNatPacket, b.objs.KprobeNfNatPacket) - if err != nil { - log.Infof("%+v", err) - if strings.Contains(err.Error(), "no such file or directory") { - log.Info("the kernel does not support netfilter based NAT feature, skip attach kprobe/nf_nat_packet") - } else { - return fmt.Errorf(": %w", err) - } - } - - err = b.attachFentryOrKprobe("nf_nat_manip_pkt", - b.objs.FentryNfNatManipPkt, b.objs.KprobeNfNatManipPkt) - if err != nil { - log.Infof("%+v", err) - if strings.Contains(err.Error(), "no such file or directory") { - log.Info("the kernel does not support netfilter based NAT feature, skip attach kprobe/nf_nat_manip_pkt") - } else { + if b.skipAttachCgroup { + err = b.attachFentryOrKprobe("tcp_sendmsg", + b.objs.FentryTcpSendmsg, b.objs.KprobeTcpSendmsg) + if err != nil { return fmt.Errorf(": %w", err) } - } - - return b.attachNetDevHooks() -} - -func (b *BPF) attachNetDevHooks() error { - if !b.opts.hookNetDev { - return nil - } - - err := b.attachFexitOrKprobe("register_netdevice", - nil, b.objs.KprobeRegisterNetdevice, b.objs.KretprobeRegisterNetdevice) - if err != nil { - return err - } - - // TODO: refine - err = b.attachFexitOrKprobe("__dev_get_by_index", - nil, nil, b.objs.KretprobeDevGetByIndex) - if err != nil { - log.Infof("%+v", err) - if strings.Contains(err.Error(), "no such file or directory") { - err = b.attachFexitOrKprobe("dev_get_by_index", - nil, nil, b.objs.KretprobeDevGetByIndexLegacy) - if err != nil { - return err - } - } else { - return err - } - } - err = b.attachFentryOrKprobe("__dev_change_net_namespace", - nil, b.objs.KprobeDevChangeNetNamespace) - if err != nil { - log.Infof("%+v", err) - if strings.Contains(err.Error(), "no such file or directory") { - err = b.attachFentryOrKprobe("dev_change_net_namespace", - nil, b.objs.KprobeDevChangeNetNamespaceLegacy) - if err != nil { - return err + err = b.attachFentryOrKprobe("udp_send_skb", b.objs.FentryUdpSendSkb, b.objs.KprobeUdpSendSkb) + if err != nil { + log.Infof("%+v", err) + if isProbeNotSupportErr(err) { + err = b.attachFentryOrKprobe("udp_sendmsg", b.objs.FentryUdpSendmsg, b.objs.KprobeUdpSendmsg) + if err != nil { + return fmt.Errorf(": %w", err) + } + } else { + return fmt.Errorf(": %w", err) } - } else { - return err } } - err = b.attachFexitOrKprobe("__dev_change_net_namespace", - nil, nil, b.objs.KretprobeDevChangeNetNamespace) - if err != nil { - log.Infof("%+v", err) - if strings.Contains(err.Error(), "no such file or directory") { - err = b.attachFexitOrKprobe("dev_change_net_namespace", - nil, nil, b.objs.KretprobeDevChangeNetNamespaceLegacy) - if err != nil { - return err - } - } else { - return err - } + if err := b.attachNatHooks(); err != nil { + return fmt.Errorf(": %w", err) } - return nil + return b.attachNetDevHooks() } func (b *BPF) AttachTracepoints() error { @@ -472,6 +369,10 @@ func attachTcHook(ifindex int, prog *ebpf.Program, ingress bool) (func(), error) } } } + err = tcnl.SetOption(netlink.ExtendedAcknowledge, true) + if err != nil { + return closeFunc, fmt.Errorf("tc: set option ExtendedAcknowledge: %w", err) + } var filter *tc.Object fd := uint32(prog.FD()) @@ -544,6 +445,10 @@ func ensureTcQdisc(ifindex int) (func(), error) { } } } + err = tcnl.SetOption(netlink.ExtendedAcknowledge, true) + if err != nil { + return closeFunc, fmt.Errorf("tc: set option ExtendedAcknowledge: %w", err) + } qdisc := tc.Object{ Msg: tc.Msg{ diff --git a/bpf/cgroup.go b/bpf/cgroup.go new file mode 100644 index 00000000..62ac4c7c --- /dev/null +++ b/bpf/cgroup.go @@ -0,0 +1,41 @@ +package bpf + +import ( + "fmt" + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/mozillazg/ptcpdump/internal/log" +) + +func (b *BPF) AttachCgroups(cgroupPath string) error { + if cgroupPath == "" { + b.skipAttachCgroup = true + } + if b.skipAttachCgroup { + return nil + } + + log.Info("attaching cgroup/sock_create") + lk, err := link.AttachCgroup(link.CgroupOptions{ + Path: cgroupPath, + Attach: ebpf.AttachCGroupInetSockCreate, + Program: b.objs.CgroupSockCreate, + }) + if err != nil { + return fmt.Errorf("attach cgroup/sock_create: %w", err) + } + b.links = append(b.links, lk) + + log.Info("attaching cgroup/sock_release") + lk, err = link.AttachCgroup(link.CgroupOptions{ + Path: cgroupPath, + Attach: ebpf.AttachCgroupInetSockRelease, + Program: b.objs.CgroupSockRelease, + }) + if err != nil { + return fmt.Errorf("attach cgroup/sock_release: %w", err) + } + b.links = append(b.links, lk) + + return nil +} diff --git a/bpf/nat.go b/bpf/nat.go new file mode 100644 index 00000000..a3e8d2a5 --- /dev/null +++ b/bpf/nat.go @@ -0,0 +1,32 @@ +package bpf + +import ( + "fmt" + "github.com/mozillazg/ptcpdump/internal/log" +) + +func (b *BPF) attachNatHooks() error { + err := b.attachFentryOrKprobe("nf_nat_packet", + b.objs.FentryNfNatPacket, b.objs.KprobeNfNatPacket) + if err != nil { + log.Infof("%+v", err) + if isProbeNotSupportErr(err) { + log.Info("the kernel does not support netfilter based NAT feature, skip attach kprobe/nf_nat_packet") + } else { + return fmt.Errorf(": %w", err) + } + } + + err = b.attachFentryOrKprobe("nf_nat_manip_pkt", + b.objs.FentryNfNatManipPkt, b.objs.KprobeNfNatManipPkt) + if err != nil { + log.Infof("%+v", err) + if isProbeNotSupportErr(err) { + log.Info("the kernel does not support netfilter based NAT feature, skip attach kprobe/nf_nat_manip_pkt") + } else { + return fmt.Errorf(": %w", err) + } + } + + return nil +} diff --git a/bpf/net_dev.go b/bpf/net_dev.go new file mode 100644 index 00000000..fbaad536 --- /dev/null +++ b/bpf/net_dev.go @@ -0,0 +1,65 @@ +package bpf + +import ( + "github.com/mozillazg/ptcpdump/internal/log" +) + +func (b *BPF) attachNetDevHooks() error { + if !b.opts.hookNetDev { + return nil + } + + err := b.attachFexitOrKprobe("register_netdevice", + nil, b.objs.KprobeRegisterNetdevice, b.objs.KretprobeRegisterNetdevice) + if err != nil { + return err + } + + // TODO: refine + err = b.attachFexitOrKprobe("__dev_get_by_index", + nil, nil, b.objs.KretprobeDevGetByIndex) + if err != nil { + log.Infof("%+v", err) + if isProbeNotSupportErr(err) { + err = b.attachFexitOrKprobe("dev_get_by_index", + nil, nil, b.objs.KretprobeDevGetByIndexLegacy) + if err != nil { + return err + } + } else { + return err + } + } + + err = b.attachFentryOrKprobe("__dev_change_net_namespace", + nil, b.objs.KprobeDevChangeNetNamespace) + if err != nil { + log.Infof("%+v", err) + if isProbeNotSupportErr(err) { + err = b.attachFentryOrKprobe("dev_change_net_namespace", + nil, b.objs.KprobeDevChangeNetNamespaceLegacy) + if err != nil { + return err + } + } else { + return err + } + } + + err = b.attachFexitOrKprobe("__dev_change_net_namespace", + nil, nil, b.objs.KretprobeDevChangeNetNamespace) + if err != nil { + log.Infof("%+v", err) + if isProbeNotSupportErr(err) { + err = b.attachFexitOrKprobe("dev_change_net_namespace", + nil, nil, b.objs.KretprobeDevChangeNetNamespaceLegacy) + if err != nil { + return err + } + } else { + return err + } + } + + return nil +} diff --git a/bpf/utils.go b/bpf/utils.go index f27d73ca..40642981 100644 --- a/bpf/utils.go +++ b/bpf/utils.go @@ -5,6 +5,7 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/mozillazg/ptcpdump/internal/log" + "strings" ) func (b *BPF) attachFentryOrKprobe(symbol string, fentryProg *ebpf.Program, kprobeProg *ebpf.Program) error { @@ -101,3 +102,14 @@ func (b *BPF) attachBTFTracepointOrRawTP(name string, btfProg *ebpf.Program, raw return nil } + +func isProbeNotSupportErr(err error) bool { + // TODO: refine + if strings.Contains(err.Error(), "no such file or directory") || + strings.Contains(err.Error(), "invalid argument") { + log.Infof("%T", err) + log.Infof("%#v", err) + return true + } + return false +} diff --git a/go.mod b/go.mod index 22ad9d49..151e80c9 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/containerd/errdefs v0.3.0 github.com/go-logr/logr v1.4.2 github.com/mandiant/GoReSym v1.7.2-0.20240819162932-534ca84b42d5 + github.com/mdlayher/netlink v1.7.2 github.com/smira/go-xz v0.1.0 github.com/stretchr/testify v1.9.0 github.com/vishvananda/netns v0.0.5 @@ -61,7 +62,6 @@ require ( github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect diff --git a/internal/capturer/capturer.go b/internal/capturer/capturer.go index ad5e06d4..a744a86b 100644 --- a/internal/capturer/capturer.go +++ b/internal/capturer/capturer.go @@ -181,10 +181,8 @@ func (c *Capturer) AttachTracingHooks() error { 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.AttachCgroups(cgroupPath); err != nil { + return err } if err := bf.AttachKprobes(); err != nil { diff --git a/internal/utils/cgroup.go b/internal/utils/cgroup.go index dcdbb941..b9016264 100644 --- a/internal/utils/cgroup.go +++ b/internal/utils/cgroup.go @@ -16,12 +16,6 @@ var reCgroup2Mount = regexp.MustCompile(`(?m)^cgroup2\s(/\S+)\scgroup2\s`) func GetCgroupV2RootDir() (string, error) { p, err := getCgroupV2RootDir(pathProcMounts) - if err != nil { - st, errv2 := os.Stat(defaultCgroupV2RootDir) - if errv2 == nil && st.IsDir() { - return defaultCgroupV2RootDir, nil - } - } return p, err }