GithubHelp home page GithubHelp logo

dropbox / goebpf Goto Github PK

View Code? Open in Web Editor NEW
1.1K 33.0 85.0 1.76 MB

Library to work with eBPF programs from Go

License: Other

C 20.40% Go 78.99% Makefile 0.62%
ebpf golang xdp bpf perfevents go golang-library xdpdump cats cats-effect

goebpf's Introduction

Go eBPF

Build Status Go Report Card Documentation

A nice and convenient way to work with eBPF programs / perf events from Go.

Requirements

  • Go 1.11+
  • Linux Kernel 4.15+

Supported eBPF features

  • eBPF programs
    • SocketFilter
    • XDP
    • Kprobe / Kretprobe
    • tc-cls (tc-act is partially implemented, currently)
  • Perf Events

Support for other program types / features can be added in future. Meanwhile your contributions are warmly welcomed.. :)

Installation

# Main library
go get github.com/dropbox/goebpf

# Mock version (if needed)
go get github.com/dropbox/goebpf/goebpf_mock

Quick start

Consider very simple example of Read / Load / Attach

    // In order to be simple this examples does not handle errors
    bpf := goebpf.NewDefaultEbpfSystem()
    // Read clang compiled binary
    bpf.LoadElf("test.elf")
    // Load XDP program into kernel (name matches function name in C)
    xdp := bpf.GetProgramByName("xdp_test")
    xdp.Load()
    // Attach to interface
    xdp.Attach("eth0")
    defer xdp.Detach()
    // Work with maps
    test := bpf.GetMapByName("test")
    value, _ := test.LookupInt(0)
    fmt.Printf("Value at index 0 of map 'test': %d\n", value)

Like it? Check our examples

Perf Events

Currently library has support for one, most popular use case of perf_events: where eBPF map key maps to cpu_id. So eBPF and go parts actually bind cpu_id to map index. It maybe as simple as:

    // Define special, perf_events map where key maps to CPU_ID
    BPF_MAP_DEF(perfmap) = {
        .map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
        .max_entries = 128,     // Max supported CPUs
    };
    BPF_MAP_ADD(perfmap);

    // ...

    // Emit perf event with "data" to map "perfmap" where index is current CPU_ID
    bpf_perf_event_output(ctx, &perfmap, BPF_F_CURRENT_CPU, &data, sizeof(data));

And the go part:

    perf, err := goebpf.NewPerfEvents("perfmap")
    // 4096 is ring buffer size
    perfEvents, err := perf.StartForAllProcessesAndCPUs(4096)
    defer perf.Stop()

    for {
        select {
            case data := <-perfEvents:
                fmt.Println(data)
        }
    }

Looks simple? Check our full XDP dump example

Kprobes

Library currently has support for kprobes and kretprobes. It can be as simple as:

    // kprobe handler function
    SEC("kprobe/guess_execve")
    int execve_entry(struct pt_regs *ctx) {
      // ...
      buf_perf_output(ctx);
      return 0;
    }

And the go part:

	// Cleanup old probes
	err := goebpf.CleanupProbes()

	// Attach all probe programs
	for _, prog := range bpf.GetPrograms() {
		err := prog.Attach(nil)
	}

	// Create perf events
	eventsMap := p.bpf.GetMapByName("events")
	p.pe, err = goebpf.NewPerfEvents(eventsMap)
	events, err := p.pe.StartForAllProcessesAndCPUs(4096)
	defer events.Stop()

	for {
		select {
		case data := <-events:
			fmt.Println(data) // kProbe event
		}
	}

Simple? Check exec dump example

Good readings

goebpf's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

goebpf's Issues

creating elf file

I am trying to create my own elf file using examples suggested in the examples. .c file as below

#include "bpf_helpers.h"

struct iphdr {
  __u8 ihl : 4;
  __u8 version : 4;
  __u8 tos;
  __u16 tot_len;
  __u16 id;
  __u16 frag_off;
  __u8 ttl;
  __u8 protocol;
  __u16 check;
  __u32 saddr;
  __u32 daddr;
};

struct Key {
  __u32 src_ip;               //source ip
  __u32 dst_ip;               //destination ip
  __u16 src_port;  //source port
  __u16 dst_port;  //destination port
};

struct Leaf {
  __u64 timestamp;            //timestamp in ns
};

// eBPF map to store IP proto counters (tcp, udp, etc)
BPF_MAP_DEF(sessions) = {
    .map_type = BPF_MAP_TYPE_HASH,
    .key_size = 96,
    .value_size = 64,
    .max_entries = 255,
};
BPF_MAP_ADD(sessions);

SEC("socket_filter")
int ip_filter(struct __sk_buff *skb) {
  struct Key key = {};
  struct Leaf zero = {0};
  
struct iphdr *ip = (struct iphdr *)skb_network_header(skb);
  
  1. Getting following warning. Basically how to get iphdr from sk_buff ?
clang -I../../.. -O2 -target bpf -c ebpf_prog/xdp.c  -o ebpf_prog/xdp.elf -static
ebpf_prog/xdp.c:59:38: warning: implicit declaration of function 'skb_network_header' is invalid in C99
      [-Wimplicit-function-declaration]
  struct iphdr *ip = (struct iphdr *)skb_network_header(skb);
                                     ^
ebpf_prog/xdp.c:59:22: warning: cast to 'struct iphdr *' from smaller integer type 'int' [-Wint-to-pointer-cast]
  struct iphdr *ip = (struct iphdr *)skb_network_header(skb);

  1. When i run the program using go, get following issue ?
    LoadElf() failed: loadPrograms() failed: Invalid BPF instruction (at 0): &{133 0 1 0 4294967295}

Attach the same program to multiple interfaces

Hi,

Does xdpProgram support attaching the same program to multiple interfaces? As far as I can tell from the code, a xdpProgram has one ifname and only detach the the program from this interface. If multiple Attach() are called with different interfaces, only the last one will be detached in the Detach() call. Is this intended? Is there any way to attach the same program to different interfaces?

Thanks!

ebpf_create_map() failed: Operation not permitted

Hi, I can not run any examples due to this error (I'm using Fedora 33):

$ uname -r
5.10.19-200.fc33.x86_64

$ pwd
goebpf/examples/xdp/basic_firewall

$ sudo ulimit
unlimited

$ clang --version
clang version 11.0.0 (Fedora 11.0.0-2.fc33)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

$ sudo ./main -iface enp7s0u2u1u2 -drop 8.8.8.8
LoadElf() failed: loadAndCreateMaps() failed: map.Create() failed: ebpf_create_map() failed: Operation not permitted

My Go program cannot exit, it traps in `p.updateChannel <- p.items[int(p.fds[i])]`

I want my Go program to catch a limited number of perf events, and then it should ends itself.

When the limited number counted, the function stopPerfEvents will be called.

func (p *bpfProgram) stopPerfEvents() {
	p.pe.Stop()
	close(p.stop)
	p.wg.Wait()
}

func (p *bpfProgram) startPerfEvents(events <-chan []byte) {
	p.wg.Add(1)
	go func(events <-chan []byte) {
		defer p.wg.Done()

		for {
			var data []byte
			select {
			case <-p.stop:
				return
			case b, ok := <-events:
				if !ok {
					return
				}
				data = b
			}

			var ev bpfEvent
			err := ev.unmarshal(data)
			if err != nil {
				eventAbnormal++
				continue
			}

			e := ev.toEvent()
			p.getCallStack(ev.KernelStackID, e)

			select {
			case p.ev <- e:
			case <-p.stop:
				return
			}
		}
	}(events)
}

Unexpectedly, it cannot exits. And then I use gops to print out its stack:

goroutine 19 [running]:
runtime/pprof.writeGoroutineStacks(0x6d00a8, 0xc0000a6008, 0xd0, 0x658380)
	/usr/local/go/src/runtime/pprof/pprof.go:693 +0x9f
runtime/pprof.writeGoroutine(0x6d00a8, 0xc0000a6008, 0x2, 0xc000038800, 0x0)
	/usr/local/go/src/runtime/pprof/pprof.go:682 +0x45
runtime/pprof.(*Profile).WriteTo(0x7f3960, 0x6d00a8, 0xc0000a6008, 0x2, 0xc0000a6008, 0x8)
	/usr/local/go/src/runtime/pprof/pprof.go:331 +0x3f2
github.com/google/gops/agent.handle(0x7f7d1ade79c0, 0xc0000a6008, 0xc0000160b8, 0x1, 0x1, 0x0, 0x0)
	/root/go/pkg/mod/github.com/google/[email protected]/agent/agent.go:201 +0x1b8
github.com/google/gops/agent.listen()
	/root/go/pkg/mod/github.com/google/[email protected]/agent/agent.go:145 +0x2f0
created by github.com/google/gops/agent.Listen
	/root/go/pkg/mod/github.com/google/[email protected]/agent/agent.go:123 +0x3ad

goroutine 1 [semacquire, 2 minutes]:
sync.runtime_Semacquire(0xc0000fecf8)
	/usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc0000fecf0)
	/usr/local/go/src/sync/waitgroup.go:130 +0x65
github.com/dropbox/goebpf.(*PerfEvents).Stop(0xc0000fecb0)
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events.go:138 +0x45
github.com/Asphaltt/skbtracer.(*bpfProgram).stopPerfEvents(0xc00005e000)
	/data/skbtracer/bpf.go:189 +0x2f
github.com/Asphaltt/skbtracer.(*bpfProgram).detachProbes(0xc00005e000)
	/data/skbtracer/bpf.go:231 +0x45
main.runBpf()
	/data/skbtracer/cmd/skbtracer/main.go:69 +0x206
main.glob..func1(0x7f8960, 0xc0000ec040, 0x0, 0x2)
	/data/skbtracer/cmd/skbtracer/main.go:38 +0x14d
github.com/spf13/cobra.(*Command).execute(0x7f8960, 0xc00009a160, 0x2, 0x2, 0x7f8960, 0xc00009a160)
	/root/go/pkg/mod/github.com/spf13/[email protected]/command.go:860 +0x2c2
github.com/spf13/cobra.(*Command).ExecuteC(0x7f8960, 0x0, 0x7ffc60, 0xc00008e058)
	/root/go/pkg/mod/github.com/spf13/[email protected]/command.go:974 +0x375
github.com/spf13/cobra.(*Command).Execute(...)
	/root/go/pkg/mod/github.com/spf13/[email protected]/command.go:902
main.main()
	/data/skbtracer/cmd/skbtracer/main.go:43 +0x2d

goroutine 6 [semacquire, 2 minutes]:
sync.runtime_Semacquire(0xc0000e4060)
	/usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc0000e4058)
	/usr/local/go/src/sync/waitgroup.go:130 +0x65
github.com/dropbox/goebpf.(*perfEventPoller).Stop(...)
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events_poller.go:98
github.com/dropbox/goebpf.(*PerfEvents).loop.func1(0xc0000e4050, 0xc0000fecb0)
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events.go:165 +0x45
github.com/dropbox/goebpf.(*PerfEvents).loop(0xc0000fecb0)
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events.go:176 +0x24f
created by github.com/dropbox/goebpf.(*PerfEvents).startLoop
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events.go:152 +0xbf

goroutine 7 [select, 2 minutes]:
github.com/Asphaltt/skbtracer.(*bpfProgram).startPerfEvents.func1(0xc00005e000, 0xc00008e3c0)
	/data/skbtracer/bpf.go:145 +0xf4
created by github.com/Asphaltt/skbtracer.(*bpfProgram).startPerfEvents
	/data/skbtracer/bpf.go:140 +0x66

goroutine 8 [chan receive, 2 minutes]:
main.handleEvent(0xc00008e0c0, 0xc0000b8d20)
	/data/skbtracer/cmd/skbtracer/main.go:99 +0x285
created by main.runBpf
	/data/skbtracer/cmd/skbtracer/main.go:63 +0x18f

goroutine 22 [chan send, 2 minutes]:
github.com/dropbox/goebpf.(*perfEventPoller).loop(0xc0000e4050)
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events_poller.go:119 +0x9c
created by github.com/dropbox/goebpf.(*perfEventPoller).Start
	/root/go/pkg/mod/github.com/dropbox/[email protected]/perf_events_poller.go:90 +0x1e5

goroutine 10 [syscall, 2 minutes]:
os/signal.signal_recv(0x0)
	/usr/local/go/src/runtime/sigqueue.go:168 +0xa5
os/signal.loop()
	/usr/local/go/src/os/signal/signal_unix.go:23 +0x25
created by os/signal.Notify.func1.1
	/usr/local/go/src/os/signal/signal.go:151 +0x45

Analyze it, and the line github.com/dropbox/[email protected]/perf_events_poller.go:119 catches my eyes.

func (p *perfEventPoller) loop() {
	defer p.wg.Done()

	for {
		......

		for i := 0; i < readyCnt; i++ {
			p.updateChannel <- p.items[int(p.fds[i])]       // it's this line
		}
	}
}

At last, I got my solution:

// Stop stops event polling loop
func (pe *PerfEvents) Stop() {
	// Stop poller firstly
	pe.poller.Stop()
	// Stop poll loop
	close(pe.stopChannel)
	// Wait until poll loop stopped, then close updates channel
	pe.wg.Wait()
	close(pe.updatesChannel)

	......
}

func (pe *PerfEvents) loop() {
	// Setup poller to poll all handlers (one handler per CPU)
	pe.poller = newPerfEventPoller()
	for _, handler := range pe.handlers {
		pe.poller.Add(handler)
	}

	// Start poller
	pollerCh := pe.poller.Start(pe.PollTimeoutMs)
	defer func() {
		// pe.poller.Stop()
		pe.wg.Done()
	}()

        ......
}

bpf_trace_printk result in LoadElf() failed: loadPrograms() failed: map '' doesn't exist

Hi,

when I add bpf_trace_printk in example below, it compiles fine, when run the program, it result in error below:

[root@centos-dev basic_firewall]# ./main -drop 10.169.72.0/24 -iface ens192
LoadElf() failed: loadPrograms() failed: map '' doesn't exist

the diff

diff --git a/examples/xdp/basic_firewall/ebpf_prog/xdp_fw.c b/examples/xdp/basic_firewall/ebpf_prog/xdp_fw.c
index 6d399fb..b9aa66c 100644
--- a/examples/xdp/basic_firewall/ebpf_prog/xdp_fw.c
+++ b/examples/xdp/basic_firewall/ebpf_prog/xdp_fw.c
@@ -80,6 +80,8 @@ int firewall(struct xdp_md *ctx) {
   key.prefixlen = 32;
   key.saddr = ip->saddr;
 
+  bpf_trace_printk("Matched with protocol %d and sAddr %lu.\n", ip->protocol, ip->saddr);
+
   // Lookup SRC IP in blacklisted IPs
   __u64 *rule_idx = bpf_map_lookup_elem(&blacklist, &key);
   if (rule_idx) {

Perf events was supported?

Hi,

The library is awesome! Thanks for great work.

But I wonder whether it already supported BPF_PERF_EVENT_OUTPUT() helper?

Incorrect padding causing errors in retrieving Ip/Tcp/Eth layer information with XDP/EBPF.

Hello,
I run the xdp_dump code located under examples in this repo .
When I want to get the full ip/tcp/eth layers, a padding issue occurs. There are errors in the source and destination. For example, the source IP should be 192.168.11.10. It lacks the initial 192.

Problematic part:
Source IP=> 168.11.10.142:18433
Dest IP=> 251.140.67.224:47872

{IpH:{IHL:69 Version:0 TOS:0 TotLen:61 ID:16384 FragOff:16384 TTL:17 Protocol:83 Checksum:49343 SrcAddr:2383023016 DstAddr:3762523387} TcpH:{Source:328 Dest:187 Seq:1145570345 AckSeq:3450062566 Flags:22071 Window:14497 Check:15429 UrgPtr:34857} EthH:{HDest:[9 91 80 34 178 208] HSource:[192 191 47 111 128 8] HProto:0}}

C code side:

// PerfEvent eBPF map
BPF_MAP_DEF(perfmap) = {
    .map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
    .max_entries = 128,
};
BPF_MAP_ADD(perfmap);

// PerfEvent item
struct perf_event_item {
    // ip header section
    struct iphdr iphdr;
    struct tcphdr tcphdr;
    struct ethhdr ethhdr;
};
_Static_assert(sizeof(struct perf_event_item) == 56, "struct perf_event_item size");
int xdp_dump(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    __u64 packet_size = data_end - data;

    // L2
    struct ethhdr *ether = data;
    if (data + sizeof(*ether) > data_end) {
        return XDP_ABORTED;
    }

    // L3
    if (ether->h_proto != 0x08) {  // htons(ETH_P_IP) -> 0x08
        // Non IPv4
        return XDP_PASS;
    }
    data += sizeof(*ether);
    struct iphdr *ip = data;
    if (data + sizeof(*ip) > data_end) {
        return XDP_ABORTED;
    }

    // L4
    // if (ip->protocol != 0x06) {  // IPPROTO_TCP -> 6
    //   // Non TCP
    //   return XDP_PASS;
    // }
    data += ip->ihl * 4;
    struct tcphdr *tcp = data;
    if (data + sizeof(*tcp) > data_end) {
        return XDP_ABORTED;
    }

    // Emit perf event for every TCP SYN packet
    if (tcp->syn) {
        struct perf_event_item evt;
        memset(&evt, 0, sizeof(evt));
        evt.iphdr = *ip;
        evt.tcphdr = *tcp;
        evt.ethhdr = *ether;
        // 

        // flags for bpf_perf_event_output() actually contain 2 parts (each 32bit long):
        //
        // bits 0-31: either
        // - Just index in eBPF map
        // or
        // - "BPF_F_CURRENT_CPU" kernel will use current CPU_ID as eBPF map index
        //
        // bits 32-63: may be used to tell kernel to amend first N bytes
        // of original packet (ctx) to the end of the data.

        // So total perf event length will be sizeof(evt) + packet_size
        bpf_perf_event_output(ctx, &perfmap, BPF_F_CURRENT_CPU, &evt, sizeof(evt));
    }

    return XDP_PASS;
}

GoLang Side:

func RunDPIXDP(ifacename string) {
	// Create eBPF system / load .ELF files compiled by clang/llvm
	bpf := goebpf.NewDefaultEbpfSystem()
	err := bpf.LoadElf(*elf)
	if err != nil {
		fatalError("LoadElf() failed: %v", err)
	}
	printBpfInfo(bpf)

	// Find special "PERF_EVENT" eBPF map
	perfmap := bpf.GetMapByName("perfmap")
	if perfmap == nil {
		fmt.Println("eBPF map 'perfmap' not found")
		fatalError("eBPF map 'perfmap' not found")
	}

	// Program name matches function name in xdp.c:
	//      int xdp_dump(struct xdp_md *ctx)
	xdp := bpf.GetProgramByName(*programName)
	if xdp == nil {
		fatalError("Program '%s' not found.", *programName)
	}

	// Load XDP program into kernel
	err = xdp.Load()
	if err != nil {
		fatalError("xdp.Load(): %v", err)
	}

	// Attach to interface
	err = xdp.Attach(*iface)
	if err != nil {
		fmt.Println(err)
		fatalError("xdp.Attach(): %v", err)
	}
	defer xdp.Detach()

	// Add CTRL+C handler
	ctrlC := make(chan os.Signal, 1)
	signal.Notify(ctrlC, os.Interrupt)

	// Start listening to Perf Events
	perf, _ := goebpf.NewPerfEvents(perfmap)
	perfEvents, err := perf.StartForAllProcessesAndCPUs(4096)
	if err != nil {
		fatalError("perf.StartForAllProcessesAndCPUs(): %v", err)
	}

	fmt.Println("XDP program successfully loaded and attached.")
	fmt.Println("All new TCP connection requests (SYN) coming to this host will be dumped here.")
	fmt.Println()

	go func() {
		var event types.PerfEventItem
		for {

			if eventData, ok := <-perfEvents; ok {
				fmt.Println(eventData)
				reader := bytes.NewReader(eventData)
				binary.Read(reader, binary.LittleEndian, &event)
				fmt.Printf("Event: %+v\n", event)

				
				fmt.Printf("Source IP=> %v:%v\n", intToIPv4(event.IpH.SrcAddr), ntohs(event.TcpH.Source))
				fmt.Printf("Dest IP=> %v:%v\n", intToIPv4(event.IpH.DstAddr), ntohs(event.TcpH.Dest))

				if len(eventData)-metadataSize > 0 {
					
				}
			} else {

				// Update channel closed
				break
			}
		}
	}()

	// Wait until Ctrl+C pressed
	<-ctrlC

	// Stop perf events and print summary
	perf.Stop()
	fmt.Println("\nSummary:")
	fmt.Printf("\t%d Event(s) Received\n", perf.EventsReceived)
	fmt.Printf("\t%d Event(s) lost (e.g. small buffer, delays in processing)\n", perf.EventsLost)
	fmt.Println("\nDetaching program and exit...")
}

kprobe_events: device or resource busy / no such file or directory

The operation process is as follows:
git clone https://github.com/dropbox/goebpf.git cd goebpf/examples/kprobe/exec_dump/ make ./main

Program failed to run,error:
write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: no such file or directory write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy write: write /sys/kernel/debug/tracing/kprobe_events: device or resource busy

LoadElf() failed: loadAndCreateMaps() failed: Invalid binary representation of BPF map

When I run golang side. "LoadElf() failed: loadAndCreateMaps() failed: Invalid binary representation of BPF map" error occurs.
I do not know what the problem is. Can you help about it.

Create object:
clang -I../../.. -O2 -target bpf -c xdp_dump.c -o xdp_dump.elf

XDP Side (xdp_dump.c):

#include <stdbool.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/if_ether.h> // Include this for struct ethhdr
#include <linux/ip.h>       // Include this for struct iphdr
#include <linux/tcp.h>      // Include this for struct tcphdr

#define MAX_ENTRIES_FLOW 1024
#define min(x, y) ((x) < (y) ? x : y)

char _license[] SEC("license") = "GPL";

struct pkt_trace_metadata {
    __u32 ifindex;
    __u32 rx_queue;
    __u16 pkt_len;
    __u16 cap_len;
    __u16 flags;
    __u16 prog_index;
    int action;
} __packed;

// Define the structure for BPF map
struct bpf_map_def {
    __u32 type;
    __u32 key_size;
    __u32 value_size;
    __u32 max_entries;
};

#define BPF_MAP(_name, _type, _key_type, _value_type, _max_entries) \
    struct bpf_map_def SEC("maps") _name = {                        \
        .type = _type,                                              \
        .key_size = sizeof(_key_type),                              \
        .value_size = sizeof(_value_type),                          \
        .max_entries = _max_entries,                                \
    };

#define BPF_HASH(_name, _key_type, _value_type) \
    BPF_MAP(_name, BPF_MAP_TYPE_HASH, _key_type, _value_type, 10240);

#define BPF_PACKET_MAP(_name) \
    BPF_MAP(_name, BPF_MAP_TYPE_PERF_EVENT_ARRAY, int, __u32, MAX_ENTRIES_FLOW);


BPF_PACKET_MAP(packetmap)
// XDP program //
SEC("xdp")
int xdp_dump(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct pkt_trace_metadata metadata;
    //
    metadata.prog_index = 0;
    metadata.ifindex = ctx->ingress_ifindex;
    metadata.rx_queue = ctx->rx_queue_index;
    metadata.pkt_len = (__u16)(data_end - data);
    metadata.cap_len = min(metadata.pkt_len, 65535);
    metadata.action = 0;
    metadata.flags = 0;
    //
    bpf_perf_event_output(ctx, &packetmap, ((__u64)metadata.cap_len << 32) | BPF_F_CURRENT_CPU, &metadata, sizeof(metadata));
    
    return XDP_PASS;
}

GoLang Side:

// Create eBPF system / load .ELF files compiled by clang/llvm
bpf := goebpf.NewDefaultEbpfSystem()
err := bpf.LoadElf("xdp_dump.elf")
if err != nil {
    fatalError("LoadElf() failed: %v", err)
}
packetmap := bpf.GetMapByName("packetmap")
if packetmap == nil {
    fmt.Println("eBPF map 'packetmap' not found")
    fatalError("eBPF map 'packetmap' not found")
}

Invalid BPF instruction while bpf map lookup after having called `htons` or `bpf_htons`

Hey all,
at first I want to thank for the great work you've done so far with this package, I really like the simple API and had a very easy quickstart with this.

At the moment, I struggle with having the error LoadElf() failed: loadPrograms() failed: Invalid BPF instruction (at 144): &{133 0 1 0 4294967295} with the following code:

BPF_MAP_DEF(rxcnt) = {
    .map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(__u32),
    .value_size = sizeof(__u64),
    .max_entries = 256,
};
BPF_MAP_ADD(rxcnt);

static inline void count_tx(__u32 protocol)
{
	__u64 *rxcnt_count;
    rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol);
    
    if (rxcnt_count)
        *rxcnt_count += 1;
}

SEC("xdp") int xdp_sock(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    size_t offset = sizeof(struct ether_header) +
                    sizeof(struct iphdr);
          
    if(data + offset > data_end) {
        return XDP_PASS; // too short
    }
    count_tx(0);
    const struct ether_header *eh = (const struct ether_header *)data;
    // FIXME: Without htons|bpf_htons it works as expected, with one of them we got
    // LoadElf() failed: loadPrograms() failed: Invalid BPF instruction (at 144): &{133 0 1 0 4294967295}
    if(eh->ether_type != htons(ETHERTYPE_IP)) {
       return XDP_PASS; // not IP
    }

    // FIXME: THis somehow depends on this instruction
    // If we have a map lookup after this htons instructions, the error occurs
    count_tx(1);
    return XDP_PASS;
}

Since I'm new to ebpf, not sure if I'm doing something wrong, but loading the respective ELF file with another approach (https://github.com/xdp-project/xdp-tutorial) works as expected. A full setup to reproduce this can be found here: https://github.com/martenwallewein/goepf-repro.

It would be nice if you could have a look at this if I'm doing something wrong or if there is a bug in the loadPrograms function.

Best,
Marten

Should we support af_xdp?

Hi,
Thanks for the great library. I really like the way goebpf handle ebpf, but I found that goebpf seems not to support af_xdp.
So it would be better to support af_xdp, af_xdp is necessary for integrate with some dpi library like ndpi. What do you think?

Question on BPF_MAP_TYPE_HASH lookup errors

Hi there, I have been running in to issue trying to read a hash type map with this goebpf package. Is that supported yet? no matter which lookup type i try results in ebpf_map_lookup_elem() failed: No such file or directory.

map def:

BPF_MAP_DEF(stat) = {
    .map_type = BPF_MAP_TYPE_HASH,
    .key_size = sizeof(__u32),
    .value_size = sizeof(__u64),
    .max_entries = MAX_BPF_IP,
};
BPF_MAP_ADD(stat);

populating it with key ip_stat and value is a counter.

struct ip_stat {
    __u32 saddr;
    __u32 daddr;
    __u32 action;
    __u8 proto;
};

examples/basic_firewall

The basic_firewall does not work, I made the following change, can work properly.
Is this related to the environment?

My system environment
kernel: 5.4.2
llvm: 9.0

xdp_fw.c

// XDP program //
SEC("xdp")
  ...
  __u64 *rule_idx = bpf_map_lookup_elem(&blacklist, &ip->saddr); 
  ...
}
// XDP program //
SEC("xdp")
  ...
struct {
    __u32 prefixlen;
    __u32 saddr;
  } key;

  key.prefixlen = 32;
  key.saddr = ip->saddr;

  // Lookup SRC IP in blacklisted IPs
  __u64 *rule_idx = bpf_map_lookup_elem(&blacklist, &key);
  ...
}

Crash on PerfEvents Stop()

Calling perf.Stop() immediately after calling perf.StartForAllProcessesAndCPUs() will lead to a crash. This can be reproduced in the xdp_dump example by adding perf.Stop() call after the successful perf.StartForAllProcessesAndCPUs() call.
The initial use case where it was observed was having multiple perf maps, initializing multiple PerfEvents, one for each map, and calling Stop() on the init sequence for the PerfEvents that were already started.

LoadElf() loads without error but elfSystem is empty

Loading elf files run smoothly without any errors but afterwards it returns empty maps and empty programs (with GetMaps() and GetPrograms()) :( However, eBPF/XDP programs run fine if loaded for example with ip tool. llvm-objdump also show all code and objects! Trying to understand what is the problem? Any help appreciated!

XDP example packet_counter - BPF map definition and LoadElf() fails

Hi,

While trying to compile and run the given example of packet_counter, I faced the following:

  • Following BPF map definition as given in the example:
BPF_MAP_DEF(protocols) = {
    .map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(__u32),
    .value_size = sizeof(__u64),
    .max_entries = 255,
};
BPF_MAP_ADD(protocols);

It is rejected by clang with the following errors:

xdp_cnt.c:28:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
BPF_MAP_DEF(protocols) = {
^
xdp_cnt.c:28:13: error: a parameter list without types is only allowed in a function definition
BPF_MAP_DEF(protocols) = {
            ^
xdp_cnt.c:34:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
BPF_MAP_ADD(protocols);
^
xdp_cnt.c:34:13: error: a parameter list without types is only allowed in a function definition
BPF_MAP_ADD(protocols);
            ^
xdp_cnt.c:56:43: error: use of undeclared identifier 'protocols'
    __u64 *counter = bpf_map_lookup_elem(&protocols, &proto_index);
                              
2 warnings and 3 errors generated.

I have rewrote it in the following way:

struct bpf_map_def SEC ("maps") protocols = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(__u32),
    .value_size = sizeof(__u64),
    .max_entries = 255,
};

Then it is flawlessly compiled by clang but goebpf fails to load the elf file throwing out: "LoadElf() failed: loadAndCreateMaps() failed: Invalid binary representation of BPF map"

Below attached my reworked C code, Golang code is the same as given in the example:

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

// Ethernet header
struct ethhdr {
  __u8 h_dest[6];
  __u8 h_source[6];
  __u16 h_proto;
} __attribute__((packed));

// IPv4 header
struct iphdr {
  __u8 ihl : 4;
  __u8 version : 4;
  __u8 tos;
  __u16 tot_len;
  __u16 id;
  __u16 frag_off;
  __u8 ttl;
  __u8 protocol;
  __u16 check;
  __u32 saddr;
  __u32 daddr;
} __attribute__((packed));


// eBPF map to store IP proto counters (tcp, udp, etc)
struct bpf_map_def SEC ("maps") protocols = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(__u32),
    .value_size = sizeof(__u64),
    .max_entries = 255,
};

// XDP program //
SEC("xdp")
int packet_count(struct xdp_md *ctx) {
  void *data_end = (void *)(long)ctx->data_end;
  void *data = (void *)(long)ctx->data;

  // Only IPv4 supported for this example
  struct ethhdr *ether = data;
  if (data + sizeof(*ether) > data_end) {
    return XDP_ABORTED;
  }
  if (ether->h_proto == 0x08U) {  // htons(ETH_P_IP) -> 0x08U
    data += sizeof(*ether);
    struct iphdr *ip = data;
    if (data + sizeof(*ip) > data_end) {
      return XDP_ABORTED;
    }
    // Increase counter in "protocols" eBPF map
    __u32 proto_index = ip->protocol;
    __u64 *counter = bpf_map_lookup_elem(&protocols, &proto_index);
    if (counter) {
      (*counter)++;
    }
  }

  return XDP_PASS;
}

char _license[] SEC("license") = "GPLv2";

mocks: A proposal to override default implementation

Hello,

First of all, thank you for working on this library. So far I've had a very positive experience using it, especially with the included examples and documentation.

I do have one suggestion regarding the mocks package. What are your thoughts on providing a way to override the default implementation each mock has? To give you an example:

Currently the LoadElf() method from mock_ebpf.go looks something like the following:

func (m *MockSystem) LoadElf(fn string) error {
	return nil
}

This is great but it doesn't let the user override with a different behaviour for LoadElf(), one that could be potentially a non happy-path that returns an error. So with that in mind it would look something like the following:

func (m *MockSystem) LoadElf(fn string) error {
        if m.loadElf != nil {
             return m.loadElf(fn)
        }
	return nil
}

Where loadElf is a member of the struct MockSystem and can be overriden as needed during a unit test where various implementations of LoadElf() are needed.

Let me know your thoughts, I can draft a PR to incorporate this.

Include linux header file failed

I want to #include <net/sock.h> in kprobe.c and i also add the path in clang but it failed.
Can you tell me how to include the linux header?
Thanks

error while loading BPF_MAP_TYPE_PROG_ARRAY map

A part of my program is as below,encountered this problem while executing.
Loadmap() failed: ebpf_map_update_elem() failed: Invalid argument
this program works when i changed BPF_MAP_TYPE_PROG_ARRAY to others like BPF_MAP_TYPE_PERCPU_ARRAY

var elf = flag.String("elf", "/usr/src/linux-source-5.11.0/samples/bpf/test_dump.o", "clang/llvm compiled binary file")
var elf1 = flag.String("elf1", "xdp_fw.o", "clang/llvm compiled binary file")
// Create eBPF system
bpf := goebpf.NewDefaultEbpfSystem()
// Load .ELF files compiled by clang/llvm
err := bpf.LoadElf(*elf, 1)
if err != nil {
fatalError("Loadbpf() failed: %v", err)
}
next_prog_map := bpf.GetMapByName("next_prog_map").(*goebpf.EbpfMap)
fmt.Println("46464646", next_prog_map)
bpf1 := goebpf.NewDefaultEbpfSystem()
// Load .ELF files compiled by clang/llvm
err = bpf1.LoadElf(*elf1, 1)
if err != nil {
fatalError("Loadbpf1() failed: %v", err)
}
shtest := bpf1.GetProgramByName("firewall")
err = next_prog_map.Update(0, shtest.GetFd())
if err != nil {
fatalError("Loadmap() failed: %v", err)
}

perf_event_output doesn't work

Hello folks,

last week i was working on a small program to submit perf events to the userspace, when i found out that the commit #36 might have introduced the issue.
what happens is that i am able to happily compile the code, however, it barfs like this when i try to load it:

panic: ebpf_prog_load() failed: 0: (bf) r6 = r1
1: (61) r2 = *(u32 *)(r6 +4)
2: (61) r1 = *(u32 *)(r6 +0)
3: (bf) r3 = r1
4: (07) r3 += 14
5: (2d) if r3 > r2 goto pc+41
 R1=pkt(id=0,off=0,r=14,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=0,off=14,r=14,imm=0) R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
6: (71) r3 = *(u8 *)(r1 +12)
7: (71) r4 = *(u8 *)(r1 +13)
8: (67) r4 <<= 8
9: (4f) r4 |= r3
10: (55) if r4 != 0x8 goto pc+36
 R1=pkt(id=0,off=0,r=14,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R4=inv8 R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
11: (bf) r3 = r1
12: (07) r3 += 34
13: (2d) if r3 > r2 goto pc+33
 R1=pkt(id=0,off=0,r=34,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=0,off=34,r=34,imm=0) R4=inv8 R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
14: (71) r3 = *(u8 *)(r1 +23)
15: (55) if r3 != 0x11 goto pc+31
 R1=pkt(id=0,off=0,r=34,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv17 R4=inv8 R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
16: (bf) r3 = r1
17: (07) r3 += 42
18: (2d) if r3 > r2 goto pc+28
 R1=pkt(id=0,off=0,r=42,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=0,off=42,r=42,imm=0) R4=inv8 R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
19: (bf) r4 = r1
20: (07) r4 += 50
21: (2d) if r4 > r2 goto pc+25
 R1=pkt(id=0,off=0,r=50,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=0,off=42,r=50,imm=0) R4=pkt(id=0,off=50,r=50,imm=0) R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
22: (69) r2 = *(u16 *)(r1 +36)
23: (55) if r2 != 0x9411 goto pc+23
 R1=pkt(id=0,off=0,r=50,imm=0) R2=inv37905 R3=pkt(id=0,off=42,r=50,imm=0) R4=pkt(id=0,off=50,r=50,imm=0) R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
24: (61) r2 = *(u32 *)(r3 +0)
25: (15) if r2 == 0x0 goto pc+21
 R1=pkt(id=0,off=0,r=50,imm=0) R2=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R3=pkt(id=0,off=42,r=50,imm=0) R4=pkt(id=0,off=50,r=50,imm=0) R6=ctx(id=0,off=0,imm=0) R10=fp0,call_-1
26: (b7) r2 = 0
27: (7b) *(u64 *)(r10 -8) = r2
28: (61) r2 = *(u32 *)(r1 +26)
29: (63) *(u32 *)(r10 -8) = r2
30: (69) r1 = *(u16 *)(r1 +34)
31: (dc) r1 = be16 r1
32: (6b) *(u16 *)(r10 -4) = r1
33: (bf) r7 = r10
34: (07) r7 += -8
35: (18) r1 = 0xffff8fea69922000
37: (bf) r2 = r7
38: (85) call bpf_map_lookup_elem#1
39: (bf) r1 = r6
40: (18) r2 = 0xffff8feaa7c18000
42: (18) r3 = 0xffffffff
44: (bf) r4 = r7
45: (b7) r5 = 8
46: (85) call bpf_skb_load_bytes#26
unknown func bpf_skb_load_bytes#26


goroutine 1 [running]:
main.main()

i found out that the bpf_perf_event_output is called by the id 26, which strangely maps to skb_load_bytes.

# llvm-objdump-9 -S kern/xdp.elf

kern/xdp.elf:	file format ELF64-BPF


Disassembly of section xdp:

0000000000000000 xdp_jitd:
       0:	bf 16 00 00 00 00 00 00	r6 = r1
       1:	61 62 04 00 00 00 00 00	r2 = *(u32 *)(r6 + 4)
       2:	61 61 00 00 00 00 00 00	r1 = *(u32 *)(r6 + 0)
       3:	bf 13 00 00 00 00 00 00	r3 = r1
       4:	07 03 00 00 0e 00 00 00	r3 += 14
       5:	2d 23 29 00 00 00 00 00	if r3 > r2 goto +41 <LBB0_9>
       6:	71 13 0c 00 00 00 00 00	r3 = *(u8 *)(r1 + 12)
       7:	71 14 0d 00 00 00 00 00	r4 = *(u8 *)(r1 + 13)
       8:	67 04 00 00 08 00 00 00	r4 <<= 8
       9:	4f 34 00 00 00 00 00 00	r4 |= r3
      10:	55 04 24 00 08 00 00 00	if r4 != 8 goto +36 <LBB0_9>
      11:	bf 13 00 00 00 00 00 00	r3 = r1
      12:	07 03 00 00 22 00 00 00	r3 += 34
      13:	2d 23 21 00 00 00 00 00	if r3 > r2 goto +33 <LBB0_9>
      14:	71 13 17 00 00 00 00 00	r3 = *(u8 *)(r1 + 23)
      15:	55 03 1f 00 11 00 00 00	if r3 != 17 goto +31 <LBB0_9>
      16:	bf 13 00 00 00 00 00 00	r3 = r1
      17:	07 03 00 00 2a 00 00 00	r3 += 42
      18:	2d 23 1c 00 00 00 00 00	if r3 > r2 goto +28 <LBB0_9>
      19:	bf 14 00 00 00 00 00 00	r4 = r1
      20:	07 04 00 00 32 00 00 00	r4 += 50
      21:	2d 24 19 00 00 00 00 00	if r4 > r2 goto +25 <LBB0_9>
      22:	69 12 24 00 00 00 00 00	r2 = *(u16 *)(r1 + 36)
      23:	55 02 17 00 11 94 00 00	if r2 != 37905 goto +23 <LBB0_9>
      24:	61 32 00 00 00 00 00 00	r2 = *(u32 *)(r3 + 0)
      25:	15 02 15 00 00 00 00 00	if r2 == 0 goto +21 <LBB0_9>
      26:	b7 02 00 00 00 00 00 00	r2 = 0
      27:	7b 2a f8 ff 00 00 00 00	*(u64 *)(r10 - 8) = r2
      28:	61 12 1a 00 00 00 00 00	r2 = *(u32 *)(r1 + 26)
      29:	63 2a f8 ff 00 00 00 00	*(u32 *)(r10 - 8) = r2
      30:	69 11 22 00 00 00 00 00	r1 = *(u16 *)(r1 + 34)
      31:	dc 01 00 00 10 00 00 00	r1 = be16 r1
      32:	6b 1a fc ff 00 00 00 00	*(u16 *)(r10 - 4) = r1
      33:	bf a7 00 00 00 00 00 00	r7 = r10
      34:	07 07 00 00 f8 ff ff ff	r7 += -8
      35:	18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00	r1 = 0 ll
      37:	bf 72 00 00 00 00 00 00	r2 = r7
      38:	85 00 00 00 01 00 00 00	call 1
      39:	bf 61 00 00 00 00 00 00	r1 = r6
      40:	18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00	r2 = 0 ll
      42:	18 03 00 00 ff ff ff ff 00 00 00 00 00 00 00 00	r3 = 4294967295 ll
      44:	bf 74 00 00 00 00 00 00	r4 = r7
      45:	b7 05 00 00 08 00 00 00	r5 = 8
      46:	85 00 00 00 1a 00 00 00	call 26

0000000000000178 LBB0_9:
      47:	b7 00 00 00 02 00 00 00	r0 = 2
      48:	95 00 00 00 00 00 00 00	exit

i expected perf_event_output to match the 27th item on the list (index 26) https://github.com/dropbox/goebpf/blob/master/bpf_helpers.h#L268

the program is successfully loaded if i do a git revert on the commit and compile it:

goebpf(master)» git revert 4910d571d799ca6c94728f5aad303c0f154a831f    [15:52:46]
[master 6c9a65b] Revert "[maps] Implement bpf_map_get_next_key (#36)"
 6 files changed, 321 deletions(-)
--------------
# llvm-objdump-9 -S kern/xdp.elf

kern/xdp.elf:	file format ELF64-BPF


Disassembly of section xdp:

0000000000000000 xdp_jitd:
       0:	bf 16 00 00 00 00 00 00	r6 = r1
       1:	61 62 04 00 00 00 00 00	r2 = *(u32 *)(r6 + 4)
       2:	61 61 00 00 00 00 00 00	r1 = *(u32 *)(r6 + 0)
       3:	bf 13 00 00 00 00 00 00	r3 = r1
       4:	07 03 00 00 0e 00 00 00	r3 += 14
       5:	2d 23 29 00 00 00 00 00	if r3 > r2 goto +41 <LBB0_9>
       6:	71 13 0c 00 00 00 00 00	r3 = *(u8 *)(r1 + 12)
       7:	71 14 0d 00 00 00 00 00	r4 = *(u8 *)(r1 + 13)
       8:	67 04 00 00 08 00 00 00	r4 <<= 8
       9:	4f 34 00 00 00 00 00 00	r4 |= r3
      10:	55 04 24 00 08 00 00 00	if r4 != 8 goto +36 <LBB0_9>
      11:	bf 13 00 00 00 00 00 00	r3 = r1
      12:	07 03 00 00 22 00 00 00	r3 += 34
      13:	2d 23 21 00 00 00 00 00	if r3 > r2 goto +33 <LBB0_9>
      14:	71 13 17 00 00 00 00 00	r3 = *(u8 *)(r1 + 23)
      15:	55 03 1f 00 11 00 00 00	if r3 != 17 goto +31 <LBB0_9>
      16:	bf 13 00 00 00 00 00 00	r3 = r1
      17:	07 03 00 00 2a 00 00 00	r3 += 42
      18:	2d 23 1c 00 00 00 00 00	if r3 > r2 goto +28 <LBB0_9>
      19:	bf 14 00 00 00 00 00 00	r4 = r1
      20:	07 04 00 00 32 00 00 00	r4 += 50
      21:	2d 24 19 00 00 00 00 00	if r4 > r2 goto +25 <LBB0_9>
      22:	69 12 24 00 00 00 00 00	r2 = *(u16 *)(r1 + 36)
      23:	55 02 17 00 11 94 00 00	if r2 != 37905 goto +23 <LBB0_9>
      24:	61 32 00 00 00 00 00 00	r2 = *(u32 *)(r3 + 0)
      25:	15 02 15 00 00 00 00 00	if r2 == 0 goto +21 <LBB0_9>
      26:	b7 02 00 00 00 00 00 00	r2 = 0
      27:	7b 2a f8 ff 00 00 00 00	*(u64 *)(r10 - 8) = r2
      28:	61 12 1a 00 00 00 00 00	r2 = *(u32 *)(r1 + 26)
      29:	63 2a f8 ff 00 00 00 00	*(u32 *)(r10 - 8) = r2
      30:	69 11 22 00 00 00 00 00	r1 = *(u16 *)(r1 + 34)
      31:	dc 01 00 00 10 00 00 00	r1 = be16 r1
      32:	6b 1a fc ff 00 00 00 00	*(u16 *)(r10 - 4) = r1
      33:	bf a7 00 00 00 00 00 00	r7 = r10
      34:	07 07 00 00 f8 ff ff ff	r7 += -8
      35:	18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00	r1 = 0 ll
      37:	bf 72 00 00 00 00 00 00	r2 = r7
      38:	85 00 00 00 01 00 00 00	call 1
      39:	bf 61 00 00 00 00 00 00	r1 = r6
      40:	18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00	r2 = 0 ll
      42:	18 03 00 00 ff ff ff ff 00 00 00 00 00 00 00 00	r3 = 4294967295 ll
      44:	bf 74 00 00 00 00 00 00	r4 = r7
      45:	b7 05 00 00 08 00 00 00	r5 = 8
      46:	85 00 00 00 19 00 00 00	call 25

0000000000000178 LBB0_9:
      47:	b7 00 00 00 02 00 00 00	r0 = 2
      48:	95 00 00 00 00 00 00 00	exit
-----------------------

# ./<binary>
Maps:
	<map1>: Hash, Fd 5
	<map2>: Event array, Fd 6

Programs:
	<xdp_prog>: XDP, size 392, license "GPL"

System data: map[xdp_prog:0xc0000b8480]Attaching program xdp_prog
[perf event received]
^C

i spent the past two days trying to get it fixed, no luck so far.
can you help me?

thank you!

edit:
if i move get_next_key function (the one introduced on the commit mentioned above) to the end of the list, the perf_event_output function works fine. does the ordering here needs to be taken into account somewhere else?

eBPF program in Go

is it possible to write eBPF programs in Go or TinyGo since it is based on LLVM?

can someone guide me to do this?

can't run xdp_dump

[root@node01 xdp_dump]# go build

github.com/dropbox/goebpf

In file included from ../../../map.go:13:0:
./bpf_helpers.h:957:15: error: expected declaration specifiers or '...' before 'sizeof'
static_assert(sizeof(__u8) == 1, "wrong_u8_size");
^
./bpf_helpers.h:957:34: error: expected declaration specifiers or '...' before string constant
static_assert(sizeof(__u8) == 1, "wrong_u8_size");
^
./bpf_helpers.h:958:15: error: expected declaration specifiers or '...' before 'sizeof'
static_assert(sizeof(__u16) == 2, "wrong_u16_size");
^
./bpf_helpers.h:958:35: error: expected declaration specifiers or '...' before string constant
static_assert(sizeof(__u16) == 2, "wrong_u16_size");
^
./bpf_helpers.h:959:15: error: expected declaration specifiers or '...' before 'sizeof'
static_assert(sizeof(__u32) == 4, "wrong_u32_size");
^
./bpf_helpers.h:959:35: error: expected declaration specifiers or '...' before string constant
static_assert(sizeof(__u32) == 4, "wrong_u32_size");
^
./bpf_helpers.h:960:15: error: expected declaration specifiers or '...' before 'sizeof'
static_assert(sizeof(__u64) == 8, "wrong_u64_size");
^
./bpf_helpers.h:960:35: error: expected declaration specifiers or '...' before string constant
static_assert(sizeof(__u64) == 8, "wrong_u64_size");
^
[root@node01 xdp_dump]#

all tests fail

I compiled tests with make and ran them with sudo ./itest_test
All tests failed with errors like

loadAndCreateMaps() failed: map.Create() failed: ebpf_create_map() failed: Operation not permitted

ebpf_create_map() failed: Operation not permitted

go version go1.15.7 linux/amd64
Ubuntu 20.04 kernel 5.8.0-41

XDP_TX | XDP_REDIRECT

Can you please tell me what conditions regarding the interface you need to fulfill in order for the return codes XDP_REDIRECT and XDP_TX to work correctly? For example, I change the destination port, and return the XDP_PASS code, the packet does not get in this interface to the server running on the port that I changed in the package:
struct tcphdr *tcp = ctx->data_start + ctx->nh_offset; tcp->dest = (__u16)bpf_ntohs(some_port_value); // 5555 for example...
Then I tried using the return code XDP_REDIRECT, followed by redirecting the packet to the external interface, but this just led to freezing the external network interface:
`
INTERNAL __u32 redirect(struct context *ctx)
{
struct bpf_fib_lookup fib_params = {};
__u32 action = XDP_PASS;

if (ctx->v4 && ctx->tcp)
{

    /* populate the fib_params fields to prepare for the lookup */
	fib_params.family	= AF_INET;
	fib_params.tos		= ctx->v4->tos;
	fib_params.l4_protocol	= ctx->v4->protocol;
	fib_params.sport	= ctx->tcp->source;
	fib_params.dport	= some_port_value;
	fib_params.tot_len	= bpf_ntohs(ctx->v4->tot_len);
	fib_params.ipv4_src	= ctx->v4->saddr;
	fib_params.ipv4_dst	= ctx->v4->daddr;
    /* Set a proper destination address */
       memcpy(custom_ctx.eth->h_dest, , ETH_ALEN);
       action = bpf_redirect_map(&tx_port, 0, 0);
}

fib_params.ifindex = 1;
/* this is where the FIB lookup happens. If the lookup is successful */
/* it will populate the fib_params.ifindex with the egress interface index */
__u16 h_proto = ctx->eth->h_proto;
int rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), 0);
switch (rc) {
case BPF_FIB_LKUP_RET_SUCCESS:         /* lookup successful */
 	   /* we are a router, so we need to decrease the ttl */
 	if (h_proto == bpf_htons(ETH_P_IP))
 		ip_decrease_ttl(ctx->v4);
 	else if (h_proto == bpf_htons(ETH_P_IPV6))
 		ctx->v6->hop_limit--;
 	/* set the correct new source and destionation mac addresses */
 	/* can be found in fib_params.dmac and fib_params.smac */
 	memcpy(ctx->eth->h_dest, fib_params.dmac, ETH_ALEN);
 	memcpy(ctx->eth->h_source, fib_params.smac, ETH_ALEN);
 	/* and done, now we set the action to bpf_redirect_map with fib_params.ifindex which is the egress port as paramater */
 	action = bpf_redirect_map(&tx_port, fib_params.ifindex, 0);
 	break;
 case BPF_FIB_LKUP_RET_BLACKHOLE:    /* dest is blackholed; can be dropped */
 case BPF_FIB_LKUP_RET_UNREACHABLE:  /* dest is unreachable; can be dropped */
 case BPF_FIB_LKUP_RET_PROHIBIT:     /* dest not allowed; can be dropped */
 	action = XDP_DROP;
 	break;
 case BPF_FIB_LKUP_RET_NOT_FWDED:    /* packet is not forwarded */
 case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */
 case BPF_FIB_LKUP_RET_UNSUPP_LWT:   /* fwd requires encapsulation */
 case BPF_FIB_LKUP_RET_NO_NEIGH:     /* no neighbor entry for nh */
 case BPF_FIB_LKUP_RET_FRAG_NEEDED:  /* fragmentation required to fwd */
 	/* PASS */
 	break;
 }

return action; 

}
`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.