Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attaching EBPF program returns no such file or directory #32

Closed
Manxiaxia opened this issue Aug 13, 2021 · 5 comments
Closed

Attaching EBPF program returns no such file or directory #32

Manxiaxia opened this issue Aug 13, 2021 · 5 comments

Comments

@Manxiaxia
Copy link

Manxiaxia commented Aug 13, 2021

Hi Florian,

I am currently working on a project which requires to load ebpf program to the tc ingress hook. I followed the example you provided a while ago: https://gist.github.com/florianl/8f421e57f419fa9a50eb5b085363de66. There are some changes on the go-tc package but I tried to update the sample code to adapt the newest version.

The error I got is: could not assign eBPF: netlink receive: no such file or directory.

Brief introduction on what I did:

bpf program trying to load:

/*
#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"

const source string = `
#define KBUILD_MODNAME "tc_eBPF"
#include <uapi/linux/bpf.h>


int tc_eBPF(struct __sk_buff *skb) {
	bpf_trace_printk("hello world\n");
	return 0;
}
`

then using the gobpf library to load bpf program

module := bpf.NewModule(source, []string{"-w"})
defer module.Close()

fn, err := module.Load("tc_eBPF", C.BPF_PROG_TYPE_SCHED_CLS, 1, 65536)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load ebpf prog: %v\n", err)
return
}

after that, open the rtnl and find the deviceID

rtnl, err := tc.Open(&tc.Config{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not open rtnetlink socket: %v\n", err)
		return
	}
	defer func() {
		if err := rtnl.Close(); err != nil {
			fmt.Fprintf(os.Stderr, "could not close rtnetlink socket: %v\n", err)
		}
	}()

	devID, err := net.InterfaceByName("wlo1")
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not get interface ID: %v\n", err)
		return
	}

once that part is covered, I added clsact qdisc for adding ebpf filter

	qdisc := tc.Object{
		tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(devID.Index),
			Handle:  core.BuildHandle(tc.HandleIngress, 0x0000),
			Parent:  tc.HandleIngress,
			Info:    0,
		},
		tc.Attribute{
			Kind: "clsact",
		},
	}

	//add clsact qdisc to tc for attaching ebpf
	if err := rtnl.Qdisc().Add(&qdisc); err != nil {
		fmt.Fprintf(os.Stderr, "could not assign clsact to lo: %v\n", err)
		return
	}
	defer deleteAddedQdisc(rtnl, qdisc)

the delete method provided in sample code seems not working, so I modified it a little bit for testing purpose:

func deleteAddedQdisc(rtnl *tc.Tc, addedQdisc tc.Object) {
	qdiscs, err := rtnl.Qdisc().Get()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	for _, qdisc := range qdiscs {
		if qdisc.Kind == addedQdisc.Kind && qdisc.Ifindex == addedQdisc.Ifindex {
			err := rtnl.Qdisc().Delete(&qdisc)
			if err != nil {
				fmt.Printf(err.Error())
				return
			}
		}
	}
}

at the end, I created a tc bpf filter

	bpfFn := uint32(fn)
	bpfFlag := uint32(0x1)
	//
	filter := tc.Object{
		Msg: tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(devID.Index),
			Handle:  core.BuildHandle(tc.HandleIngress, 0x0000),
			Parent:  tc.HandleIngress,
			Info:    0x300,
		},
		Attribute: tc.Attribute{
			Kind: "bpf",
			BPF: &tc.Bpf{
				FD:    &bpfFn,
				Flags: &bpfFlag,
			},

		},
	}

	if err := rtnl.Filter().Add(&filter); err != nil {
		fmt.Fprintf(os.Stderr, "could not assign eBPF: %v\n", err)
		return
	}

That's pretty much it, I am also gonna to include a full program at the end. After run the program, I got could not assign ebpf: netlink receive: no such file or directory error. My assumption is that when adding the bpf filter, it is trying to find the ebpf file but cannot find it. I am wondering if I did something wrong? Thank you.

package main

import (
	"fmt"
	"github.com/florianl/go-tc"
	"github.com/florianl/go-tc/core"
	bpf "github.com/iovisor/gobpf/bcc"
	"golang.org/x/sys/unix"
	"net"
	"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"

const source string = `
#define KBUILD_MODNAME "tc_eBPF"
#include <uapi/linux/bpf.h>


int tc_eBPF(struct __sk_buff *skb) {
	bpf_trace_printk("hello world\n");
	return 0;
}
`

func main() {
	module := bpf.NewModule(source, []string{"-w"})
	defer module.Close()

	fn, err := module.Load("tc_eBPF", C.BPF_PROG_TYPE_SCHED_CLS, 1, 65536)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to load ebpf prog: %v\n", err)
		return
	}

	rtnl, err := tc.Open(&tc.Config{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not open rtnetlink socket: %v\n", err)
		return
	}
	defer func() {
		if err := rtnl.Close(); err != nil {
			fmt.Fprintf(os.Stderr, "could not close rtnetlink socket: %v\n", err)
		}
	}()

	devID, err := net.InterfaceByName("wlo1")
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not get interface ID: %v\n", err)
		return
	}

	qdisc := tc.Object{
		tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(devID.Index),
			Handle:  core.BuildHandle(tc.HandleIngress, 0x0000),
			Parent:  tc.HandleIngress,
			Info:    0,
		},
		tc.Attribute{
			Kind: "clsact",
		},
	}

	//add clsact qdisc to tc for attaching ebpf
	if err := rtnl.Qdisc().Add(&qdisc); err != nil {
		fmt.Fprintf(os.Stderr, "could not assign clsact to lo: %v\n", err)
		return
	}
	defer deleteAddedQdisc(rtnl, qdisc)

	//programName := "tc_eBPF"
	bpfFn := uint32(fn)
	bpfFlag := uint32(0x1)
	//
	filter := tc.Object{
		Msg: tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(devID.Index),
			Handle:  core.BuildHandle(tc.HandleIngress, 0x0000),
			Parent:  tc.HandleIngress,
			Info:    0x300,
		},
		Attribute: tc.Attribute{
			Kind: "bpf",
			BPF: &tc.Bpf{
				FD:    &bpfFn,
				Flags: &bpfFlag,
			},

		},
	}

	if err := rtnl.Filter().Add(&filter); err != nil {
		fmt.Fprintf(os.Stderr, "could not assign eBPF: %v\n", err)
		return
	}
	//time.Sleep(30*time.Hour)

	// cat sys/kernel/debug/tracing/trace_pipe
}

func deleteAddedQdisc(rtnl *tc.Tc, addedQdisc tc.Object) {
	qdiscs, err := rtnl.Qdisc().Get()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	for _, qdisc := range qdiscs {
		if qdisc.Kind == addedQdisc.Kind && qdisc.Ifindex == addedQdisc.Ifindex {
			err := rtnl.Qdisc().Delete(&qdisc)
			if err != nil {
				fmt.Printf(err.Error())
				return
			}
		}
	}
}
@florianl
Copy link
Owner

Thanks for your report 👍

The feedback from the netlink subsystem of the kernel is limited. Reading your code I noticed some differences to the gist example.

  • filter.Attribute.BPF.Name is not set
  • filter.Msg.Handle should not be the same as qdisc.Msg.Handle

Wrt deleting the filer - there is already issue #17 . But I didn't find time taking a closer look so far.

@Manxiaxia
Copy link
Author

Hi Florian,

Thank you for your response. I am actually a bit confused on the filter.Attribute.BPF.Name filed. The name used on the gist example is tc_prog but the name of the ebpf program is tc_eBPF. I am wondering what does the Name file represnets?

I actually tried to run the program with the filter.Attribute.BPF.Name field set to name of the ebpf program, but still got the same error. Also, the filter.Msg.Handle is changed to 0.

@florianl
Copy link
Owner

I just remember that filter.Attribute.BPF.Name can be set to something arbitrary.

The following settings I applied to your example and it works now:

	qdisc := tc.Object{
		tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: devIfaceIndex,
			Handle:  core.BuildHandle(0xFFFF, 0x0000),
			Parent:  tc.HandleIngress,
		},
		tc.Attribute{
			Kind: "clsact",
		},
	}
[...]
	filter := tc.Object{
		Msg: tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: devIfaceIndex,
			Handle:  0,
			Parent:  0xFFFFFFF2,
			Info:    0x10300,
		},
		Attribute: tc.Attribute{
			Kind: "bpf",
			BPF: &tc.Bpf{
				FD:    &bpfFn,
				Flags: &bpfFlag,
			},
		},
	}

@Manxiaxia
Copy link
Author

Hi Florian,

I noticed that the changes you made are filter.Msg.Parent and filter.Msg.Info.

Based on my understanding, since the parent of the qdisc is tc.HandleIngress which is 0xFFFFFFF1, therefore, the ID of the clsact qdisc we created will be 0xFFFFFFF2. Since we want to assign the filter to clsact qdisc, the parent of the filter will then be 0xFFFFFFF2. Is that right?

In terms of filter.Msg.Info filed, I am wondering is that field mandatory? I am not quite sure understand the meaning of that field. I tried to look through the tc man page but I got no luck.

Thank you so much for your help, I really appreciate it.

@florianl
Copy link
Owner

Besides the man page tc(8) there is more documentation at https://www.infradead.org/~tgr/libnl/doc/route.html#route_tc.

Therefore I'm closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants