Skip to content

Commit

Permalink
chore(bpf): Improve compatibility with older kernels (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
mozillazg authored Nov 17, 2024
1 parent 6f689b8 commit a4ca7e9
Show file tree
Hide file tree
Showing 21 changed files with 274 additions and 405 deletions.
149 changes: 54 additions & 95 deletions bpf/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ type BPF struct {
closeFuncs []func()

skipAttachCgroup bool
skipOptimize bool
isLegacyKernel bool
report *types.CountReport
}
Expand All @@ -55,19 +54,25 @@ type Options struct {

func NewBPF() (*BPF, error) {
var legacyKernel bool
var skipOptimize bool
var skipAttachCgroup bool
if ok, err := isLegacyKernel(); err != nil {
log.Warnf("%s", err)
} else {
legacyKernel = ok
}
if !supportCgroupSock() {
skipAttachCgroup = true
legacyKernel = true
}

b := _BpfBytes
if legacyKernel {
skipOptimize = true
b = _Bpf_legacyBytes
} else if !supportTracing() {
skipOptimize = true
b = _Bpf_no_optimizeBytes
skipAttachCgroup = true
} else {
if !supportTracing() {
b = _Bpf_no_tracingBytes
}
}

spec, err := loadBpfWithData(b)
Expand All @@ -80,8 +85,7 @@ func NewBPF() (*BPF, error) {
objs: &BpfObjects{},
report: &types.CountReport{},
isLegacyKernel: legacyKernel,
skipAttachCgroup: legacyKernel,
skipOptimize: skipOptimize,
skipAttachCgroup: skipAttachCgroup,
}

return bf, nil
Expand Down Expand Up @@ -129,53 +133,17 @@ func (b *BPF) Load(opts Options) error {
}
}

// TODO: refine
loadCount := 0
load:
loadCount++
if b.isLegacyKernel || !supportCgroupSock() {
log.Info("will load the objs for legacy kernel")
b.skipAttachCgroup = true
objs := BpfObjectsForLegacyKernel{}
if err = b.spec.LoadAndAssign(&objs, &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
KernelTypes: opts.kernelTypes,
LogLevel: ebpf.LogLevelInstruction,
LogSize: logSzie,
},
}); err != nil {
return fmt.Errorf("bpf load: %w", err)
}
b.objs.FromLegacy(&objs)
} else if b.skipOptimize {
log.Info("will load the objs for skip optimize")
objs := BpfObjectsForNoOptimize{}
if err = b.spec.LoadAndAssign(&objs, &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
KernelTypes: opts.kernelTypes,
LogLevel: ebpf.LogLevelInstruction,
LogSize: logSzie,
},
}); err != nil {
return fmt.Errorf("bpf load: %w", err)
}
b.objs.FromNoOptimize(&objs)
} else {
err = b.spec.LoadAndAssign(b.objs, &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
KernelTypes: opts.kernelTypes,
LogLevel: ebpf.LogLevelInstruction,
LogSize: logSzie,
},
})
if err != nil {
if loadCount < 2 && isTracingNotSupportErr(err) {
log.Infof("bpf load failed, try again: %+v", err)
b.skipOptimize = true
goto load
}
return fmt.Errorf("bpf load: %w", err)
}
err = b.spec.LoadAndAssign(b.objs, &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
KernelTypes: opts.kernelTypes,
LogLevel: ebpf.LogLevelInstruction,
LogSize: logSzie,
},
IgnoreUnknownProgram: true,
IgnoreNotSupportedProgram: true,
})
if err != nil {
return fmt.Errorf("bpf load and assign: %w", err)
}

b.opts = opts
Expand Down Expand Up @@ -266,7 +234,6 @@ func (b *BPF) AttachKprobes() error {
err = b.attachFentryOrKprobe("udp_send_skb", b.objs.FentryUdpSendSkb, b.objs.KprobeUdpSendSkb)
if err != nil {
log.Infof("%+v", err)
// TODO: use errors.Is(xxx) or ==
if strings.Contains(err.Error(), "no such file or directory") {
err = b.attachFentryOrKprobe("udp_sendmsg", b.objs.FentryUdpSendmsg, b.objs.KprobeUdpSendmsg)
if err != nil {
Expand All @@ -280,8 +247,8 @@ func (b *BPF) AttachKprobes() error {
err = b.attachFentryOrKprobe("nf_nat_packet",
b.objs.FentryNfNatPacket, b.objs.KprobeNfNatPacket)
if err != nil {
// TODO: use errors.Is(xxx) or ==
if strings.Contains(err.Error(), "nf_nat_packet: not found: no such file or directory") {
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)
Expand All @@ -291,8 +258,8 @@ func (b *BPF) AttachKprobes() error {
err = b.attachFentryOrKprobe("nf_nat_manip_pkt",
b.objs.FentryNfNatManipPkt, b.objs.KprobeNfNatManipPkt)
if err != nil {
// TODO: use errors.Is(xxx) or ==
if strings.Contains(err.Error(), "nf_nat_manip_pkt: not found: no such file or directory") {
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 {
return fmt.Errorf(": %w", err)
Expand All @@ -307,67 +274,57 @@ func (b *BPF) attachNetDevHooks() error {
return nil
}

// TODO: refine
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{})
err := b.attachFexitOrKprobe("register_netdevice",
nil, b.objs.KprobeRegisterNetdevice, b.objs.KretprobeRegisterNetdevice)
if err != nil {
return fmt.Errorf("attach kretprobe/register_netdevice: %w", err)
return err
}
b.links = append(b.links, lk)
lk, err = link.Kretprobe("__dev_get_by_index",
b.objs.KretprobeDevGetByIndex, &link.KprobeOptions{})

// TODO: refine
err = b.attachFexitOrKprobe("__dev_get_by_index",
nil, nil, b.objs.KretprobeDevGetByIndex)
if err != nil {
log.Infof("%+v", err)
// TODO: use errors.Is(xxx) or ==
if strings.Contains(err.Error(), "no such file or directory") {
lk, err = link.Kretprobe("dev_get_by_index",
b.objs.KretprobeDevGetByIndexLegacy, &link.KprobeOptions{})
err = b.attachFexitOrKprobe("dev_get_by_index",
nil, nil, b.objs.KretprobeDevGetByIndexLegacy)
if err != nil {
return fmt.Errorf("attach kretprobe/dev_get_by_index: %w", err)
return err
}
} else {
return fmt.Errorf("attach kretprobe/__dev_get_by_index: %w", err)
return err
}
}
b.links = append(b.links, lk)
lk, err = link.Kprobe("__dev_change_net_namespace",
b.objs.KprobeDevChangeNetNamespace, &link.KprobeOptions{})

err = b.attachFentryOrKprobe("__dev_change_net_namespace",
nil, b.objs.KprobeDevChangeNetNamespace)
if err != nil {
log.Infof("%+v", err)
// TODO: use errors.Is(xxx) or ==
if strings.Contains(err.Error(), "no such file or directory") {
lk, err = link.Kprobe("dev_change_net_namespace",
b.objs.KprobeDevChangeNetNamespaceLegacy, &link.KprobeOptions{})
err = b.attachFentryOrKprobe("dev_change_net_namespace",
nil, b.objs.KprobeDevChangeNetNamespaceLegacy)
if err != nil {
return fmt.Errorf("attach kprobe/dev_change_net_namespace: %w", err)
return err
}
} else {
return fmt.Errorf("attach kprobe/__dev_change_net_namespace: %w", err)
return err
}
}
b.links = append(b.links, lk)
lk, err = link.Kretprobe("__dev_change_net_namespace",
b.objs.KretprobeDevChangeNetNamespace, &link.KprobeOptions{})

err = b.attachFexitOrKprobe("__dev_change_net_namespace",
nil, nil, b.objs.KretprobeDevChangeNetNamespace)
if err != nil {
log.Infof("%+v", err)
// TODO: use errors.Is(xxx) or ==
if strings.Contains(err.Error(), "no such file or directory") {
lk, err = link.Kretprobe("dev_change_net_namespace",
b.objs.KretprobeDevChangeNetNamespaceLegacy, &link.KprobeOptions{})
err = b.attachFexitOrKprobe("dev_change_net_namespace",
nil, nil, b.objs.KretprobeDevChangeNetNamespaceLegacy)
if err != nil {
return fmt.Errorf("attach kretprobe/dev_change_net_namespace: %w", err)
return err
}
} else {
return fmt.Errorf("attach kretprobe/__dev_change_net_namespace: %w", err)
return err
}
}
b.links = append(b.links, lk)

return nil
}
Expand Down Expand Up @@ -397,11 +354,13 @@ func (b *BPF) AttachTracepoints() error {
}

if b.opts.hookMount {
log.Info("attaching tracepoint/syscalls/sys_enter_mount")
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)
log.Info("attaching tracepoint/syscalls/sys_exit_mount")
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)
Expand Down
Binary file modified bpf/bpf_arm64_bpfel.o
Binary file not shown.
58 changes: 1 addition & 57 deletions bpf/bpf_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,63 +11,7 @@ import (
)

// $TARGET is set by the Makefile
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -no-strip -no-global-types -target $TARGET bpf_legacy ./ptcpdump.c -- -I./headers -I./headers/$TARGET -I. -Wall -DLEGACY_KERNEL -DNO_CGROUP_PROG -DNO_OPTIMIZE

type BpfObjectsForLegacyKernel struct {
KprobeDevChangeNetNamespace *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace"`
KprobeDevChangeNetNamespaceLegacy *ebpf.Program `ebpf:"kprobe__dev_change_net_namespace_legacy"`
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"`
KretprobeDevChangeNetNamespaceLegacy *ebpf.Program `ebpf:"kretprobe__dev_change_net_namespace_legacy"`
KretprobeDevGetByIndex *ebpf.Program `ebpf:"kretprobe__dev_get_by_index"`
KretprobeDevGetByIndexLegacy *ebpf.Program `ebpf:"kretprobe__dev_get_by_index_legacy"`
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"`

BpfMaps
}

func (b *BpfObjects) FromLegacy(o *BpfObjectsForLegacyKernel) {
b.KprobeTcpSendmsg = o.KprobeTcpSendmsg
b.KprobeUdpSendmsg = o.KprobeUdpSendmsg
b.KprobeUdpSendSkb = o.KprobeUdpSendSkb
b.KprobeNfNatManipPkt = o.KprobeNfNatManipPkt
b.KprobeNfNatPacket = o.KprobeNfNatPacket
b.KprobeSecuritySkClassifyFlow = o.KprobeSecuritySkClassifyFlow
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.KprobeDevChangeNetNamespaceLegacy = o.KprobeDevChangeNetNamespaceLegacy
b.KretprobeDevChangeNetNamespace = o.KretprobeDevChangeNetNamespace
b.KretprobeDevChangeNetNamespaceLegacy = o.KretprobeDevChangeNetNamespaceLegacy
b.KretprobeDevGetByIndex = o.KretprobeDevGetByIndex
b.KretprobeDevGetByIndexLegacy = o.KretprobeDevGetByIndexLegacy
b.TracepointSyscallsSysEnterMount = o.TracepointSyscallsSysEnterMount
b.TracepointSyscallsSysExitMount = o.TracepointSyscallsSysExitMount
b.TcEgress = o.TcEgress
b.TcIngress = o.TcIngress
b.UprobeGoBuiltinTlsWriteKeyLog = o.UprobeGoBuiltinTlsWriteKeyLog
b.UprobeGoBuiltinTlsWriteKeyLogRet = o.UprobeGoBuiltinTlsWriteKeyLogRet

b.BpfMaps = o.BpfMaps
}
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -no-strip -no-global-types -target $TARGET bpf_legacy ./ptcpdump.c -- -I./headers -I./headers/$TARGET -I. -Wall -DLEGACY_KERNEL -DNO_CGROUP_PROG -DNO_TRACING

func supportCgroupSock() bool {
if err := features.HaveProgramHelper(ebpf.CGroupSock, asm.FnGetSocketCookie); err != nil {
Expand Down
3 changes: 0 additions & 3 deletions bpf/bpf_legacy_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified bpf/bpf_legacy_arm64_bpfel.o
Binary file not shown.
3 changes: 0 additions & 3 deletions bpf/bpf_legacy_x86_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified bpf/bpf_legacy_x86_bpfel.o
Binary file not shown.
Loading

0 comments on commit a4ca7e9

Please sign in to comment.