diff --git a/.gitignore b/.gitignore index 3756b3df..834a28eb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ release tags kprobepwru_* kprobemultipwru_* +featurespwru_* !pwru/ diff --git a/bpf/features.c b/bpf/features.c new file mode 100644 index 00000000..a154e6c6 --- /dev/null +++ b/bpf/features.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright Leon Hwang */ + +#include "vmlinux.h" +#include "bpf/bpf_helpers.h" +#include "bpf/bpf_core_read.h" + +struct pwru_features { + bool kprobe_happened; + bool has_snprintf_btf; +} features; + +SEC("kprobe") +int kprobe__map_lookup_elem(struct pt_regs *ctx) +{ + features.kprobe_happened = true; + + /* Detect if bpf_snprintf_btf is available. + * Since: c4d0bfb45068 ("bpf: Add bpf_snprintf_btf helper") + */ + features.has_snprintf_btf = bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_snprintf_btf); + + return BPF_OK; +} + +char __license[] SEC("license") = "Dual BSD/GPL"; diff --git a/build.go b/build.go index a7258379..126334d6 100644 --- a/build.go +++ b/build.go @@ -4,5 +4,6 @@ //go:generate sh -c "echo Generating for $TARGET_GOARCH" //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET_GOARCH -cc clang -no-strip KProbePWRU ./bpf/kprobe_pwru.c -- -I./bpf/headers -Wno-address-of-packed-member //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET_GOARCH -cc clang -no-strip KProbeMultiPWRU ./bpf/kprobe_pwru.c -- -DHAS_KPROBE_MULTI -I./bpf/headers -Wno-address-of-packed-member +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET_GOARCH -cc clang -no-strip FeaturesPWRU ./bpf/features.c -- -I./bpf/headers -Wno-address-of-packed-member package main diff --git a/internal/pwru/features.go b/internal/pwru/features.go new file mode 100644 index 00000000..04e22899 --- /dev/null +++ b/internal/pwru/features.go @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright Leon Hwang */ + +package pwru + +import ( + "log" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/btf" + "github.com/cilium/ebpf/link" +) + +func DetectFeatures(spec *ebpf.CollectionSpec, btfSpec *btf.Spec, f *Flags) { + bssMap, err := ebpf.NewMap(spec.Maps[".bss"]) + if err != nil { + log.Fatalf("Failed to create bss map: %v", err) + } + defer bssMap.Close() + + coll, err := ebpf.NewCollectionWithOptions(spec, ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + KernelTypes: btfSpec, + }, + MapReplacements: map[string]*ebpf.Map{ + ".bss": bssMap, + }, + }) + if err != nil { + log.Fatalf("Failed to create collection: %v", err) + } + defer coll.Close() + + kp, err := link.Kprobe("map_lookup_elem", coll.Programs["kprobe__map_lookup_elem"], nil) + if err != nil { + log.Fatalf("Failed to kprobe map_lookup_elem: %v", err) + } + defer kp.Close() + + var features struct { + Happened uint8 + HasSnprintfBtf uint8 + } + + // Trigger kprobe and retrieve features data + err = bssMap.Lookup(uint32(0), &features) + if err != nil { + log.Fatalf("Failed to lookup features data: %v", err) + } + + if features.Happened == 0 { + log.Fatalf("Features detection was not triggered") + } + + if (f.OutputSkb || f.OutputShinfo) && features.HasSnprintfBtf == 0 { + log.Fatalf("Unsupported to output skb or shinfo because bpf_snprintf_btf() is unavailable") + } +} diff --git a/main.go b/main.go index e0352917..2aa18137 100644 --- a/main.go +++ b/main.go @@ -67,6 +67,13 @@ func main() { log.Fatalf("Failed to load BTF spec: %s", err) } + featBpfSpec, err := LoadFeaturesPWRU() + if err != nil { + log.Fatalf("Failed to load features bpf spec: %v", err) + } + + pwru.DetectFeatures(featBpfSpec, btfSpec, &flags) + if flags.AllKMods { files, err := os.ReadDir("/sys/kernel/btf") if err != nil {