bpf: Test for bpf ID
authorMartin KaFai Lau <kafai@fb.com>
Mon, 5 Jun 2017 19:15:53 +0000 (12:15 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 6 Jun 2017 19:41:24 +0000 (15:41 -0400)
Add test to exercise the bpf_prog/map id generation,
bpf_(prog|map)_get_next_id(), bpf_(prog|map)_get_fd_by_id() and
bpf_get_obj_info_by_fd().

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@fb.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/include/uapi/linux/bpf.h
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/test_obj_id.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_progs.c

index e78aece036280292cac154f84349922420cd2800..9b2c10b45733e4dc66d601ac2ea52fed65f3752d 100644 (file)
@@ -82,6 +82,11 @@ enum bpf_cmd {
        BPF_PROG_ATTACH,
        BPF_PROG_DETACH,
        BPF_PROG_TEST_RUN,
+       BPF_PROG_GET_NEXT_ID,
+       BPF_MAP_GET_NEXT_ID,
+       BPF_PROG_GET_FD_BY_ID,
+       BPF_MAP_GET_FD_BY_ID,
+       BPF_OBJ_GET_INFO_BY_FD,
 };
 
 enum bpf_map_type {
@@ -209,6 +214,21 @@ union bpf_attr {
                __u32           repeat;
                __u32           duration;
        } test;
+
+       struct { /* anonymous struct used by BPF_*_GET_*_ID */
+               union {
+                       __u32           start_id;
+                       __u32           prog_id;
+                       __u32           map_id;
+               };
+               __u32           next_id;
+       };
+
+       struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
+               __u32           bpf_fd;
+               __u32           info_len;
+               __aligned_u64   info;
+       } info;
 } __attribute__((aligned(8)));
 
 /* BPF helper function descriptions:
@@ -673,4 +693,25 @@ struct xdp_md {
        __u32 data_end;
 };
 
+#define BPF_TAG_SIZE   8
+
+struct bpf_prog_info {
+       __u32 type;
+       __u32 id;
+       __u8  tag[BPF_TAG_SIZE];
+       __u32 jited_prog_len;
+       __u32 xlated_prog_len;
+       __aligned_u64 jited_prog_insns;
+       __aligned_u64 xlated_prog_insns;
+} __attribute__((aligned(8)));
+
+struct bpf_map_info {
+       __u32 type;
+       __u32 id;
+       __u32 key_size;
+       __u32 value_size;
+       __u32 max_entries;
+       __u32 map_flags;
+} __attribute__((aligned(8)));
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 6e178987af8e3f45d63da349e38c39b4e4cd6d84..7e0405e1651d23ea80b2f000495e199a561eca72 100644 (file)
@@ -257,3 +257,71 @@ int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
                *duration = attr.test.duration;
        return ret;
 }
+
+int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
+{
+       union bpf_attr attr;
+       int err;
+
+       bzero(&attr, sizeof(attr));
+       attr.start_id = start_id;
+
+       err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
+       if (!err)
+               *next_id = attr.next_id;
+
+       return err;
+}
+
+int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
+{
+       union bpf_attr attr;
+       int err;
+
+       bzero(&attr, sizeof(attr));
+       attr.start_id = start_id;
+
+       err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
+       if (!err)
+               *next_id = attr.next_id;
+
+       return err;
+}
+
+int bpf_prog_get_fd_by_id(__u32 id)
+{
+       union bpf_attr attr;
+
+       bzero(&attr, sizeof(attr));
+       attr.prog_id = id;
+
+       return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_map_get_fd_by_id(__u32 id)
+{
+       union bpf_attr attr;
+
+       bzero(&attr, sizeof(attr));
+       attr.map_id = id;
+
+       return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
+{
+       union bpf_attr attr;
+       int err;
+
+       bzero(&attr, sizeof(attr));
+       bzero(info, *info_len);
+       attr.info.bpf_fd = prog_fd;
+       attr.info.info_len = *info_len;
+       attr.info.info = ptr_to_u64(info);
+
+       err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
+       if (!err)
+               *info_len = attr.info.info_len;
+
+       return err;
+}
index 972bd8333eb72e982420abeac9f8ceff5a4ed1a3..16de44a14b48706886ef77e37abed2bafa8e8fc1 100644 (file)
@@ -54,5 +54,10 @@ int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
 int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
                      void *data_out, __u32 *size_out, __u32 *retval,
                      __u32 *duration);
+int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
+int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
+int bpf_prog_get_fd_by_id(__u32 id);
+int bpf_map_get_fd_by_id(__u32 id);
+int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
 
 #endif
index f389b02d43a004e90aed9acd68148875af3beba2..9f0e07ba5334b1412668e2fe4a43779d5e857060 100644 (file)
@@ -14,7 +14,7 @@ LDLIBS += -lcap -lelf
 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
        test_align
 
-TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o
+TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o
 
 TEST_PROGS := test_kmod.sh
 
diff --git a/tools/testing/selftests/bpf/test_obj_id.c b/tools/testing/selftests/bpf/test_obj_id.c
new file mode 100644 (file)
index 0000000..d8723aa
--- /dev/null
@@ -0,0 +1,35 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <linux/pkt_cls.h>
+#include "bpf_helpers.h"
+
+/* It is a dumb bpf program such that it must have no
+ * issue to be loaded since testing the verifier is
+ * not the focus here.
+ */
+
+int _version SEC("version") = 1;
+
+struct bpf_map_def SEC("maps") test_map_id = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(__u64),
+       .max_entries = 1,
+};
+
+SEC("test_prog_id")
+int test_prog_id(struct __sk_buff *skb)
+{
+       __u32 key = 0;
+       __u64 *value;
+
+       value = bpf_map_lookup_elem(&test_map_id, &key);
+
+       return TC_ACT_OK;
+}
index b59f5ed4ae40d36bf9755fa844e63597c2a6f2c1..8189bfc7e2773869f794408aa41be086b19aa6e6 100644 (file)
@@ -22,6 +22,8 @@ typedef __u16 __sum16;
 
 #include <sys/wait.h>
 #include <sys/resource.h>
+#include <sys/types.h>
+#include <pwd.h>
 
 #include <linux/bpf.h>
 #include <linux/err.h>
@@ -70,6 +72,7 @@ static struct {
                pass_cnt++;                                             \
                printf("%s:PASS:%s %d nsec\n", __func__, tag, duration);\
        }                                                               \
+       __ret;                                                          \
 })
 
 static int bpf_prog_load(const char *file, enum bpf_prog_type type,
@@ -283,6 +286,193 @@ static void test_tcp_estats(void)
        bpf_object__close(obj);
 }
 
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+       return (__u64) (unsigned long) ptr;
+}
+
+static void test_bpf_obj_id(void)
+{
+       const __u64 array_magic_value = 0xfaceb00c;
+       const __u32 array_key = 0;
+       const int nr_iters = 2;
+       const char *file = "./test_obj_id.o";
+
+       struct bpf_object *objs[nr_iters];
+       int prog_fds[nr_iters], map_fds[nr_iters];
+       /* +1 to test for the info_len returned by kernel */
+       struct bpf_prog_info prog_infos[nr_iters + 1];
+       struct bpf_map_info map_infos[nr_iters + 1];
+       char jited_insns[128], xlated_insns[128];
+       __u32 i, next_id, info_len, nr_id_found, duration = 0;
+       int err = 0;
+       __u64 array_value;
+
+       err = bpf_prog_get_fd_by_id(0);
+       CHECK(err >= 0 || errno != ENOENT,
+             "get-fd-by-notexist-prog-id", "err %d errno %d\n", err, errno);
+
+       err = bpf_map_get_fd_by_id(0);
+       CHECK(err >= 0 || errno != ENOENT,
+             "get-fd-by-notexist-map-id", "err %d errno %d\n", err, errno);
+
+       for (i = 0; i < nr_iters; i++)
+               objs[i] = NULL;
+
+       /* Check bpf_obj_get_info_by_fd() */
+       for (i = 0; i < nr_iters; i++) {
+               err = bpf_prog_load(file, BPF_PROG_TYPE_SOCKET_FILTER,
+                                   &objs[i], &prog_fds[i]);
+               /* test_obj_id.o is a dumb prog. It should never fail
+                * to load.
+                */
+               assert(!err);
+
+               /* Check getting prog info */
+               info_len = sizeof(struct bpf_prog_info) * 2;
+               prog_infos[i].jited_prog_insns = ptr_to_u64(jited_insns);
+               prog_infos[i].jited_prog_len = sizeof(jited_insns);
+               prog_infos[i].xlated_prog_insns = ptr_to_u64(xlated_insns);
+               prog_infos[i].xlated_prog_len = sizeof(xlated_insns);
+               err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i],
+                                            &info_len);
+               if (CHECK(err ||
+                         prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
+                         info_len != sizeof(struct bpf_prog_info) ||
+                         !prog_infos[i].jited_prog_len ||
+                         !prog_infos[i].xlated_prog_len,
+                         "get-prog-info(fd)",
+                         "err %d errno %d i %d type %d(%d) info_len %u(%lu) jited_prog_len %u xlated_prog_len %u\n",
+                         err, errno, i,
+                         prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
+                         info_len, sizeof(struct bpf_prog_info),
+                         prog_infos[i].jited_prog_len,
+                         prog_infos[i].xlated_prog_len))
+                       goto done;
+
+               map_fds[i] = bpf_find_map(__func__, objs[i], "test_map_id");
+               assert(map_fds[i] >= 0);
+               err = bpf_map_update_elem(map_fds[i], &array_key,
+                                         &array_magic_value, 0);
+               assert(!err);
+
+               /* Check getting map info */
+               info_len = sizeof(struct bpf_map_info) * 2;
+               err = bpf_obj_get_info_by_fd(map_fds[i], &map_infos[i],
+                                            &info_len);
+               if (CHECK(err ||
+                         map_infos[i].type != BPF_MAP_TYPE_ARRAY ||
+                         map_infos[i].key_size != sizeof(__u32) ||
+                         map_infos[i].value_size != sizeof(__u64) ||
+                         map_infos[i].max_entries != 1 ||
+                         map_infos[i].map_flags != 0 ||
+                         info_len != sizeof(struct bpf_map_info),
+                         "get-map-info(fd)",
+                         "err %d errno %d type %d(%d) info_len %u(%lu) key_size %u value_size %u max_entries %u map_flags %X\n",
+                         err, errno,
+                         map_infos[i].type, BPF_MAP_TYPE_ARRAY,
+                         info_len, sizeof(struct bpf_map_info),
+                         map_infos[i].key_size,
+                         map_infos[i].value_size,
+                         map_infos[i].max_entries,
+                         map_infos[i].map_flags))
+                       goto done;
+       }
+
+       /* Check bpf_prog_get_next_id() */
+       nr_id_found = 0;
+       next_id = 0;
+       while (!bpf_prog_get_next_id(next_id, &next_id)) {
+               struct bpf_prog_info prog_info;
+               int prog_fd;
+
+               info_len = sizeof(prog_info);
+
+               prog_fd = bpf_prog_get_fd_by_id(next_id);
+               if (prog_fd < 0 && errno == ENOENT)
+                       /* The bpf_prog is in the dead row */
+                       continue;
+               if (CHECK(prog_fd < 0, "get-prog-fd(next_id)",
+                         "prog_fd %d next_id %d errno %d\n",
+                         prog_fd, next_id, errno))
+                       break;
+
+               for (i = 0; i < nr_iters; i++)
+                       if (prog_infos[i].id == next_id)
+                               break;
+
+               if (i == nr_iters)
+                       continue;
+
+               nr_id_found++;
+
+               err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
+               CHECK(err || info_len != sizeof(struct bpf_prog_info) ||
+                     memcmp(&prog_info, &prog_infos[i], info_len),
+                     "get-prog-info(next_id->fd)",
+                     "err %d errno %d info_len %u(%lu) memcmp %d\n",
+                     err, errno, info_len, sizeof(struct bpf_prog_info),
+                     memcmp(&prog_info, &prog_infos[i], info_len));
+
+               close(prog_fd);
+       }
+       CHECK(nr_id_found != nr_iters,
+             "check total prog id found by get_next_id",
+             "nr_id_found %u(%u)\n",
+             nr_id_found, nr_iters);
+
+       /* Check bpf_map_get_next_id() */
+       nr_id_found = 0;
+       next_id = 0;
+       while (!bpf_map_get_next_id(next_id, &next_id)) {
+               struct bpf_map_info map_info;
+               int map_fd;
+
+               info_len = sizeof(map_info);
+
+               map_fd = bpf_map_get_fd_by_id(next_id);
+               if (map_fd < 0 && errno == ENOENT)
+                       /* The bpf_map is in the dead row */
+                       continue;
+               if (CHECK(map_fd < 0, "get-map-fd(next_id)",
+                         "map_fd %d next_id %u errno %d\n",
+                         map_fd, next_id, errno))
+                       break;
+
+               for (i = 0; i < nr_iters; i++)
+                       if (map_infos[i].id == next_id)
+                               break;
+
+               if (i == nr_iters)
+                       continue;
+
+               nr_id_found++;
+
+               err = bpf_map_lookup_elem(map_fd, &array_key, &array_value);
+               assert(!err);
+
+               err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
+               CHECK(err || info_len != sizeof(struct bpf_map_info) ||
+                     memcmp(&map_info, &map_infos[i], info_len) ||
+                     array_value != array_magic_value,
+                     "check get-map-info(next_id->fd)",
+                     "err %d errno %d info_len %u(%lu) memcmp %d array_value %llu(%llu)\n",
+                     err, errno, info_len, sizeof(struct bpf_map_info),
+                     memcmp(&map_info, &map_infos[i], info_len),
+                     array_value, array_magic_value);
+
+               close(map_fd);
+       }
+       CHECK(nr_id_found != nr_iters,
+             "check total map id found by get_next_id",
+             "nr_id_found %u(%u)\n",
+             nr_id_found, nr_iters);
+
+done:
+       for (i = 0; i < nr_iters; i++)
+               bpf_object__close(objs[i]);
+}
+
 int main(void)
 {
        struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
@@ -293,6 +483,7 @@ int main(void)
        test_xdp();
        test_l4lb();
        test_tcp_estats();
+       test_bpf_obj_id();
 
        printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
        return 0;