bpf: selftests add sockmap tests
authorJohn Fastabend <john.fastabend@gmail.com>
Wed, 16 Aug 2017 05:34:22 +0000 (22:34 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Aug 2017 18:27:53 +0000 (11:27 -0700)
This generates a set of sockets, attaches BPF programs, and sends some
simple traffic using basic send/recv pattern. Additionally, we do a bunch
of negative tests to ensure adding/removing socks out of the sockmap fail
correctly.

Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/sockmap_parse_prog.c [new file with mode: 0644]
tools/testing/selftests/bpf/sockmap_verdict_prog.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_maps.c
tools/testing/selftests/bpf/test_progs.c

index 1a2c07e..1cc3ea0 100644 (file)
@@ -1744,3 +1744,32 @@ long libbpf_get_error(const void *ptr)
                return PTR_ERR(ptr);
        return 0;
 }
+
+int bpf_prog_load(const char *file, enum bpf_prog_type type,
+                 struct bpf_object **pobj, int *prog_fd)
+{
+       struct bpf_program *prog;
+       struct bpf_object *obj;
+       int err;
+
+       obj = bpf_object__open(file);
+       if (IS_ERR(obj))
+               return -ENOENT;
+
+       prog = bpf_program__next(NULL, obj);
+       if (!prog) {
+               bpf_object__close(obj);
+               return -ENOENT;
+       }
+
+       bpf_program__set_type(prog, type);
+       err = bpf_object__load(obj);
+       if (err) {
+               bpf_object__close(obj);
+               return -EINVAL;
+       }
+
+       *pobj = obj;
+       *prog_fd = bpf_program__fd(prog);
+       return 0;
+}
index 32c7252..7959086 100644 (file)
@@ -243,4 +243,6 @@ int bpf_map__pin(struct bpf_map *map, const char *path);
 
 long libbpf_get_error(const void *ptr);
 
+int bpf_prog_load(const char *file, enum bpf_prog_type type,
+                 struct bpf_object **pobj, int *prog_fd);
 #endif
index 3c2e67d..f4b23d6 100644 (file)
@@ -15,7 +15,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
        test_align
 
 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
-       test_pkt_md_access.o test_xdp_redirect.o
+       test_pkt_md_access.o test_xdp_redirect.o sockmap_parse_prog.o sockmap_verdict_prog.o
 
 TEST_PROGS := test_kmod.sh test_xdp_redirect.sh
 
diff --git a/tools/testing/selftests/bpf/sockmap_parse_prog.c b/tools/testing/selftests/bpf/sockmap_parse_prog.c
new file mode 100644 (file)
index 0000000..8b54531
--- /dev/null
@@ -0,0 +1,38 @@
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+#include "bpf_util.h"
+#include "bpf_endian.h"
+
+int _version SEC("version") = 1;
+
+#define bpf_printk(fmt, ...)                                   \
+({                                                             \
+              char ____fmt[] = fmt;                            \
+              bpf_trace_printk(____fmt, sizeof(____fmt),       \
+                               ##__VA_ARGS__);                 \
+})
+
+SEC("sk_skb1")
+int bpf_prog1(struct __sk_buff *skb)
+{
+       void *data_end = (void *)(long) skb->data_end;
+       void *data = (void *)(long) skb->data;
+       __u32 lport = skb->local_port;
+       __u32 rport = skb->remote_port;
+       char *d = data;
+
+       if (data + 8 > data_end)
+               return skb->len;
+
+       /* This write/read is a bit pointless but tests the verifier and
+        * strparser handler for read/write pkt data and access into sk
+        * fields.
+        */
+       d[0] = 1;
+
+       bpf_printk("data[0] = (%u): local_port %i remote %i\n",
+                  d[0], lport, bpf_ntohl(rport));
+       return skb->len;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/sockmap_verdict_prog.c
new file mode 100644 (file)
index 0000000..d5f9447
--- /dev/null
@@ -0,0 +1,48 @@
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+#include "bpf_util.h"
+#include "bpf_endian.h"
+
+int _version SEC("version") = 1;
+
+#define bpf_printk(fmt, ...)                                   \
+({                                                             \
+              char ____fmt[] = fmt;                            \
+              bpf_trace_printk(____fmt, sizeof(____fmt),       \
+                               ##__VA_ARGS__);                 \
+})
+
+struct bpf_map_def SEC("maps") sock_map = {
+       .type = BPF_MAP_TYPE_SOCKMAP,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 20,
+};
+
+SEC("sk_skb2")
+int bpf_prog2(struct __sk_buff *skb)
+{
+       void *data_end = (void *)(long) skb->data_end;
+       void *data = (void *)(long) skb->data;
+       __u32 lport = skb->local_port;
+       __u32 rport = skb->remote_port;
+       char *d = data;
+
+       if (data + 8 > data_end)
+               return SK_DROP;
+
+       d[0] = 0xd;
+       d[1] = 0xe;
+       d[2] = 0xa;
+       d[3] = 0xd;
+       d[4] = 0xb;
+       d[5] = 0xe;
+       d[6] = 0xe;
+       d[7] = 0xf;
+
+       bpf_printk("data[0] = (%u): local_port %i remote %i\n",
+                  d[0], lport, bpf_ntohl(rport));
+       return bpf_sk_redirect_map(&sock_map, 5, 0);
+}
+
+char _license[] SEC("license") = "GPL";
index c991ab6..40b2d1f 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/bpf.h>
 
 #include <bpf/bpf.h>
+#include <bpf/libbpf.h>
 #include "bpf_util.h"
 
 static int map_flags;
@@ -453,6 +454,312 @@ static void test_devmap(int task, void *data)
        close(fd);
 }
 
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+#include <linux/err.h>
+#define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o"
+#define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o"
+static void test_sockmap(int task, void *data)
+{
+       int ports[] = {50200, 50201, 50202, 50204};
+       int err, i, fd, sfd[6] = {0xdeadbeef};
+       char buf[] = "hello sockmap user\n";
+       int one = 1, map_fd, s, sc, rc;
+       int parse_prog, verdict_prog;
+       struct bpf_map *bpf_map;
+       struct sockaddr_in addr;
+       struct bpf_object *obj;
+       struct timeval to;
+       __u32 key, value;
+       fd_set w;
+
+       /* Create some sockets to use with sockmap */
+       for (i = 0; i < 2; i++) {
+               sfd[i] = socket(AF_INET, SOCK_STREAM, 0);
+               if (sfd[i] < 0)
+                       goto out;
+               err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR,
+                                (char *)&one, sizeof(one));
+               if (err) {
+                       printf("failed to setsockopt\n");
+                       goto out;
+               }
+               err = ioctl(sfd[i], FIONBIO, (char *)&one);
+               if (err < 0) {
+                       printf("failed to ioctl\n");
+                       goto out;
+               }
+               memset(&addr, 0, sizeof(struct sockaddr_in));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+               addr.sin_port = htons(ports[i]);
+               err = bind(sfd[i], (struct sockaddr *)&addr, sizeof(addr));
+               if (err < 0) {
+                       printf("failed to bind: err %i: %i:%i\n",
+                              err, i, sfd[i]);
+                       goto out;
+               }
+               err = listen(sfd[i], 32);
+               if (err < 0) {
+                       printf("failed to listeen\n");
+                       goto out;
+               }
+       }
+
+       for (i = 2; i < 4; i++) {
+               sfd[i] = socket(AF_INET, SOCK_STREAM, 0);
+               if (sfd[i] < 0)
+                       goto out;
+               err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR,
+                                (char *)&one, sizeof(one));
+               if (err) {
+                       printf("set sock opt\n");
+                       goto out;
+               }
+               memset(&addr, 0, sizeof(struct sockaddr_in));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+               addr.sin_port = htons(ports[i - 2]);
+               err = connect(sfd[i], (struct sockaddr *)&addr, sizeof(addr));
+               if (err) {
+                       printf("failed to conenct\n");
+                       goto out;
+               }
+       }
+
+
+       for (i = 4; i < 6; i++) {
+               sfd[i] = accept(sfd[i - 4], NULL, NULL);
+               if (sfd[i] < 0) {
+                       printf("accept failed\n");
+                       goto out;
+               }
+       }
+
+       /* Test sockmap with connected sockets */
+       fd = bpf_create_map(BPF_MAP_TYPE_SOCKMAP,
+                           sizeof(key), sizeof(value),
+                           6, 0);
+       if (fd < 0) {
+               printf("Failed to create sockmap %i\n", fd);
+               goto out_sockmap;
+       }
+
+       /* Nothing attached so these should fail */
+       for (i = 0; i < 6; i++) {
+               err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
+               if (!err) {
+                       printf("Failed invalid update sockmap '%i:%i'\n",
+                              i, sfd[i]);
+                       goto out_sockmap;
+               }
+       }
+
+       /* Test attaching bad fds */
+       err = __bpf_prog_attach(-1, -2, fd, BPF_CGROUP_SMAP_INGRESS, 0);
+       if (!err) {
+               printf("Failed invalid prog attach\n");
+               goto out_sockmap;
+       }
+
+       /* Load SK_SKB program and Attach */
+       err = bpf_prog_load(SOCKMAP_PARSE_PROG,
+                           BPF_PROG_TYPE_SK_SKB, &obj, &parse_prog);
+       if (err) {
+               printf("Failed to load SK_SKB parse prog\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_prog_load(SOCKMAP_VERDICT_PROG,
+                           BPF_PROG_TYPE_SK_SKB, &obj, &verdict_prog);
+       if (err) {
+               printf("Failed to load SK_SKB verdict prog\n");
+               goto out_sockmap;
+       }
+
+       bpf_map = bpf_object__find_map_by_name(obj, "sock_map");
+       if (IS_ERR(bpf_map)) {
+               printf("Failed to load map from verdict prog\n");
+               goto out_sockmap;
+       }
+
+       map_fd = bpf_map__fd(bpf_map);
+       if (map_fd < 0) {
+               printf("Failed to get map fd\n");
+               goto out_sockmap;
+       }
+
+       err = __bpf_prog_attach(parse_prog, verdict_prog, map_fd,
+                               BPF_CGROUP_SMAP_INGRESS, 0);
+       if (err) {
+               printf("Failed bpf prog attach\n");
+               goto out_sockmap;
+       }
+
+       /* Test map update elem */
+       for (i = 0; i < 6; i++) {
+               err = bpf_map_update_elem(map_fd, &i, &sfd[i], BPF_ANY);
+               if (err) {
+                       printf("Failed map_fd update sockmap %i '%i:%i'\n",
+                              err, i, sfd[i]);
+                       goto out_sockmap;
+               }
+       }
+
+       /* Test map delete elem and remove send/recv sockets */
+       for (i = 2; i < 4; i++) {
+               err = bpf_map_delete_elem(map_fd, &i);
+               if (err) {
+                       printf("Failed delete  sockmap %i '%i:%i'\n",
+                              err, i, sfd[i]);
+                       goto out_sockmap;
+               }
+       }
+
+       /* Test map send/recv */
+       sc = send(sfd[2], buf, 10, 0);
+       if (sc < 0) {
+               printf("Failed sockmap send\n");
+               goto out_sockmap;
+       }
+
+       FD_ZERO(&w);
+       FD_SET(sfd[3], &w);
+       to.tv_sec = 1;
+       to.tv_usec = 0;
+       s = select(sfd[3] + 1, &w, NULL, NULL, &to);
+       if (s == -1) {
+               perror("Failed sockmap select()");
+               goto out_sockmap;
+       } else if (!s) {
+               printf("Failed sockmap unexpected timeout\n");
+               goto out_sockmap;
+       }
+
+       if (!FD_ISSET(sfd[3], &w)) {
+               printf("Failed sockmap select/recv\n");
+               goto out_sockmap;
+       }
+
+       rc = recv(sfd[3], buf, sizeof(buf), 0);
+       if (rc < 0) {
+               printf("Failed sockmap recv\n");
+               goto out_sockmap;
+       }
+
+       /* Delete the reset of the elems include some NULL elems */
+       for (i = 0; i < 6; i++) {
+               err = bpf_map_delete_elem(map_fd, &i);
+               if (err && (i == 0 || i == 1 || i >= 4)) {
+                       printf("Failed delete  sockmap %i '%i:%i'\n",
+                              err, i, sfd[i]);
+                       goto out_sockmap;
+               } else if (!err && (i == 2 || i == 3)) {
+                       printf("Failed null delete sockmap %i '%i:%i'\n",
+                              err, i, sfd[i]);
+                       goto out_sockmap;
+               }
+       }
+
+       /* Test having multiple SMAPs open and active on same fds */
+       err = __bpf_prog_attach(parse_prog, verdict_prog, fd,
+                               BPF_CGROUP_SMAP_INGRESS, 0);
+       if (err) {
+               printf("Failed fd bpf prog attach\n");
+               goto out_sockmap;
+       }
+
+       for (i = 0; i < 6; i++) {
+               err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
+               if (err) {
+                       printf("Failed fd update sockmap %i '%i:%i'\n",
+                              err, i, sfd[i]);
+                       goto out_sockmap;
+               }
+       }
+
+       /* Test duplicate socket add of NOEXIST, ANY and EXIST */
+       i = 0;
+       err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST);
+       if (!err) {
+               printf("Failed BPF_NOEXIST create\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
+       if (err) {
+               printf("Failed sockmap update BPF_ANY\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST);
+       if (err) {
+               printf("Failed sockmap update BPF_EXIST\n");
+               goto out_sockmap;
+       }
+
+       /* The above were pushing fd into same slot try different slot now */
+       i = 2;
+       err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST);
+       if (!err) {
+               printf("Failed BPF_NOEXIST create\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
+       if (err) {
+               printf("Failed sockmap update BPF_ANY\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST);
+       if (err) {
+               printf("Failed sockmap update BPF_EXIST\n");
+               goto out_sockmap;
+       }
+
+       /* Try pushing fd into different map, this is not allowed at the
+        * moment. Which programs would we use?
+        */
+       err = bpf_map_update_elem(map_fd, &i, &sfd[i], BPF_NOEXIST);
+       if (!err) {
+               printf("Failed BPF_NOEXIST create\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_map_update_elem(map_fd, &i, &sfd[i], BPF_ANY);
+       if (!err) {
+               printf("Failed sockmap update BPF_ANY\n");
+               goto out_sockmap;
+       }
+
+       err = bpf_map_update_elem(map_fd, &i, &sfd[i], BPF_EXIST);
+       if (!err) {
+               printf("Failed sockmap update BPF_EXIST\n");
+               goto out_sockmap;
+       }
+
+       /* Test map close sockets */
+       for (i = 0; i < 6; i++)
+               close(sfd[i]);
+       close(fd);
+       close(map_fd);
+       bpf_object__close(obj);
+       return;
+out:
+       for (i = 0; i < 6; i++)
+               close(sfd[i]);
+       printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno));
+       exit(1);
+out_sockmap:
+       for (i = 0; i < 6; i++)
+               close(sfd[i]);
+       close(fd);
+       exit(1);
+}
+
 #define MAP_SIZE (32 * 1024)
 
 static void test_map_large(void)
@@ -621,6 +928,7 @@ static void run_all_tests(void)
        test_arraymap_percpu_many_keys();
 
        test_devmap(0, NULL);
+       test_sockmap(0, NULL);
 
        test_map_large();
        test_map_parallel();
index 1f7dd35..1cb0378 100644 (file)
@@ -75,39 +75,6 @@ static struct {
        __ret;                                                          \
 })
 
-static int bpf_prog_load(const char *file, enum bpf_prog_type type,
-                        struct bpf_object **pobj, int *prog_fd)
-{
-       struct bpf_program *prog;
-       struct bpf_object *obj;
-       int err;
-
-       obj = bpf_object__open(file);
-       if (IS_ERR(obj)) {
-               error_cnt++;
-               return -ENOENT;
-       }
-
-       prog = bpf_program__next(NULL, obj);
-       if (!prog) {
-               bpf_object__close(obj);
-               error_cnt++;
-               return -ENOENT;
-       }
-
-       bpf_program__set_type(prog, type);
-       err = bpf_object__load(obj);
-       if (err) {
-               bpf_object__close(obj);
-               error_cnt++;
-               return -EINVAL;
-       }
-
-       *pobj = obj;
-       *prog_fd = bpf_program__fd(prog);
-       return 0;
-}
-
 static int bpf_find_map(const char *test, struct bpf_object *obj,
                        const char *name)
 {
@@ -130,8 +97,10 @@ static void test_pkt_access(void)
        int err, prog_fd;
 
        err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
-       if (err)
+       if (err) {
+               error_cnt++;
                return;
+       }
 
        err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4),
                                NULL, NULL, &retval, &duration);
@@ -162,8 +131,10 @@ static void test_xdp(void)
        int err, prog_fd, map_fd;
 
        err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
-       if (err)
+       if (err) {
+               error_cnt++;
                return;
+       }
 
        map_fd = bpf_find_map(__func__, obj, "vip2tnl");
        if (map_fd < 0)
@@ -223,8 +194,10 @@ static void test_l4lb(void)
        u32 *magic = (u32 *)buf;
 
        err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
-       if (err)
+       if (err) {
+               error_cnt++;
                return;
+       }
 
        map_fd = bpf_find_map(__func__, obj, "vip_map");
        if (map_fd < 0)
@@ -280,8 +253,10 @@ static void test_tcp_estats(void)
 
        err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
        CHECK(err, "", "err %d errno %d\n", err, errno);
-       if (err)
+       if (err) {
+               error_cnt++;
                return;
+       }
 
        bpf_object__close(obj);
 }
@@ -336,6 +311,8 @@ static void test_bpf_obj_id(void)
                /* test_obj_id.o is a dumb prog. It should never fail
                 * to load.
                 */
+               if (err)
+                       error_cnt++;
                assert(!err);
 
                /* Check getting prog info */
@@ -496,8 +473,10 @@ static void test_pkt_md_access(void)
        int err, prog_fd;
 
        err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
-       if (err)
+       if (err) {
+               error_cnt++;
                return;
+       }
 
        err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4),
                                NULL, NULL, &retval, &duration);