0

I have a bpf program storing some stats in a map via BPF_TYPE_MAP_ARRAY. That side of code seems to works correctly, I can use bpftool to see my map.

# bpftool map show
31: array  name xdp_stats_map  flags 0x0
        key 4B  value 16B  max_entries 16  memlock 4096B

# bpftool map dump id 31
key: 00 00 00 00  value: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
key: 01 00 00 00  value: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
key: 02 00 00 00  value: e3 a6 00 00 00 00 00 00  99 38 b3 00 00 00 00 00
key: 03 00 00 00  value: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
[...] 

We can see some data on key 2 and theses data are updated correctly. However when I try to collect theses data in userspace with libbpf, I only have null values. I have no clue what's wrong.

If I fake the fd, call fails, if I try to fetch more than 16 elements, it fails later, so everything sounds correct.

struct counters {
    __u64 rx_packets;
    __u64 rx_bytes;
};

void dies(char *str) {
    fprintf(stderr, "[-] %s", str);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
    struct counters value;
    char *filename = "./xdp_pass.o";
    int fd;

    if(argc > 1)
        filename = argv[1];

    struct bpf_object *obj = bpf_object__open(filename);
    if(libbpf_get_error(obj))
        dies("could not open bpf object");

    bpf_object__load(obj);
    if(libbpf_get_error(obj))
        dies("could not load bpf object");

    if((fd = bpf_object__find_map_fd_by_name(obj, "xdp_stats_map")) < 0)
        dies("could not find map in the object");

    for(__u32 key = 0; key < 16; key++) {
        if((bpf_map_lookup_elem(fd, &key, &value)) != 0)
            dies("could not key in map");

        printf("ID % 3d: %llu, %llu\n", key, value.rx_packets, value.rx_bytes);
    }

    return 0;
}

Returns:

ID   0: 0, 0
ID   1: 0, 0
ID   2: 0, 0
ID   3: 0, 0
ID   4: 0, 0
[...] (all zero)
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Maxux
  • 199
  • 1
  • 1
  • 11
  • 2
    Is it pointing to the same map? I see you load your program but you don't attach it. You also don't seem to reuse any existing map. So you could have one `xdp_stats_map` existing on your system, with non-0 counters; When loading the program with libbpf, the lib may recreate a map (same name, but in fact different map) with empty counters; and then you'd dump that second map with empty values. Try checking with bpftool (sleep in your program during the dump?), or by retrieving the id for your map? – Qeole May 18 '22 at 17:06
  • 1
    @Qeole you're right, when adding some sleep, I see two separated map on the dump, the lib create a new one, I'll take a look on how to attach the existing one. I don't attach in my program because the code is already attached via `ip link`, do I need to attach it anyway ? – Maxux May 19 '22 at 19:01
  • 1
    Thanks @Qeole, that was it. I used a global pinning, `ip link` then export it automatically to `/sys/fs/bpf` and I can use the file created there to attach to the map, now it works ! – Maxux May 19 '22 at 19:27

1 Answers1

3

Thanks to @Qeole in the comment of my original post. Here is how I solved my problem:

I use ip link to attach my program to my interface. In the definition of my bpf map, I added a pinning option LIBBPF_PIN_BY_NAME:

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(key_size, sizeof(__u32));
    __uint(value_size, sizeof(struct xdp_stats));
    __uint(pinning, LIBBPF_PIN_BY_NAME);
    __uint(max_entries, 16);

} xdp_stats_map SEC(".maps");

With this way, tc will automatically pin the map to /sys/fs/bpf/tc/globals/xdp_stats_map.

In my userspace program, I can directly fetch that fd now:

int fd = bpf_obj_get("/sys/fs/bpf/tc/globals/xdp_stats_map");

for(__u32 key = 0; key < 16; key++) {
    if((bpf_map_lookup_elem(fd, &key, &value)) != 0)
        ...

And it works !

Maxux
  • 199
  • 1
  • 1
  • 11
  • Great! I'm linking a related questions on [how to reuse an existing (e.g. pinned) map for a new eBPF program](https://stackoverflow.com/questions/72151741/how-to-access-to-bpf-map-which-was-made-by-user-program-in-the-kernel-program-k), in case future readers might be looking for that. – Qeole May 20 '22 at 08:25