创建 bpf map 时,出现了如下错误:

1
failed to load bpf: failed to create map: invalid argument

看下需要创建的 bpf map:

1
2
3
4
5
6
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key, __u8);
    __type(value, bitmap);
    __uint(max_entries, PROTO_MAX_ENTRIES);
} acl_protocol SEC(".maps");

想着,需要处理的 protocol 也就 TCP、UDP 和 ICMP 这 3 个,所以就使用最节省内存的 key 类型 __u8;因为每个 key 只需要占用 1 个字节。

可是,为什么出现无效参数的错误呢?

话不多说,直接查看 BPF_MAP_TYPE_ARRAY 对应 bpf map 的源代码吧。

在创建 bpf map 时,会先调用 map_ops->map_alloc_check() 进行一次检查; 这次检查是每个 bpf map 类型做的一次针对类型需求进行的检查。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// ${KERNEL}/kernel/bpf/arraymap.c

const struct bpf_map_ops array_map_ops = {
    .map_meta_equal = array_map_meta_equal,
    .map_alloc_check = array_map_alloc_check,
    .map_alloc = array_map_alloc,
    .map_free = array_map_free,
    .map_get_next_key = array_map_get_next_key,
    .map_release_uref = array_map_free_timers,
    .map_lookup_elem = array_map_lookup_elem,
    .map_update_elem = array_map_update_elem,
    .map_delete_elem = array_map_delete_elem,
    // ...
};

int array_map_alloc_check(union bpf_attr *attr)
{
    bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
    int numa_node = bpf_map_attr_numa_node(attr);

    /* check sanity of attributes */
    if (attr->max_entries == 0 || attr->key_size != 4 ||
        attr->value_size == 0 ||
        attr->map_flags & ~ARRAY_CREATE_FLAG_MASK ||
        !bpf_map_flags_access_ok(attr->map_flags) ||
        (percpu && numa_node != NUMA_NO_NODE))
        return -EINVAL;

    // ...

    if (attr->value_size > KMALLOC_MAX_SIZE)
        /* if value_size is bigger, the user space won't be able to
         * access the elements.
         */
        return -E2BIG;

    return 0;
}

ARRAY bpf map 做了以下检查:

  1. max_entries 不能为 0。
  2. key 大小只能是 4。
  3. value 大小不能为 0。
  4. flags 检查。
  5. NUMA flags 检查。
  6. value 大小不能大于 “Maximum allocatable size”。

所以,acl_protocol 的 key 大小是 1,违反了第 2 条。

最终,只需要将 key 的类型从 __u8 改为 __u32 即可。

1
2
3
4
5
6
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key, __u32);
    __type(value, bitmap);
    __uint(max_entries, PROTO_MAX_ENTRIES);
} acl_protocol SEC(".maps");