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
  • Loading branch information
mozillazg authored Nov 30, 2024
1 parent 64ad6b3 commit 1277cdb
Show file tree
Hide file tree
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
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/mdlayher/netlink"
"strings"
"unsafe"

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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{
Expand Down
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
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions internal/capturer/capturer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 0 additions & 6 deletions internal/utils/cgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down

0 comments on commit 1277cdb

Please sign in to comment.