diff --git a/.gitignore b/.gitignore index 96e9462d..6ce7c20c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ # vendor/ test.pcapng ptcpdump +ptcpdump.pcapng diff --git a/bpf/bpf.go b/bpf/bpf.go index 0d1767b5..69e9b779 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -49,17 +49,22 @@ func (b *BPF) Load() error { func (b *BPF) Close() { for _, lk := range b.links { - lk.Close() + if err := lk.Close(); err != nil { + log.Printf("[bpf] close link %v failed: %+v", lk, err) + } } for i := len(b.closeFuncs) - 1; i > 0; i-- { f := b.closeFuncs[i] f() } - b.objs.Close() + if err := b.objs.Close(); err != nil { + log.Printf("[bpf] close objects failed: %+v", err) + } } func (b *BPF) AttachKprobes() error { - lk, err := link.Kprobe("security_sk_classify_flow", b.objs.KprobeSecuritySkClassifyFlow, &link.KprobeOptions{}) + lk, err := link.Kprobe("security_sk_classify_flow", + b.objs.KprobeSecuritySkClassifyFlow, &link.KprobeOptions{}) if err != nil { return xerrors.Errorf("attach kprobe/security_sk_classify_flow: %w", err) } diff --git a/bpf/bpf_x86_bpfel.o b/bpf/bpf_x86_bpfel.o index df74eb11..99880b9d 100644 Binary files a/bpf/bpf_x86_bpfel.o and b/bpf/bpf_x86_bpfel.o differ diff --git a/bpf/ptcpdump.c b/bpf/ptcpdump.c index 05616a8e..daf77410 100644 --- a/bpf/ptcpdump.c +++ b/bpf/ptcpdump.c @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore #include "vmlinux.h" diff --git a/bpf/tools.go b/bpf/tools.go new file mode 100644 index 00000000..9afaed28 --- /dev/null +++ b/bpf/tools.go @@ -0,0 +1,6 @@ +//go:build ignore +// +build ignore + +package bpf + +import _ "github.com/cilium/ebpf/cmd/bpf2go" diff --git a/internal/event/net.go b/internal/event/net.go new file mode 100644 index 00000000..9f09adf1 --- /dev/null +++ b/internal/event/net.go @@ -0,0 +1,59 @@ +package event + +import ( + "bytes" + "encoding/binary" + "github.com/mozillazg/ptcpdump/bpf" + "golang.org/x/xerrors" + "unsafe" +) + +type packetType int + +const ( + packetTypeIngress packetType = 0 + packetTypeEgress packetType = 1 +) + +type Packet struct { + Type packetType + Pid int + Comm string + + Data []byte +} + +func ParsePacketEvent(rawSample []byte) (*Packet, error) { + var p Packet + event := bpf.BpfPacketEventT{} + if err := binary.Read(bytes.NewBuffer(rawSample), binary.LittleEndian, &event.Meta); err != nil { + return nil, xerrors.Errorf("parse meta: %w", err) + } + copy(event.Payload[:], rawSample[unsafe.Offsetof(event.Payload):]) + + p.Pid = int(event.Meta.Pid) + p.Comm = strComm(event.Meta.Comm) + if event.Meta.PacketType == 1 { + p.Type = packetTypeEgress + } + p.Data = make([]byte, event.Meta.PayloadLen) + copy(p.Data[:], event.Payload[:event.Meta.PayloadLen]) + + return &p, nil +} + +func (p Packet) Ingress() bool { + return p.Type == packetTypeIngress +} + +func (p Packet) Egress() bool { + return p.Type == packetTypeEgress +} + +func strComm(comm [16]int8) string { + b := make([]byte, len(comm)) + for i, c := range comm { + b[i] = byte(c) + } + return string(b) +} diff --git a/internal/writer/pcapng.go b/internal/writer/pcapng.go new file mode 100644 index 00000000..76e734a4 --- /dev/null +++ b/internal/writer/pcapng.go @@ -0,0 +1,41 @@ +package writer + +import ( + "fmt" + "github.com/gopacket/gopacket" + "github.com/gopacket/gopacket/pcapgo" + "github.com/mozillazg/ptcpdump/internal/event" + "golang.org/x/xerrors" + "time" +) + +type PcapNGWriter struct { + pw *pcapgo.NgWriter +} + +func NewPcapNGWriter(pw *pcapgo.NgWriter) *PcapNGWriter { + return &PcapNGWriter{pw: pw} +} + +func (w *PcapNGWriter) Write(p *event.Packet) error { + payloadLen := len(p.Data) + info := gopacket.CaptureInfo{ + Timestamp: time.Now(), + CaptureLength: payloadLen, + Length: payloadLen, + InterfaceIndex: 0, + } + opts := pcapgo.NgPacketOptions{ + Comment: fmt.Sprintf("PID: %d\nCOMMAND: %s", p.Pid, p.Comm), + } + + if err := w.pw.WritePacketWithOptions(info, p.Data, opts); err != nil { + return xerrors.Errorf("writing packet: %w", err) + } + + return nil +} + +func (w *PcapNGWriter) Flush() error { + return w.pw.Flush() +} diff --git a/internal/writer/stdout.go b/internal/writer/stdout.go new file mode 100644 index 00000000..60c48b69 --- /dev/null +++ b/internal/writer/stdout.go @@ -0,0 +1,48 @@ +package writer + +import ( + "github.com/gopacket/gopacket" + "github.com/gopacket/gopacket/layers" + "github.com/mozillazg/ptcpdump/internal/event" + "log" +) + +type StdoutWriter struct { +} + +func NewStdoutWriter() *StdoutWriter { + return &StdoutWriter{} +} + +func (w *StdoutWriter) Write(p *event.Packet) error { + packetType := "=>· " + if p.Egress() { + packetType = " ·=>" + } + + // Decode a packet + packet := gopacket.NewPacket(p.Data, layers.LayerTypeEthernet, gopacket.Default) + var ipv4 *layers.IPv4 + if ipv4Layer := packet.Layer(layers.LayerTypeIPv4); ipv4Layer != nil { + ipv4, _ = ipv4Layer.(*layers.IPv4) + } + if ipv4 == nil { + return nil + } + if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + tcp, _ := tcpLayer.(*layers.TCP) + log.Printf("%s %s-%d %s:%d => %s:%d", + packetType, p.Comm, p.Pid, + ipv4.SrcIP.String(), tcp.SrcPort, + ipv4.DstIP.String(), tcp.DstPort) + } + if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil { + udp, _ := udpLayer.(*layers.UDP) + log.Printf("%s %s-%d %s:%d => %s:%d", + packetType, p.Comm, p.Pid, + ipv4.SrcIP.String(), udp.SrcPort, + ipv4.DstIP.String(), udp.DstPort) + } + + return nil +} diff --git a/internal/writer/writer.go b/internal/writer/writer.go new file mode 100644 index 00000000..fb2cc91a --- /dev/null +++ b/internal/writer/writer.go @@ -0,0 +1,4 @@ +package writer + +type PacketWriter struct { +} diff --git a/main.go b/main.go index 1b9eac6c..c9a533e1 100644 --- a/main.go +++ b/main.go @@ -1,29 +1,25 @@ package main import ( - "bytes" "context" - "encoding/binary" "errors" "fmt" - "io" - "log" - "math" - "net" - "os" - "os/signal" - "syscall" - "time" - "unsafe" - "github.com/cilium/ebpf" "github.com/cilium/ebpf/perf" "github.com/cilium/ebpf/rlimit" - "github.com/gopacket/gopacket" "github.com/gopacket/gopacket/layers" "github.com/gopacket/gopacket/pcapgo" "github.com/mozillazg/ptcpdump/bpf" + "github.com/mozillazg/ptcpdump/internal/event" + "github.com/mozillazg/ptcpdump/internal/writer" "golang.org/x/xerrors" + "io" + "log" + "math" + "net" + "os" + "os/signal" + "syscall" ) func logErr(err error) { @@ -36,71 +32,23 @@ func logErr(err error) { log.Printf("%+v", err) } -func parseEvent(pcapWriter *pcapgo.NgWriter, rawSample []byte) { - event := bpf.BpfPacketEventT{} - if err := binary.Read(bytes.NewBuffer(rawSample), binary.LittleEndian, &event.Meta); err != nil { - log.Printf("parse event failed: %+v", err) +func parseEvent(pcapWriter *writer.PcapNGWriter, rawSample []byte) { + pevent, err := event.ParsePacketEvent(rawSample) + if err != nil { + logErr(err) return } - copy(event.Payload[:], rawSample[unsafe.Offsetof(event.Payload):]) - - packetType := "=>· " - if event.Meta.PacketType == 1 { - packetType = " ·=>" - } - - // Decode a packet - data := event.Payload[:] - packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default) - var ipv4 *layers.IPv4 - if ipv4Layer := packet.Layer(layers.LayerTypeIPv4); ipv4Layer != nil { - ipv4, _ = ipv4Layer.(*layers.IPv4) - } - if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { - tcp, _ := tcpLayer.(*layers.TCP) - log.Printf("%s %s-%d %s:%d => %s:%d", - packetType, strComm(event.Meta.Comm), event.Meta.Pid, - ipv4.SrcIP.String(), tcp.SrcPort, - ipv4.DstIP.String(), tcp.DstPort) - } - if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil { - udp, _ := udpLayer.(*layers.UDP) - log.Printf("%s %s-%d %s:%d => %s:%d", - packetType, strComm(event.Meta.Comm), event.Meta.Pid, - ipv4.SrcIP.String(), udp.SrcPort, - ipv4.DstIP.String(), udp.DstPort) - } - - payloadLen := int(event.Meta.PayloadLen) - info := gopacket.CaptureInfo{ - Timestamp: time.Now(), - CaptureLength: payloadLen, - Length: payloadLen, - InterfaceIndex: 0, - } - packetData := make([]byte, payloadLen) - copy(packetData, data[:payloadLen]) - log.Printf("len1: %d, len2: %d", len(packetData), len(data[:payloadLen])) - opts := pcapgo.NgPacketOptions{ - Comment: fmt.Sprintf("PID: %d\nCOMMAND: %s", event.Meta.Pid, strComm(event.Meta.Comm)), - } - if err := pcapWriter.WritePacketWithOptions(info, packetData, opts); err != nil { - // if err := pcapWriter.WritePacket(info, packetData); err != nil { - log.Printf("Error writing packet: %+v", err) + if err := writer.NewStdoutWriter().Write(pevent); err != nil { + logErr(err) } - pcapWriter.Flush() -} -func strComm(comm [16]int8) string { - b := []byte{} - for _, c := range comm { - b = append(b, byte(c)) + if err := pcapWriter.Write(pevent); err != nil { + logErr(err) } - return string(b) } -func newPcapWriter(w io.Writer, ifaceNames []string) (*pcapgo.NgWriter, map[string]int, error) { +func newPcapWriter(w io.Writer, ifaceNames []string) (*writer.PcapNGWriter, map[string]int, error) { if len(ifaceNames) == 0 { return nil, nil, xerrors.New("can't create pcap with no ifaceNames") } @@ -135,7 +83,7 @@ func newPcapWriter(w io.Writer, ifaceNames []string) (*pcapgo.NgWriter, map[stri return nil, nil, xerrors.Errorf("writing pcap header: %w", err) } - return pcapWriter, nameIfcs, nil + return writer.NewPcapNGWriter(pcapWriter), nameIfcs, nil } func main() { @@ -143,12 +91,12 @@ func main() { logErr(err) return } - pcapFile, err := os.Create("test.pcapng") + pcapFile, err := os.Create("ptcpdump.pcapng") if err != nil { logErr(err) return } - pcapWriter, _, err := newPcapWriter(pcapFile, []string{"lo", "enp0s3", "docker0"}) + pcapWriter, _, err := newPcapWriter(pcapFile, []string{"lo", "enp0s3", "docker0", "wlp4s0", "enp5s0"}) if err != nil { logErr(err) return @@ -171,7 +119,7 @@ func main() { return } - for _, ifaceName := range []string{"lo", "enp0s3", "docker0"} { + for _, ifaceName := range []string{"lo", "enp0s3", "docker0", "wlp4s0", "enp5s0"} { dev, err := net.InterfaceByName(ifaceName) if err != nil { log.Printf("get interface by name %s failed: %+v", ifaceName, err) diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/README.md b/vendor/github.com/cilium/ebpf/cmd/bpf2go/README.md new file mode 100644 index 00000000..bbc58e55 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/README.md @@ -0,0 +1,39 @@ +bpf2go +=== + +`bpf2go` compiles a C source file into eBPF bytecode and then emits a +Go file containing the eBPF. The goal is to avoid loading the +eBPF from disk at runtime and to minimise the amount of manual +work required to interact with eBPF programs. It takes inspiration +from `bpftool gen skeleton`. + +Invoke the program using go generate: + + //go:generate go run github.com/cilium/ebpf/cmd/bpf2go foo path/to/src.c -- -I/path/to/include + +This will emit `foo_bpfel.go` and `foo_bpfeb.go`, with types using `foo` +as a stem. The two files contain compiled BPF for little and big +endian systems, respectively. + +## Environment Variables + +You can use environment variables to affect all bpf2go invocations +across a project, e.g. to set specific C flags: + + BPF2GO_FLAGS="-O2 -g -Wall -Werror $(CFLAGS)" go generate ./... + +Alternatively, by exporting `$BPF2GO_FLAGS` from your build system, you can +control all builds from a single location. + +Most bpf2go arguments can be controlled this way. See `bpf2go -h` for an +up-to-date list. + +## Generated types + +`bpf2go` generates Go types for all map keys and values by default. You can +disable this behaviour using `-no-global-types`. You can add to the set of +types by specifying `-type foo` for each type you'd like to generate. + +## Examples + +See [examples/kprobe](../../examples/kprobe/main.go) for a fully worked out example. diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/compile.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/compile.go new file mode 100644 index 00000000..2aa08f92 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/compile.go @@ -0,0 +1,210 @@ +package main + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strings" +) + +type compileArgs struct { + // Which compiler to use + cc string + cFlags []string + // Absolute working directory + dir string + // Absolute input file name + source string + // Absolute output file name + dest string + // Target to compile for, defaults to "bpf". + target string + // Depfile will be written here if depName is not empty + dep io.Writer +} + +func compile(args compileArgs) error { + // Default cflags that can be overridden by args.cFlags + overrideFlags := []string{ + // Code needs to be optimized, otherwise the verifier will often fail + // to understand it. + "-O2", + // Clang defaults to mcpu=probe which checks the kernel that we are + // compiling on. This isn't appropriate for ahead of time + // compiled code so force the most compatible version. + "-mcpu=v1", + } + + cmd := exec.Command(args.cc, append(overrideFlags, args.cFlags...)...) + cmd.Stderr = os.Stderr + + inputDir := filepath.Dir(args.source) + relInputDir, err := filepath.Rel(args.dir, inputDir) + if err != nil { + return err + } + + target := args.target + if target == "" { + target = "bpf" + } + + // C flags that can't be overridden. + cmd.Args = append(cmd.Args, + "-target", target, + "-c", args.source, + "-o", args.dest, + // Don't include clang version + "-fno-ident", + // Don't output inputDir into debug info + "-fdebug-prefix-map="+inputDir+"="+relInputDir, + "-fdebug-compilation-dir", ".", + // We always want BTF to be generated, so enforce debug symbols + "-g", + fmt.Sprintf("-D__BPF_TARGET_MISSING=%q", "GCC error \"The eBPF is using target specific macros, please provide -target that is not bpf, bpfel or bpfeb\""), + ) + cmd.Dir = args.dir + + var depFile *os.File + if args.dep != nil { + depFile, err = os.CreateTemp("", "bpf2go") + if err != nil { + return err + } + defer depFile.Close() + defer os.Remove(depFile.Name()) + + cmd.Args = append(cmd.Args, + // Output dependency information. + "-MD", + // Create phony targets so that deleting a dependency doesn't + // break the build. + "-MP", + // Write it to temporary file + "-MF"+depFile.Name(), + ) + } + + if err := cmd.Run(); err != nil { + return fmt.Errorf("can't execute %s: %s", args.cc, err) + } + + if depFile != nil { + if _, err := io.Copy(args.dep, depFile); err != nil { + return fmt.Errorf("error writing depfile: %w", err) + } + } + + return nil +} + +func adjustDependencies(baseDir string, deps []dependency) ([]byte, error) { + var buf bytes.Buffer + for _, dep := range deps { + relativeFile, err := filepath.Rel(baseDir, dep.file) + if err != nil { + return nil, err + } + + if len(dep.prerequisites) == 0 { + _, err := fmt.Fprintf(&buf, "%s:\n\n", relativeFile) + if err != nil { + return nil, err + } + continue + } + + var prereqs []string + for _, prereq := range dep.prerequisites { + relativePrereq, err := filepath.Rel(baseDir, prereq) + if err != nil { + return nil, err + } + + prereqs = append(prereqs, relativePrereq) + } + + _, err = fmt.Fprintf(&buf, "%s: \\\n %s\n\n", relativeFile, strings.Join(prereqs, " \\\n ")) + if err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} + +type dependency struct { + file string + prerequisites []string +} + +func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) { + abs := func(path string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(baseDir, path) + } + + scanner := bufio.NewScanner(in) + var line strings.Builder + var deps []dependency + for scanner.Scan() { + buf := scanner.Bytes() + if line.Len()+len(buf) > 1024*1024 { + return nil, errors.New("line too long") + } + + if bytes.HasSuffix(buf, []byte{'\\'}) { + line.Write(buf[:len(buf)-1]) + continue + } + + line.Write(buf) + if line.Len() == 0 { + // Skip empty lines + continue + } + + parts := strings.SplitN(line.String(), ":", 2) + if len(parts) < 2 { + return nil, fmt.Errorf("invalid line without ':'") + } + + // NB: This doesn't handle filenames with spaces in them. + // It seems like make doesn't do that either, so oh well. + var prereqs []string + for _, prereq := range strings.Fields(parts[1]) { + prereqs = append(prereqs, abs(prereq)) + } + + deps = append(deps, dependency{ + abs(string(parts[0])), + prereqs, + }) + line.Reset() + } + if err := scanner.Err(); err != nil { + return nil, err + } + + // There is always at least a dependency for the main file. + if len(deps) == 0 { + return nil, fmt.Errorf("empty dependency file") + } + return deps, nil +} + +// strip DWARF debug info from file by executing exe. +func strip(exe, file string) error { + cmd := exec.Command(exe, "-g", file) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("%s: %s", exe, err) + } + return nil +} diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/doc.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/doc.go new file mode 100644 index 00000000..138eb4e8 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/doc.go @@ -0,0 +1,4 @@ +// Program bpf2go embeds eBPF in Go. +// +// Please see the README for details how to use it. +package main diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/flags.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/flags.go new file mode 100644 index 00000000..ca8852a2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/flags.go @@ -0,0 +1,57 @@ +package main + +import ( + "flag" + "go/build/constraint" +) + +// buildTags is a comma-separated list of build tags. +// +// This follows the pre-Go 1.17 syntax and is kept for compatibility reasons. +type buildTags struct { + Expr constraint.Expr +} + +var _ flag.Value = (*buildTags)(nil) + +func (bt *buildTags) String() string { + if bt.Expr == nil { + return "" + } + + return (bt.Expr).String() +} + +func (bt *buildTags) Set(value string) error { + ct, err := constraint.Parse("// +build " + value) + if err != nil { + return err + } + + bt.Expr = ct + return nil +} + +func andConstraints(x, y constraint.Expr) constraint.Expr { + if x == nil { + return y + } + + if y == nil { + return x + } + + return &constraint.AndExpr{X: x, Y: y} +} + +func orConstraints(x, y constraint.Expr) constraint.Expr { + if x == nil { + return y + } + + if y == nil { + return x + } + + return &constraint.OrExpr{X: x, Y: y} +} diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go new file mode 100644 index 00000000..6064d44f --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/main.go @@ -0,0 +1,550 @@ +package main + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/build/constraint" + "go/token" + "io" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "slices" + "sort" + "strings" + + "github.com/cilium/ebpf" +) + +const helpText = `Usage: %[1]s [options] [-- ] + +ident is used as the stem of all generated Go types and functions, and +must be a valid Go identifier. + +source is a single C file that is compiled using the specified compiler +(usually some version of clang). + +You can pass options to the compiler by appending them after a '--' argument +or by supplying -cflags. Flags passed as arguments take precedence +over flags passed via -cflags. Additionally, the program expands quotation +marks in -cflags. This means that -cflags 'foo "bar baz"' is passed to the +compiler as two arguments "foo" and "bar baz". + +The program expects GOPACKAGE to be set in the environment, and should be invoked +via go generate. The generated files are written to the current directory. + +Some options take defaults from the environment. Variable name is mentioned +next to the respective option. + +Options: + +` + +// Targets understood by bpf2go. +// +// Targets without a Linux string can't be used directly and are only included +// for the generic bpf, bpfel, bpfeb targets. +// +// See https://go.dev/doc/install/source#environment for valid GOARCHes when +// GOOS=linux. +var targetByGoArch = map[goarch]target{ + "386": {"bpfel", "x86"}, + "amd64": {"bpfel", "x86"}, + "arm": {"bpfel", "arm"}, + "arm64": {"bpfel", "arm64"}, + "loong64": {"bpfel", "loongarch"}, + "mips": {"bpfeb", "mips"}, + "mipsle": {"bpfel", ""}, + "mips64": {"bpfeb", ""}, + "mips64le": {"bpfel", ""}, + "ppc64": {"bpfeb", "powerpc"}, + "ppc64le": {"bpfel", "powerpc"}, + "riscv64": {"bpfel", "riscv"}, + "s390x": {"bpfeb", "s390"}, +} + +func run(stdout io.Writer, args []string) (err error) { + b2g, err := newB2G(stdout, args) + switch { + case err == nil: + return b2g.convertAll() + case errors.Is(err, flag.ErrHelp): + return nil + default: + return err + } +} + +type bpf2go struct { + stdout io.Writer + // Absolute path to a .c file. + sourceFile string + // Absolute path to a directory where .go are written + outputDir string + // Alternative output stem. If empty, identStem is used. + outputStem string + // Valid go package name. + pkg string + // Valid go identifier. + identStem string + // Targets to build for. + targetArches map[target][]goarch + // C compiler. + cc string + // Command used to strip DWARF. + strip string + disableStripping bool + // C flags passed to the compiler. + cFlags []string + skipGlobalTypes bool + // C types to include in the generated output. + cTypes cTypes + // Build tags to be included in the output. + tags buildTags + // Base directory of the Makefile. Enables outputting make-style dependencies + // in .d files. + makeBase string +} + +func newB2G(stdout io.Writer, args []string) (*bpf2go, error) { + b2g := &bpf2go{ + stdout: stdout, + } + + fs := flag.NewFlagSet("bpf2go", flag.ContinueOnError) + fs.StringVar(&b2g.cc, "cc", getEnv("BPF2GO_CC", "clang"), + "`binary` used to compile C to BPF ($BPF2GO_CC)") + fs.StringVar(&b2g.strip, "strip", getEnv("BPF2GO_STRIP", ""), + "`binary` used to strip DWARF from compiled BPF ($BPF2GO_STRIP)") + fs.BoolVar(&b2g.disableStripping, "no-strip", false, "disable stripping of DWARF") + flagCFlags := fs.String("cflags", getEnv("BPF2GO_CFLAGS", ""), + "flags passed to the compiler, may contain quoted arguments ($BPF2GO_CFLAGS)") + fs.Var(&b2g.tags, "tags", "Comma-separated list of Go build tags to include in generated files") + flagTarget := fs.String("target", "bpfel,bpfeb", "clang target(s) to compile for (comma separated)") + fs.StringVar(&b2g.makeBase, "makebase", getEnv("BPF2GO_MAKEBASE", ""), + "write make compatible depinfo files relative to `directory` ($BPF2GO_MAKEBASE)") + fs.Var(&b2g.cTypes, "type", "`Name` of a type to generate a Go declaration for, may be repeated") + fs.BoolVar(&b2g.skipGlobalTypes, "no-global-types", false, "Skip generating types for map keys and values, etc.") + fs.StringVar(&b2g.outputStem, "output-stem", "", "alternative stem for names of generated files (defaults to ident)") + outDir := fs.String("output-dir", "", "target directory of generated files (defaults to current directory)") + outPkg := fs.String("go-package", "", "package for output go file (default as ENV GOPACKAGE)") + fs.SetOutput(b2g.stdout) + fs.Usage = func() { + fmt.Fprintf(fs.Output(), helpText, fs.Name()) + fs.PrintDefaults() + fmt.Fprintln(fs.Output()) + printTargets(fs.Output()) + } + if err := fs.Parse(args); err != nil { + return nil, err + } + + if *outDir == "" { + var err error + if *outDir, err = os.Getwd(); err != nil { + return nil, err + } + } + b2g.outputDir = *outDir + + if *outPkg == "" { + *outPkg = os.Getenv(gopackageEnv) + } + b2g.pkg = *outPkg + + if b2g.pkg == "" { + return nil, errors.New("missing package, you should either set the go-package flag or the GOPACKAGE env") + } + + if b2g.cc == "" { + return nil, errors.New("no compiler specified") + } + + args, cFlags := splitCFlagsFromArgs(fs.Args()) + + if *flagCFlags != "" { + splitCFlags, err := splitArguments(*flagCFlags) + if err != nil { + return nil, err + } + + // Command line arguments take precedence over C flags + // from the flag. + cFlags = append(splitCFlags, cFlags...) + } + + for _, cFlag := range cFlags { + if strings.HasPrefix(cFlag, "-M") { + return nil, fmt.Errorf("use -makebase instead of %q", cFlag) + } + } + + b2g.cFlags = cFlags[:len(cFlags):len(cFlags)] + + if len(args) < 2 { + return nil, errors.New("expected at least two arguments") + } + + b2g.identStem = args[0] + if !token.IsIdentifier(b2g.identStem) { + return nil, fmt.Errorf("%q is not a valid identifier", b2g.identStem) + } + + sourceFile, err := filepath.Abs(args[1]) + if err != nil { + return nil, err + } + b2g.sourceFile = sourceFile + + if b2g.makeBase != "" { + b2g.makeBase, err = filepath.Abs(b2g.makeBase) + if err != nil { + return nil, err + } + } + + if b2g.outputStem != "" && strings.ContainsRune(b2g.outputStem, filepath.Separator) { + return nil, fmt.Errorf("-output-stem %q must not contain path separation characters", b2g.outputStem) + } + + targetArches, err := collectTargets(strings.Split(*flagTarget, ",")) + if errors.Is(err, errInvalidTarget) { + printTargets(b2g.stdout) + fmt.Fprintln(b2g.stdout) + return nil, err + } + if err != nil { + return nil, err + } + + if len(targetArches) == 0 { + return nil, fmt.Errorf("no targets specified") + } + b2g.targetArches = targetArches + + // Try to find a suitable llvm-strip, possibly with a version suffix derived + // from the clang binary. + if b2g.strip == "" { + b2g.strip = "llvm-strip" + if strings.HasPrefix(b2g.cc, "clang") { + b2g.strip += strings.TrimPrefix(b2g.cc, "clang") + } + } + + return b2g, nil +} + +// cTypes collects the C type names a user wants to generate Go types for. +// +// Names are guaranteed to be unique, and only a subset of names is accepted so +// that we may extend the flag syntax in the future. +type cTypes []string + +var _ flag.Value = (*cTypes)(nil) + +func (ct *cTypes) String() string { + if ct == nil { + return "[]" + } + return fmt.Sprint(*ct) +} + +const validCTypeChars = `[a-z0-9_]` + +var reValidCType = regexp.MustCompile(`(?i)^` + validCTypeChars + `+$`) + +func (ct *cTypes) Set(value string) error { + if !reValidCType.MatchString(value) { + return fmt.Errorf("%q contains characters outside of %s", value, validCTypeChars) + } + + i := sort.SearchStrings(*ct, value) + if i >= len(*ct) { + *ct = append(*ct, value) + return nil + } + + if (*ct)[i] == value { + return fmt.Errorf("duplicate type %q", value) + } + + *ct = append((*ct)[:i], append([]string{value}, (*ct)[i:]...)...) + return nil +} + +func getEnv(key, defaultVal string) string { + if val, ok := os.LookupEnv(key); ok { + return val + } + return defaultVal +} + +func (b2g *bpf2go) convertAll() (err error) { + if _, err := os.Stat(b2g.sourceFile); os.IsNotExist(err) { + return fmt.Errorf("file %s doesn't exist", b2g.sourceFile) + } else if err != nil { + return err + } + + if !b2g.disableStripping { + b2g.strip, err = exec.LookPath(b2g.strip) + if err != nil { + return err + } + } + + for target, arches := range b2g.targetArches { + if err := b2g.convert(target, arches); err != nil { + return err + } + } + + return nil +} + +func (b2g *bpf2go) convert(tgt target, goarches []goarch) (err error) { + removeOnError := func(f *os.File) { + if err != nil { + os.Remove(f.Name()) + } + f.Close() + } + + outputStem := b2g.outputStem + if outputStem == "" { + outputStem = strings.ToLower(b2g.identStem) + } + + // The output filename must not match any of the following patterns: + // + // *_GOOS + // *_GOARCH + // *_GOOS_GOARCH + // + // Otherwise it is interpreted as a build constraint by the Go toolchain. + stem := fmt.Sprintf("%s_%s", outputStem, tgt.clang) + if tgt.linux != "" { + stem = fmt.Sprintf("%s_%s_%s", outputStem, tgt.linux, tgt.clang) + } + + absOutPath, err := filepath.Abs(b2g.outputDir) + if err != nil { + return err + } + + objFileName := filepath.Join(absOutPath, stem+".o") + + cwd, err := os.Getwd() + if err != nil { + return err + } + + var archConstraint constraint.Expr + for _, goarch := range goarches { + tag := &constraint.TagExpr{Tag: string(goarch)} + archConstraint = orConstraints(archConstraint, tag) + } + + constraints := andConstraints(archConstraint, b2g.tags.Expr) + + cFlags := make([]string, len(b2g.cFlags)) + copy(cFlags, b2g.cFlags) + if tgt.linux != "" { + cFlags = append(cFlags, "-D__TARGET_ARCH_"+tgt.linux) + } + + if err := b2g.removeOldOutputFiles(outputStem, tgt); err != nil { + return fmt.Errorf("remove obsolete output: %w", err) + } + + var dep bytes.Buffer + err = compile(compileArgs{ + cc: b2g.cc, + cFlags: cFlags, + target: tgt.clang, + dir: cwd, + source: b2g.sourceFile, + dest: objFileName, + dep: &dep, + }) + if err != nil { + return err + } + + fmt.Fprintln(b2g.stdout, "Compiled", objFileName) + + if !b2g.disableStripping { + if err := strip(b2g.strip, objFileName); err != nil { + return err + } + fmt.Fprintln(b2g.stdout, "Stripped", objFileName) + } + + spec, err := ebpf.LoadCollectionSpec(objFileName) + if err != nil { + return fmt.Errorf("can't load BPF from ELF: %s", err) + } + + maps, programs, types, err := collectFromSpec(spec, b2g.cTypes, b2g.skipGlobalTypes) + if err != nil { + return err + } + + // Write out generated go + goFileName := filepath.Join(absOutPath, stem+".go") + goFile, err := os.Create(goFileName) + if err != nil { + return err + } + defer removeOnError(goFile) + + err = output(outputArgs{ + pkg: b2g.pkg, + stem: b2g.identStem, + constraints: constraints, + maps: maps, + programs: programs, + types: types, + obj: filepath.Base(objFileName), + out: goFile, + }) + if err != nil { + return fmt.Errorf("can't write %s: %s", goFileName, err) + } + + fmt.Fprintln(b2g.stdout, "Wrote", goFileName) + + if b2g.makeBase == "" { + return + } + + deps, err := parseDependencies(cwd, &dep) + if err != nil { + return fmt.Errorf("can't read dependency information: %s", err) + } + + // There is always at least a dependency for the main file. + deps[0].file = goFileName + depFile, err := adjustDependencies(b2g.makeBase, deps) + if err != nil { + return fmt.Errorf("can't adjust dependency information: %s", err) + } + + depFileName := goFileName + ".d" + if err := os.WriteFile(depFileName, depFile, 0666); err != nil { + return fmt.Errorf("can't write dependency file: %s", err) + } + + fmt.Fprintln(b2g.stdout, "Wrote", depFileName) + return nil +} + +// removeOldOutputFiles removes output files generated by an old naming scheme. +// +// In the old scheme some linux targets were interpreted as build constraints +// by the go toolchain. +func (b2g *bpf2go) removeOldOutputFiles(outputStem string, tgt target) error { + if tgt.linux == "" { + return nil + } + + stem := fmt.Sprintf("%s_%s_%s", outputStem, tgt.clang, tgt.linux) + for _, ext := range []string{".o", ".go"} { + filename := filepath.Join(b2g.outputDir, stem+ext) + + if err := os.Remove(filename); errors.Is(err, os.ErrNotExist) { + continue + } else if err != nil { + return err + } + + fmt.Fprintln(b2g.stdout, "Removed obsolete", filename) + } + + return nil +} + +type target struct { + // Clang arch string, used to define the clang -target flag, as per + // "clang -print-targets". + clang string + // Linux arch string, used to define __TARGET_ARCH_xzy macros used by + // https://github.com/libbpf/libbpf/blob/master/src/bpf_tracing.h + linux string +} + +type goarch string + +func printTargets(w io.Writer) { + var arches []string + for goarch, archTarget := range targetByGoArch { + if archTarget.linux == "" { + continue + } + arches = append(arches, string(goarch)) + } + sort.Strings(arches) + + fmt.Fprint(w, "Supported targets:\n") + fmt.Fprint(w, "\tbpf\n\tbpfel\n\tbpfeb\n") + for _, arch := range arches { + fmt.Fprintf(w, "\t%s\n", arch) + } +} + +var errInvalidTarget = errors.New("unsupported target") + +func collectTargets(targets []string) (map[target][]goarch, error) { + result := make(map[target][]goarch) + for _, tgt := range targets { + switch tgt { + case "bpf", "bpfel", "bpfeb": + var goarches []goarch + for arch, archTarget := range targetByGoArch { + if archTarget.clang == tgt { + // Include tags for all goarches that have the same endianness. + goarches = append(goarches, arch) + } + } + slices.Sort(goarches) + result[target{tgt, ""}] = goarches + + case "native": + tgt = runtime.GOARCH + fallthrough + + default: + archTarget, ok := targetByGoArch[goarch(tgt)] + if !ok || archTarget.linux == "" { + return nil, fmt.Errorf("%q: %w", tgt, errInvalidTarget) + } + + var goarches []goarch + for goarch, lt := range targetByGoArch { + if lt == archTarget { + // Include tags for all goarches that have the same + // target. + goarches = append(goarches, goarch) + } + } + + slices.Sort(goarches) + result[archTarget] = goarches + } + } + + return result, nil +} + +const gopackageEnv = "GOPACKAGE" + +func main() { + if err := run(os.Stdout, os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(1) + } +} diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/output.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/output.go new file mode 100644 index 00000000..51f19707 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/output.go @@ -0,0 +1,244 @@ +package main + +import ( + "bytes" + _ "embed" + "fmt" + "go/build/constraint" + "go/token" + "io" + "sort" + "strings" + "text/template" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/btf" + "github.com/cilium/ebpf/internal" +) + +//go:embed output.tpl +var commonRaw string + +var commonTemplate = template.Must(template.New("common").Parse(commonRaw)) + +type templateName string + +func (n templateName) maybeExport(str string) string { + if token.IsExported(string(n)) { + return toUpperFirst(str) + } + + return str +} + +func (n templateName) Bytes() string { + return "_" + toUpperFirst(string(n)) + "Bytes" +} + +func (n templateName) Specs() string { + return string(n) + "Specs" +} + +func (n templateName) ProgramSpecs() string { + return string(n) + "ProgramSpecs" +} + +func (n templateName) MapSpecs() string { + return string(n) + "MapSpecs" +} + +func (n templateName) Load() string { + return n.maybeExport("load" + toUpperFirst(string(n))) +} + +func (n templateName) LoadObjects() string { + return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects") +} + +func (n templateName) Objects() string { + return string(n) + "Objects" +} + +func (n templateName) Maps() string { + return string(n) + "Maps" +} + +func (n templateName) Programs() string { + return string(n) + "Programs" +} + +func (n templateName) CloseHelper() string { + return "_" + toUpperFirst(string(n)) + "Close" +} + +type outputArgs struct { + // Package of the resulting file. + pkg string + // The prefix of all names declared at the top-level. + stem string + // Build constraints included in the resulting file. + constraints constraint.Expr + // Maps to be emitted. + maps []string + // Programs to be emitted. + programs []string + // Types to be emitted. + types []btf.Type + // Filename of the ELF object to embed. + obj string + out io.Writer +} + +func output(args outputArgs) error { + maps := make(map[string]string) + for _, name := range args.maps { + maps[name] = internal.Identifier(name) + } + + programs := make(map[string]string) + for _, name := range args.programs { + programs[name] = internal.Identifier(name) + } + + typeNames := make(map[btf.Type]string) + for _, typ := range args.types { + typeNames[typ] = args.stem + internal.Identifier(typ.TypeName()) + } + + // Ensure we don't have conflicting names and generate a sorted list of + // named types so that the output is stable. + types, err := sortTypes(typeNames) + if err != nil { + return err + } + + module := currentModule() + + gf := &btf.GoFormatter{ + Names: typeNames, + Identifier: internal.Identifier, + } + + ctx := struct { + *btf.GoFormatter + Module string + Package string + Constraints constraint.Expr + Name templateName + Maps map[string]string + Programs map[string]string + Types []btf.Type + TypeNames map[btf.Type]string + File string + }{ + gf, + module, + args.pkg, + args.constraints, + templateName(args.stem), + maps, + programs, + types, + typeNames, + args.obj, + } + + var buf bytes.Buffer + if err := commonTemplate.Execute(&buf, &ctx); err != nil { + return fmt.Errorf("can't generate types: %s", err) + } + + return internal.WriteFormatted(buf.Bytes(), args.out) +} + +func collectFromSpec(spec *ebpf.CollectionSpec, cTypes []string, skipGlobalTypes bool) (maps, programs []string, types []btf.Type, _ error) { + for name := range spec.Maps { + // Skip .rodata, .data, .bss, etc. sections + if !strings.HasPrefix(name, ".") { + maps = append(maps, name) + } + } + + for name := range spec.Programs { + programs = append(programs, name) + } + + types, err := collectCTypes(spec.Types, cTypes) + if err != nil { + return nil, nil, nil, fmt.Errorf("collect C types: %w", err) + } + + // Collect map key and value types, unless we've been asked not to. + if skipGlobalTypes { + return maps, programs, types, nil + } + + for _, typ := range collectMapTypes(spec.Maps) { + switch btf.UnderlyingType(typ).(type) { + case *btf.Datasec: + // Avoid emitting .rodata, .bss, etc. for now. We might want to + // name these types differently, etc. + continue + + case *btf.Int: + // Don't emit primitive types by default. + continue + } + + types = append(types, typ) + } + + return maps, programs, types, nil +} + +func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) { + var result []btf.Type + for _, cType := range names { + typ, err := types.AnyTypeByName(cType) + if err != nil { + return nil, err + } + result = append(result, typ) + } + return result, nil +} + +// collectMapTypes returns a list of all types used as map keys or values. +func collectMapTypes(maps map[string]*ebpf.MapSpec) []btf.Type { + var result []btf.Type + for _, m := range maps { + if m.Key != nil && m.Key.TypeName() != "" { + result = append(result, m.Key) + } + + if m.Value != nil && m.Value.TypeName() != "" { + result = append(result, m.Value) + } + } + return result +} + +// sortTypes returns a list of types sorted by their (generated) Go type name. +// +// Duplicate Go type names are rejected. +func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) { + var types []btf.Type + var names []string + for typ, name := range typeNames { + i := sort.SearchStrings(names, name) + if i >= len(names) { + types = append(types, typ) + names = append(names, name) + continue + } + + if names[i] == name { + return nil, fmt.Errorf("type name %q is used multiple times", name) + } + + types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...) + names = append(names[:i], append([]string{name}, names[i:]...)...) + } + + return types, nil +} diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/output.tpl b/vendor/github.com/cilium/ebpf/cmd/bpf2go/output.tpl new file mode 100644 index 00000000..8d804706 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/output.tpl @@ -0,0 +1,137 @@ +// Code generated by bpf2go; DO NOT EDIT. +{{ with .Constraints }}//go:build {{ . }}{{ end }} + +package {{ .Package }} + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "{{ .Module }}" +) + +{{- if .Types }} +{{- range $type := .Types }} +{{ $.TypeDeclaration (index $.TypeNames $type) $type }} + +{{ end }} +{{- end }} + +// {{ .Name.Load }} returns the embedded CollectionSpec for {{ .Name }}. +func {{ .Name.Load }}() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader({{ .Name.Bytes }}) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load {{ .Name }}: %w", err) + } + + return spec, err +} + +// {{ .Name.LoadObjects }} loads {{ .Name }} and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *{{ .Name.Objects }} +// *{{ .Name.Programs }} +// *{{ .Name.Maps }} +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (error) { + spec, err := {{ .Name.Load }}() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// {{ .Name.Specs }} contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type {{ .Name.Specs }} struct { + {{ .Name.ProgramSpecs }} + {{ .Name.MapSpecs }} +} + +// {{ .Name.Specs }} contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type {{ .Name.ProgramSpecs }} struct { +{{- range $name, $id := .Programs }} + {{ $id }} *ebpf.ProgramSpec `ebpf:"{{ $name }}"` +{{- end }} +} + +// {{ .Name.MapSpecs }} contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type {{ .Name.MapSpecs }} struct { +{{- range $name, $id := .Maps }} + {{ $id }} *ebpf.MapSpec `ebpf:"{{ $name }}"` +{{- end }} +} + +// {{ .Name.Objects }} contains all objects after they have been loaded into the kernel. +// +// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. +type {{ .Name.Objects }} struct { + {{ .Name.Programs }} + {{ .Name.Maps }} +} + +func (o *{{ .Name.Objects }}) Close() error { + return {{ .Name.CloseHelper }}( + &o.{{ .Name.Programs }}, + &o.{{ .Name.Maps }}, + ) +} + +// {{ .Name.Maps }} contains all maps after they have been loaded into the kernel. +// +// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. +type {{ .Name.Maps }} struct { +{{- range $name, $id := .Maps }} + {{ $id }} *ebpf.Map `ebpf:"{{ $name }}"` +{{- end }} +} + +func (m *{{ .Name.Maps }}) Close() error { + return {{ .Name.CloseHelper }}( +{{- range $id := .Maps }} + m.{{ $id }}, +{{- end }} + ) +} + +// {{ .Name.Programs }} contains all programs after they have been loaded into the kernel. +// +// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. +type {{ .Name.Programs }} struct { +{{- range $name, $id := .Programs }} + {{ $id }} *ebpf.Program `ebpf:"{{ $name }}"` +{{- end }} +} + +func (p *{{ .Name.Programs }}) Close() error { + return {{ .Name.CloseHelper }}( +{{- range $id := .Programs }} + p.{{ $id }}, +{{- end }} + ) +} + +func {{ .Name.CloseHelper }}(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +//go:embed {{ .File }} +var {{ .Name.Bytes }} []byte diff --git a/vendor/github.com/cilium/ebpf/cmd/bpf2go/tools.go b/vendor/github.com/cilium/ebpf/cmd/bpf2go/tools.go new file mode 100644 index 00000000..d2e020b4 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/cmd/bpf2go/tools.go @@ -0,0 +1,94 @@ +package main + +import ( + "errors" + "fmt" + "runtime/debug" + "strings" + "unicode" + "unicode/utf8" +) + +func splitCFlagsFromArgs(in []string) (args, cflags []string) { + for i, arg := range in { + if arg == "--" { + return in[:i], in[i+1:] + } + } + + return in, nil +} + +func splitArguments(in string) ([]string, error) { + var ( + result []string + builder strings.Builder + escaped bool + delim = ' ' + ) + + for _, r := range strings.TrimSpace(in) { + if escaped { + builder.WriteRune(r) + escaped = false + continue + } + + switch r { + case '\\': + escaped = true + + case delim: + current := builder.String() + builder.Reset() + + if current != "" || delim != ' ' { + // Only append empty words if they are not + // delimited by spaces + result = append(result, current) + } + delim = ' ' + + case '"', '\'', ' ': + if delim == ' ' { + delim = r + continue + } + + fallthrough + + default: + builder.WriteRune(r) + } + } + + if delim != ' ' { + return nil, fmt.Errorf("missing `%c`", delim) + } + + if escaped { + return nil, errors.New("unfinished escape") + } + + // Add the last word + if builder.Len() > 0 { + result = append(result, builder.String()) + } + + return result, nil +} + +func toUpperFirst(str string) string { + first, n := utf8.DecodeRuneInString(str) + return string(unicode.ToUpper(first)) + str[n:] +} + +func currentModule() string { + bi, ok := debug.ReadBuildInfo() + if !ok { + // Fall back to constant since bazel doesn't support BuildInfo. + return "github.com/cilium/ebpf" + } + + return bi.Main.Path +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5424690d..c12eb481 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -3,6 +3,7 @@ github.com/cilium/ebpf github.com/cilium/ebpf/asm github.com/cilium/ebpf/btf +github.com/cilium/ebpf/cmd/bpf2go github.com/cilium/ebpf/internal github.com/cilium/ebpf/internal/epoll github.com/cilium/ebpf/internal/kallsyms