From: Toke Høiland-Jørgensen <t...@redhat.com>

This adds new self tests for the XDP chain call functionality.

Signed-off-by: Toke Høiland-Jørgensen <t...@redhat.com>
---
 tools/testing/selftests/bpf/.gitignore        |    1 
 tools/testing/selftests/bpf/Makefile          |    3 
 tools/testing/selftests/bpf/progs/xdp_dummy.c |    6 +
 tools/testing/selftests/bpf/test_maps.c       |   45 ++++
 tools/testing/selftests/bpf/test_xdp_chain.sh |   77 +++++++
 tools/testing/selftests/bpf/xdp_chain.c       |  271 +++++++++++++++++++++++++
 6 files changed, 402 insertions(+), 1 deletion(-)
 create mode 100755 tools/testing/selftests/bpf/test_xdp_chain.sh
 create mode 100644 tools/testing/selftests/bpf/xdp_chain.c

diff --git a/tools/testing/selftests/bpf/.gitignore 
b/tools/testing/selftests/bpf/.gitignore
index 7470327edcfe..e9d2d765cc8f 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -39,3 +39,4 @@ libbpf.so.*
 test_hashmap
 test_btf_dump
 xdping
+xdp_chain
diff --git a/tools/testing/selftests/bpf/Makefile 
b/tools/testing/selftests/bpf/Makefile
index 6889c19a628c..97e8f6ae4a15 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -29,7 +29,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps 
test_lru_map test_lpm_map test
        test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
        test_cgroup_storage test_select_reuseport test_section_names \
        test_netcnt test_tcpnotify_user test_sock_fields test_sysctl 
test_hashmap \
-       test_btf_dump test_cgroup_attach xdping
+       test_btf_dump test_cgroup_attach xdping xdp_chain
 
 BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
 TEST_GEN_FILES = $(BPF_OBJ_FILES)
@@ -71,6 +71,7 @@ TEST_PROGS := test_kmod.sh \
        test_tc_tunnel.sh \
        test_tc_edt.sh \
        test_xdping.sh \
+       test_xdp_chain.sh \
        test_bpftool_build.sh
 
 TEST_PROGS_EXTENDED := with_addr.sh \
diff --git a/tools/testing/selftests/bpf/progs/xdp_dummy.c 
b/tools/testing/selftests/bpf/progs/xdp_dummy.c
index 43b0ef1001ed..454a1f0763a1 100644
--- a/tools/testing/selftests/bpf/progs/xdp_dummy.c
+++ b/tools/testing/selftests/bpf/progs/xdp_dummy.c
@@ -10,4 +10,10 @@ int xdp_dummy_prog(struct xdp_md *ctx)
        return XDP_PASS;
 }
 
+SEC("xdp_drop")
+int xdp_drop_prog(struct xdp_md *ctx)
+{
+       return XDP_DROP;
+}
+
 char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_maps.c 
b/tools/testing/selftests/bpf/test_maps.c
index e1f1becda529..44f2f8548a24 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -523,6 +523,50 @@ static void test_devmap_hash(unsigned int task, void *data)
        close(fd);
 }
 
+static void test_xdp_chain_map(unsigned int task, void *data)
+{
+       const char *file = "./xdp_dummy.o";
+       struct xdp_chain_acts acts = {};
+       struct bpf_object *obj;
+       int map_fd, prog_fd;
+       __u32 key = 0;
+       int err;
+
+       map_fd = bpf_create_map(BPF_MAP_TYPE_XDP_CHAIN, sizeof(key), 
sizeof(acts),
+                           2, 0);
+       if (map_fd < 0) {
+               printf("Failed to create xdp_chain map '%s'!\n", 
strerror(errno));
+               exit(1);
+       }
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
+       if (err < 0) {
+               printf("Failed to load dummy prog: '%s'!\n", strerror(errno));
+               exit(1);
+       }
+
+       /* Try inserting NULL key/value - should fail */
+       assert(bpf_map_update_elem(map_fd, &key, &acts, 0) == -1 && errno == 
EINVAL);
+
+       /* Try inserting NULL value - should fail */
+       key = 1;
+       assert(bpf_map_update_elem(map_fd, &key, &acts, 0) == -1 && errno == 
EINVAL);
+
+       /* Try a real insert - should succeed */
+       acts.wildcard_act = prog_fd;
+       assert(bpf_map_update_elem(map_fd, &key, &acts, 0) == 0);
+
+       /* Replace with full table */
+       acts.drop_act = acts.pass_act = acts.tx_act = acts.redirect_act = 
prog_fd;
+       assert(bpf_map_update_elem(map_fd, &key, &acts, 0) == 0);
+
+       /* Try deleting element */
+       assert(bpf_map_delete_elem(map_fd, &key) == 0);
+
+       bpf_object__close(obj);
+       close(map_fd);
+}
+
 static void test_queuemap(unsigned int task, void *data)
 {
        const int MAP_SIZE = 32;
@@ -1700,6 +1744,7 @@ static void run_all_tests(void)
 
        test_devmap(0, NULL);
        test_devmap_hash(0, NULL);
+       test_xdp_chain_map(0, NULL);
        test_sockmap(0, NULL);
 
        test_map_large();
diff --git a/tools/testing/selftests/bpf/test_xdp_chain.sh 
b/tools/testing/selftests/bpf/test_xdp_chain.sh
new file mode 100755
index 000000000000..3997655d4e45
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_xdp_chain.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# xdp_chain tests
+#   Here we setup and teardown configuration required to run
+#   xdp_chain, exercising its options.
+#
+#   Setup is similar to xdping tests.
+#
+# Topology:
+# ---------
+#     root namespace   |     tc_ns0 namespace
+#                      |
+#      ----------      |     ----------
+#      |  veth1  | --------- |  veth0  |
+#      ----------    peer    ----------
+#
+# Device Configuration
+# --------------------
+# Root namespace with BPF
+# Device names and addresses:
+#      veth1 IP: 10.1.1.200
+#
+# Namespace tc_ns0 with BPF
+# Device names and addresses:
+#       veth0 IPv4: 10.1.1.100
+#      xdp_chain binary run inside this
+#
+
+readonly TARGET_IP="10.1.1.100"
+readonly TARGET_NS="xdp_ns0"
+
+readonly LOCAL_IP="10.1.1.200"
+
+setup()
+{
+       ip netns add $TARGET_NS
+       ip link add veth0 type veth peer name veth1
+       ip link set veth0 netns $TARGET_NS
+       ip netns exec $TARGET_NS ip addr add ${TARGET_IP}/24 dev veth0
+       ip addr add ${LOCAL_IP}/24 dev veth1
+       ip netns exec $TARGET_NS ip link set veth0 up
+       ip link set veth1 up
+}
+
+cleanup()
+{
+       set +e
+       ip netns delete $TARGET_NS 2>/dev/null
+       ip link del veth1 2>/dev/null
+}
+
+die()
+{
+        echo "$@" >&2
+        exit 1
+}
+
+test()
+{
+       args="$1"
+
+       ip netns exec $TARGET_NS ./xdp_chain $args || die "XDP chain test error"
+}
+
+set -e
+
+server_pid=0
+
+trap cleanup EXIT
+
+setup
+
+test "-I veth0 -S $LOCAL_IP"
+
+echo "OK. All tests passed"
+exit 0
diff --git a/tools/testing/selftests/bpf/xdp_chain.c 
b/tools/testing/selftests/bpf/xdp_chain.c
new file mode 100644
index 000000000000..11f78bb1c2e7
--- /dev/null
+++ b/tools/testing/selftests/bpf/xdp_chain.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
+
+#include <linux/bpf.h>
+#include <linux/if_link.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/resource.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "bpf/bpf.h"
+#include "bpf/libbpf.h"
+
+static int ifindex;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static char *dest = NULL, *ifname = NULL;
+
+static void cleanup(int sig)
+{
+       int ret;
+
+       fprintf(stderr, "  Cleaning up\n");
+       if ((ret = bpf_set_link_xdp_chain(ifindex, -1, -1, xdp_flags)))
+               fprintf(stderr, "Warning: Unable to clear XDP prog: %s\n",
+                       strerror(-ret));
+       if (sig)
+               exit(1);
+}
+
+static void show_usage(const char *prog)
+{
+       fprintf(stderr,
+               "usage: %s [OPTS] -I interface destination\n\n"
+               "OPTS:\n"
+               "    -I interface               interface name\n"
+               "    -N                 Run in driver mode\n"
+               "    -S                 Run in skb mode\n"
+               "    -p pin_path                path to pin chain call map\n"
+               "    -x                 Exit after setup\n"
+               "    -c                 Cleanup and exit\n",
+               prog);
+}
+
+static int run_ping(bool should_fail, const char *msg)
+{
+       char cmd[256];
+       bool success;
+       int ret;
+
+       snprintf(cmd, sizeof(cmd), "ping -c 1 -W 1 -I %s %s >/dev/null", 
ifname, dest);
+
+       printf("  %s: ", msg);
+
+       ret = system(cmd);
+
+       success = (!!ret == should_fail);
+       printf(success ? "PASS\n" : "FAIL\n");
+
+       return !success;
+}
+
+int main(int argc, char **argv)
+{
+       __u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE;
+       int pass_prog_fd = -1, drop_prog_fd = -1, map_fd = -1;
+       const char *filename = "xdp_dummy.o", *pin_path = NULL;
+       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       struct bpf_program *pass_prog, *drop_prog;
+       __u32 map_key, prog_id, chain_map_id;
+       struct xdp_chain_acts acts = {};
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
+       const char *optstr = "I:NSp:xc";
+       bool setup_only = false, cleanup_only = false;
+       struct bpf_object *obj;
+       int opt, ret = 1;
+
+       while ((opt = getopt(argc, argv, optstr)) != -1) {
+               switch (opt) {
+               case 'I':
+                       ifname = optarg;
+                       ifindex = if_nametoindex(ifname);
+                       if (!ifindex) {
+                               fprintf(stderr, "Could not get interface %s\n",
+                                       ifname);
+                               return 1;
+                       }
+                       break;
+               case 'N':
+                       xdp_flags |= XDP_FLAGS_DRV_MODE;
+                       break;
+               case 'S':
+                       xdp_flags |= XDP_FLAGS_SKB_MODE;
+                       break;
+               case 'x':
+                       setup_only = true;
+                       break;
+               case 'c':
+                       cleanup_only = true;
+                       break;
+               case 'p':
+                       pin_path = optarg;
+                       break;
+               default:
+                       show_usage(basename(argv[0]));
+                       return 1;
+               }
+       }
+
+       if (!ifname) {
+               show_usage(basename(argv[0]));
+               return 1;
+       }
+
+       if (cleanup_only) {
+               if (pin_path)
+                       unlink(pin_path);
+               cleanup(0);
+               return 0;
+       }
+
+       if (!setup_only && optind == argc) {
+               show_usage(basename(argv[0]));
+               return 1;
+       }
+       dest = argv[optind];
+
+       if ((xdp_flags & mode_flags) == mode_flags) {
+               fprintf(stderr, "-N or -S can be specified, not both.\n");
+               show_usage(basename(argv[0]));
+               return 1;
+       }
+
+       if (setrlimit(RLIMIT_MEMLOCK, &r)) {
+               perror("setrlimit(RLIMIT_MEMLOCK)");
+               return 1;
+       }
+
+       if (bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &pass_prog_fd)) {
+               fprintf(stderr, "load of %s failed\n", filename);
+               return 1;
+       }
+
+       pass_prog = bpf_object__find_program_by_title(obj, "xdp_dummy");
+       drop_prog = bpf_object__find_program_by_title(obj, "xdp_drop");
+
+       if (!pass_prog || !drop_prog) {
+               fprintf(stderr, "could not find xdp programs\n");
+               return 1;
+       }
+       pass_prog_fd = bpf_program__fd(pass_prog);
+       drop_prog_fd = bpf_program__fd(drop_prog);
+       if (pass_prog_fd < 0 || drop_prog_fd < 0) {
+               fprintf(stderr, "could not find xdp programs\n");
+               goto done;
+       }
+
+       ret = bpf_obj_get_info_by_fd(pass_prog_fd, &info, &info_len);
+       if (ret) {
+               fprintf(stderr, "unable to get program ID from kernel\n");
+               goto done;
+       }
+       map_key = info.id;
+       map_fd = bpf_create_map(BPF_MAP_TYPE_XDP_CHAIN,
+                               sizeof(map_key), sizeof(acts),
+                               2, 0);
+
+       if (map_fd < 0) {
+               fprintf(stderr, "unable to create chain call map: %s\n", 
strerror(errno));
+               goto done;
+       }
+
+       if (pin_path && (ret = bpf_obj_pin(map_fd, pin_path))) {
+               fprintf(stderr, "unable to pin map at %s: %s\n", pin_path,
+                       strerror(errno));
+               goto done;
+       }
+
+
+#define RUN_PING(should_fail, err) if ((ret = run_ping(should_fail, err))) 
goto done;
+
+       if (!setup_only) {
+               RUN_PING(false, "Pre-setup ping test");
+
+               signal(SIGINT, cleanup);
+               signal(SIGTERM, cleanup);
+       }
+
+       if ((ret = bpf_set_link_xdp_chain(ifindex, pass_prog_fd, map_fd, 
xdp_flags)) < 0) {
+               fprintf(stderr, "Link set xdp fd failed for %s: %s\n", ifname,
+                       strerror(-ret));
+               goto done;
+       }
+
+       if ((ret = bpf_get_link_xdp_chain(ifindex, &prog_id, &chain_map_id, 0)) 
< 0) {
+               fprintf(stderr, "Unable to get xdp IDs for %s: '%s'\n", ifname, 
strerror(-ret));
+               goto done;
+       }
+       printf("  XDP prog ID: %u Chain map ID: %u\n", prog_id, chain_map_id);
+
+       if (!setup_only) {
+               sleep(1);
+               RUN_PING(false, "Empty map test");
+       }
+
+       acts.wildcard_act = drop_prog_fd;
+       if (bpf_map_update_elem(map_fd, &map_key, &acts, 0)) {
+               fprintf(stderr, "unable to insert into map: %s\n", 
strerror(errno));
+               goto done;
+       }
+
+       if (setup_only) {
+               printf("Setup done; exiting.\n");
+               ret = 0;
+               goto done;
+       }
+
+       sleep(1);
+
+       RUN_PING(true, "Wildcard act test");
+
+       if (bpf_map_delete_elem(map_fd, &map_key)) {
+               fprintf(stderr, "unable to delete from map: %s\n", 
strerror(errno));
+               goto done;
+       }
+       sleep(1);
+
+       RUN_PING(false, "Post-delete map test");
+
+       acts.wildcard_act = 0;
+       acts.pass_act = drop_prog_fd;
+       if (bpf_map_update_elem(map_fd, &map_key, &acts, 0)) {
+               fprintf(stderr, "unable to insert into map: %s\n", 
strerror(errno));
+               goto done;
+       }
+       sleep(1);
+
+       RUN_PING(true, "Pass act test");
+
+
+       if ((ret = bpf_set_link_xdp_chain(ifindex, -1, -1, xdp_flags)) < 0) {
+               fprintf(stderr, "Link clear xdp fd failed for %s: '%s'\n", 
ifname, strerror(-ret));
+               goto done;
+       }
+       sleep(1);
+
+       RUN_PING(false, "Post-delete prog test");
+
+
+done:
+       cleanup(ret);
+
+       if (pass_prog_fd > 0)
+               close(pass_prog_fd);
+       if (drop_prog_fd > 0)
+               close(drop_prog_fd);
+       if (map_fd > 0)
+               close(map_fd);
+
+       return ret;
+}

Reply via email to