cilium / ebpf

ebpf-go is a pure-Go library to read, modify and load eBPF programs and attach them to various hooks in the Linux kernel.

Home Page:https://ebpf-go.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Missing support for iterating over per-CPU maps

HouzuoGuo opened this issue · comments

Describe the bug

(Disclaimer: I'm new to eBPF)

When iterating over a `BPF_MAP_TYPE_LRU_PERCPU_HASH`, the iterator `Map.Iterate(...)` yields nothing.

How to reproduce

(Partial snippet)


struct {
  __uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
  // __uint(type, BPF_MAP_TYPE_LRU_HASH);
  __type(key, __u32);
  __type(value, __u64);
  __uint(max_entries, 128);
} src_data_len SEC(".maps");

static __always_inline int parse_addr(struct xdp_md *ctx, __u32 *out_src,
                                      __u16 *out_len) {
...

SEC("xdp")
int count_packets(struct xdp_md *ctx) {
...
  __u32 src_ip;
  __u16 src_len;
  __u64 new_len = 0;
  if (!parse_addr(ctx, &src_ip, &src_len)) {
    return XDP_PASS;
  }
  __u64 *existing_len = bpf_map_lookup_elem(&src_data_len, &src_ip);
  new_len += src_len;
  if (existing_len) {
    new_len += *existing_len;
  }
  bpf_map_update_elem(&src_data_len, &src_ip, &new_len, BPF_ANY);
...

	for {
		select {
		case <-ticker.C:
			var totalPacketCount, totalPacketLen []uint64
			var srcIP []byte
			var ipTrafficLen uint64

			if err := objs.PktCount.Lookup(uint32(0), &totalPacketCount); err != nil {
				log.Fatal(err)
			}
			if err = objs.PktSize.Lookup(uint32(0), &totalPacketLen); err != nil {
				log.Fatal(err)
			}
			srcTrafficIter := objs.SrcDataLen.Iterate()
			for srcTrafficIter.Next(&srcIP, &ipTrafficLen) {
                                // Yields nothing from BPF_MAP_TYPE_LRU_PERCPU_HASH.
				srcIPAddr := net.IPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3])
				log.Printf("source IP %s: %v bytes of traffic", srcIPAddr, ipTrafficLen)
			}
			for cpu := range totalPacketCount {
				log.Printf("cpu %d: Seen %d packets in %d bytes", cpu, totalPacketCount[cpu], totalPacketLen[cpu])
			}
		case <-sigInt:
			return
		}
	}


### Version information

github.com/cilium/ebpf v0.12.3

You're not checking srcTrafficIter.Err(). Otherwise you'd see the following error:

look up next key: per-cpu value requires a slice or a pointer to slice

Here is a diff to one of the examples which shows how to use it:

diff --git a/examples/xdp/bpf_bpfeb.o b/examples/xdp/bpf_bpfeb.o
index f90c8b99..9ab3a59f 100644
Binary files a/examples/xdp/bpf_bpfeb.o and b/examples/xdp/bpf_bpfeb.o differ
diff --git a/examples/xdp/bpf_bpfel.o b/examples/xdp/bpf_bpfel.o
index 35dba4b8..e708d827 100644
Binary files a/examples/xdp/bpf_bpfel.o and b/examples/xdp/bpf_bpfel.o differ
diff --git a/examples/xdp/main.go b/examples/xdp/main.go
index 123513b1..99796157 100644
--- a/examples/xdp/main.go
+++ b/examples/xdp/main.go
@@ -72,10 +72,10 @@ func formatMapContents(m *ebpf.Map) (string, error) {
 	var (
 		sb  strings.Builder
 		key netip.Addr
-		val uint32
+		val = make([]uint32, ebpf.MustPossibleCPU())
 	)
 	iter := m.Iterate()
-	for iter.Next(&key, &val) {
+	for iter.Next(&key, val) {
 		sourceIP := key // IPv4 source address in network byte order.
 		packetCount := val
 		sb.WriteString(fmt.Sprintf("\t%s => %d\n", sourceIP, packetCount))
diff --git a/examples/xdp/xdp.c b/examples/xdp/xdp.c
index 2f36c161..ac414b66 100644
--- a/examples/xdp/xdp.c
+++ b/examples/xdp/xdp.c
@@ -9,7 +9,7 @@ char __license[] SEC("license") = "Dual MIT/GPL";
 
 /* Define an LRU hash map for storing packet count by source IPv4 address */
 struct {
-	__uint(type, BPF_MAP_TYPE_LRU_HASH);
+	__uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
 	__uint(max_entries, MAX_MAP_ENTRIES);
 	__type(key, __u32); // source IPv4 address
 	__type(value, __u32); // packet count

Output:

2024/01/08 12:15:10 Attached XDP program to iface "lo" (index 1)
2024/01/08 12:15:10 Press Ctrl-C to exit and remove the program
2024/01/08 12:15:11 Map contents:
2024/01/08 12:15:12 Map contents:
2024/01/08 12:15:13 Map contents:
	127.0.0.1 => [0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
2024/01/08 12:15:14 Map contents:
	127.0.0.1 => [2 2 4 0 5 0 0 0 0 0 0 0 0 2 0 0]
2024/01/08 12:15:15 Map contents:
	127.0.0.1 => [2 2 4 0 5 0 0 0 0 0 0 0 0 2 0 0]

That's very cool, thank you!