Search code examples
c++ebpflibbpf

How to access BPF map from userspace that was created in kernel space


I am a complete novice at anything ebpf but trying out some random ideas to get some knowledge.

I wanted to have an eBPF module that could filter some packets based on an allowed list of CIDR. A userspace application should be able to update the allowed list so that filtering can happen without reloading the eBPF probe.

I started with some very simple kernel code:

// IPv4 addr/mask to store in the trie
struct ip4_trie_key {
    __u32 prefixlen;
    struct in_addr addr;
};

struct {
    __uint(type, BPF_MAP_TYPE_LPM_TRIE);
    __uint(max_entries, 128);
    __type(key, struct ip4_trie_key);
    __type(value, char);
    __uint(map_flags, BPF_F_NO_PREALLOC);
} allowlist SEC(".maps");

SEC("socket")
int test_ip_filter(struct __sk_buff *skb)
{
    // get header
    struct iphdr iph;
    if (0 != bpf_skb_load_bytes(skb, 0, &iph, sizeof(iph))) {
        return BPF_DROP;
    }

    // Form the key to lookup in the allowlist
    struct ip4_trie_key key = {
        .prefixlen = 32,
        .addr = iph.saddr
    };

    if (bpf_map_lookup_elem(&allowlist, &key)) {
        return BPF_OK;
    }

    return BPF_DROP;

I load the module with bpftool prog load bpf_filter.o /sys/fs/bpf/ipfilter and can see with the same tool it looks ok:

[root@~]$ bpftool -f map show
...
134: socket_filter  name test_ip_filter  tag ac82ae2a45a2e98d  gpl
    loaded_at 2022-10-26T16:36:03+0100  uid 0
    xlated 472B  jited 276B  memlock 4096B  map_ids 38,40
    pinned /sys/fs/bpf/ipfilter
    btf_id 191


[root@~]$ bpftool -f map show
...
38: lpm_trie  name allowlist  flags 0x1
    key 8B  value 1B  max_entries 128  memlock 4096B
    btf_id 191


Now in my userspace application I want to connect to the map and update it. I thought I could maybe do something like this:

// note, error checking omitted.

// Get object fd
int obj_fd = bpf_obj_get("/sys/fs/bpf/ipfilter");

// Get the bpf object
struct bpf_object *obj; // <= HOW TO GET THIS

// Get the maps FD (use for updating etc).
int map_fd = bpf_object__find_map_fd_by_name(obj, "allowlist");

// get details about the map (key size etc).
struct bpf_map_info map_info = {};
__u32 info_len = sizeof(map_info);
bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);

Assuming what I have is correct so far, how do I retrieve bpf_object struct or am I way off in my undertanding?


Solution

  • Posting an answer myself as I seem to have found a way (although not sure it is the correct thing to do?).

    I set the pinning mode of the struct in kernel space to be pinned by name e.g

    struct {
        __uint(type, BPF_MAP_TYPE_LPM_TRIE);
        __uint(max_entries, 128);
        __type(key, struct ip4_trie_key);
        __type(value, char);
        __uint(map_flags, BPF_F_NO_PREALLOC);
        __uint(pinning, LIBBPF_PIN_BY_NAME);    // <= THIS LINE FIXED IT
    } allowlist SEC(".maps");
    

    This gave the map it's own pinned file in /sys/fs/bpf/allowlist which I could then just access with bpf_obj_get("/sys/fs/bpf/allowlist").