user2233706
user2233706

Reputation: 7225

Header file not found in XDP source compiled in Go program

My XDP module compiles by itself, but compilation within the context of a Go program fails because it can't find bpf/bpf_helpers.h file. Here's the code leading up the problem:

package main

import (
    "fmt"
    bpf "github.com/iovisor/gobpf/bcc"
    log "github.com/sirupsen/logrus"
    "io/ioutil"
    "os"
)

/*
#cgo CFLAGS: -I/usr/include/bcc/compat
#cgo LDFLAGS: -lbcc
#include <bcc/bcc_common.h>
#include <bcc/libbpf.h>
void perf_reader_free(void *ptr);
*/
import "C"

func main() {
    // Get the source code from disk
    source, err := ioutil.ReadFile("xdp/collect_ips.c")

    if err != nil {
        log.Fatalln("Cannot read collect_ips.c")
    }

    // Compile module
    module := bpf.NewModule(string(source), []string{
        "-Wall",
        "-O2",
    })
    defer module.Close()

    // Load module
    fn, err := module.Load("collect_ips", C.BPF_PROG_TYPE_XDP, 1, 65536) // Problem happens here

    // ...

The Go program compiles fine, but I when I run the program, I get this:

/virtual/main.c:2:10: fatal error: 'bpf/bpf_helpers.h' file not found
#include <bpf/bpf_helpers.h>
         ^~~~~~~~~~~~~~~~~~~
9 warnings and 1 error generated.
panic: runtime error: invalid memory address or nil pointer dereference
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4b23d4]

goroutine 1 [running]:
github.com/iovisor/gobpf/bcc.(*Module).Close.func1(0x203000)
        /home/me/go/pkg/mod/github.com/iovisor/[email protected]/bcc/module.go:132 +0x14
github.com/iovisor/gobpf/bcc.(*Module).Close(0x0)
        /home/me/go/pkg/mod/github.com/iovisor/[email protected]/bcc/module.go:132 +0x36
panic({0x4da720, 0x59ec60})
        /usr/lib/go-1.17/src/runtime/panic.go:1038 +0x215
github.com/iovisor/gobpf/bcc.(*Module).Load(0x0, {0x4ef9f2, 0xb}, 0x2, 0x2, 0x8001)
        /home/me/go/pkg/mod/github.com/iovisor/[email protected]/bcc/module.go:202 +0x36
main.main()
        /home/me/go/src/code.squarespace.net/net/prism-stream/cmd/server/main.go:35 +0x1b7

This problem is happening because of my XDP module because if I comment out this header file in the C source, the error moves to a different header file.

I think this is happening because of bpf_helpers.h does not exist here https://github.com/iovisor/gobpf/tree/master/elf/include. If this is the issue, is there a way to use the header file from /usr/include/bpf?

If I take out bpf_helpers.h from the XDP code, I get an error complaining about the use of SEC in my code:

struct bpf_map_def SEC("maps") addr_map = {
    .type = BPF_MAP_TYPE_LRU_HASH,
    .key_size = sizeof(struct addr_desc_struct),
    .value_size = sizeof(long),
    .max_entries = 4096
};

I copied the macro for SEC from bpf_helpers.h to my code, but I then get error: variable has incomplete type 'struct bpf_map_def'. I also use bpf_map_lookup_elem() and bpf_map_update_elem(), which are defined in the bpf/ directory.

Upvotes: 0

Views: 1006

Answers (1)

user2233706
user2233706

Reputation: 7225

I used the eBPF library from Cilium. Here's an example program that loads .o file to an interface, waits for 10 seconds, and prints whatever is in the BPF map.

You have to make sure your XDP function has SEC("xdp") before it. Whereas if you load the program using xdp-loader, you can pass whatever you want to SEC as long as it's different from the name of the function.

package main

import (
    "bytes"
    "fmt"
    "github.com/cilium/ebpf"
    log "github.com/sirupsen/logrus"
    "github.com/vishvananda/netlink"
    "time"
)


func main() {
    var objects struct {
        XCProg  *ebpf.Program `ebpf:"collect_ips"` // Name of function containing the XDP program
        XCMap   *ebpf.Map     `ebpf:"addr_map"` // Name of the map 
    }

    spec, err := ebpf.LoadCollectionSpec("dist/collect_ips.o")

    if err != nil {
        log.Fatalln("ebpf.LoadCollectionSpec", err)
    }

    if err := spec.LoadAndAssign(&objects, nil); err != nil {
        log.Fatalln("ebpf.LoadAndAssign", err)
    }

    link, err := netlink.LinkByName("enp0s8")

    if err != nil {
        log.Fatalln("netlink.LinkByName", err)
    }

    err = netlink.LinkSetXdpFdWithFlags(link, objects.XCProg.FD(), 2)

    if err != nil {
        log.Fatalln("netlink.LinkSetXdpFdWithFlags")
    }

    time.Sleep(time.Second*10)

    var keyBytes, valueBytes []byte

    iterator := objects.XCMap.Iterate()

    if iterator == nil {
        log.Fatalln("nil iterator")
    }

    for iterator.Next(&keyBytes, &valueBytes) != false {
        fmt.Printf("%x: %x", keyBytes, valueBytes)
    }

    defer objects.XCProg.Close()
    defer objects.XCMap.Close()
}

Upvotes: 1

Related Questions