Skip to content

Commit

Permalink
optimize(bpf): Skip attaching {tcp,udp}_send* hooks when cgroup hooks…
Browse files Browse the repository at this point in the history
… are attached (#197)

* optimize(bpf): Skip attaching {tcp,udp}_send* hooks when cgroup hooks are attached

* fix missing `tcnl.SetOption(netlink.ExtendedAcknowledge, true)`

* remove fallbak dir when not found cgroup v2 root dir

* fix `-i any` will cause an error when running on Alibaba Cloud Linux (Aliyun Linux) release 2.1903 LTS
mozillazg authored Nov 30, 2024

Verified

This commit was signed with the committer’s verified signature.
bostonaholic Matthew Boston
1 parent 64ad6b3 commit 1277cdb
Showing 8 changed files with 179 additions and 132 deletions.
147 changes: 26 additions & 121 deletions bpf/bpf.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/mdlayher/netlink"
"strings"
"unsafe"

@@ -198,143 +199,39 @@ 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)
if err != nil {
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{
41 changes: 41 additions & 0 deletions bpf/cgroup.go
Original file line number Diff line number Diff line change
@@ -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
}
32 changes: 32 additions & 0 deletions bpf/nat.go
Original file line number Diff line number Diff line change
@@ -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
}
65 changes: 65 additions & 0 deletions bpf/net_dev.go
Original file line number Diff line number Diff line change
@@ -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
}
12 changes: 12 additions & 0 deletions bpf/utils.go
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -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
6 changes: 2 additions & 4 deletions internal/capturer/capturer.go
Original file line number Diff line number Diff line change
@@ -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 {
6 changes: 0 additions & 6 deletions internal/utils/cgroup.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 1277cdb

Please sign in to comment.