]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
bpf: make tc's bpf loader generic and move into lib
authorDaniel Borkmann <daniel@iogearbox.net>
Thu, 10 Nov 2016 00:20:59 +0000 (01:20 +0100)
committerStephen Hemminger <sthemmin@microsoft.com>
Tue, 29 Nov 2016 20:35:32 +0000 (12:35 -0800)
This work moves the bpf loader into the iproute2 library and reworks
the tc specific parts into generic code. It's useful as we can then
more easily support new program types by just having the same ELF
loader backend. Joint work with Thomas Graf. I hacked a rough start
of a test suite to make sure nothing breaks [1] and looks all good.

  [1] https://github.com/borkmann/clsact/blob/master/test_bpf.sh

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Makefile
configure
include/bpf_api.h
include/bpf_util.h [moved from tc/tc_bpf.h with 63% similarity]
lib/Makefile
lib/bpf.c [moved from tc/tc_bpf.c with 79% similarity]
tc/Makefile
tc/e_bpf.c
tc/f_bpf.c
tc/m_bpf.c

index fa200ddb76679f0db53890e6f7843b85542afa0a..37b68ad87f0689861c7705353ee07ac8267f6716 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,8 @@
+# Include "Config" if already generated
+ifneq ($(wildcard Config),)
+include Config
+endif
+
 ifndef VERBOSE
 MAKEFLAGS += --no-print-directory
 endif
@@ -7,6 +12,7 @@ LIBDIR?=$(PREFIX)/lib
 SBINDIR?=/sbin
 CONFDIR?=/etc/iproute2
 DATADIR?=$(PREFIX)/share
+HDRDIR?=$(PREFIX)/include/iproute2
 DOCDIR?=$(DATADIR)/doc/iproute2
 MANDIR?=$(DATADIR)/man
 ARPDDIR?=/var/lib/arpd
@@ -51,6 +57,11 @@ SUBDIRS=lib ip tc bridge misc netem genl tipc devlink man
 LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
 LDLIBS += $(LIBNETLINK)
 
+ifeq ($(HAVE_ELF),y)
+CFLAGS += -DHAVE_ELF
+LDLIBS += -lelf
+endif
+
 all: Config
        @set -e; \
        for i in $(SUBDIRS); \
@@ -63,6 +74,7 @@ install: all
        install -m 0755 -d $(DESTDIR)$(SBINDIR)
        install -m 0755 -d $(DESTDIR)$(CONFDIR)
        install -m 0755 -d $(DESTDIR)$(ARPDDIR)
+       install -m 0755 -d $(DESTDIR)$(HDRDIR)
        install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples
        install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv
        install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \
@@ -73,6 +85,7 @@ install: all
        install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR)
        install -m 0755 -d $(DESTDIR)$(BASH_COMPDIR)
        install -m 0644 bash-completion/tc $(DESTDIR)$(BASH_COMPDIR)
+       install -m 0644 include/bpf_elf.h $(DESTDIR)$(HDRDIR)
 
 snapshot:
        echo "static const char SNAPSHOT[] = \""`date +%y%m%d`"\";" \
index c978da34b404c06efa6582e5887ef376b3a3c07e..6c431c31042b4472e554d373e58009ca89133319 100755 (executable)
--- a/configure
+++ b/configure
@@ -272,7 +272,7 @@ EOF
 
     if $CC -I$INCLUDE -o $TMPDIR/elftest $TMPDIR/elftest.c -lelf >/dev/null 2>&1
     then
-       echo "TC_CONFIG_ELF:=y" >>Config
+       echo "HAVE_ELF:=y" >>Config
        echo "yes"
     else
        echo "no"
index 1b250d2e9c2d54a26c1f7c9bb0d1ad87d0cd73a9..7642623598064b1e6cdb23ef019109d4d05770d4 100644 (file)
 
 /** BPF helper functions for tc. Individual flags are in linux/bpf.h */
 
+#ifndef __BPF_FUNC
+# define __BPF_FUNC(NAME, ...)                                         \
+       (* NAME)(__VA_ARGS__) __maybe_unused
+#endif
+
 #ifndef BPF_FUNC
 # define BPF_FUNC(NAME, ...)                                           \
-       (* NAME)(__VA_ARGS__) __maybe_unused = (void *) BPF_FUNC_##NAME
+       __BPF_FUNC(NAME, __VA_ARGS__) = (void *) BPF_FUNC_##NAME
 #endif
 
 /* Map access/manipulation */
@@ -147,10 +152,15 @@ static void BPF_FUNC(tail_call, struct __sk_buff *skb, void *map,
 
 /* System helpers */
 static uint32_t BPF_FUNC(get_smp_processor_id);
+static uint32_t BPF_FUNC(get_numa_node_id);
 
 /* Packet misc meta data */
 static uint32_t BPF_FUNC(get_cgroup_classid, struct __sk_buff *skb);
+static int BPF_FUNC(skb_under_cgroup, void *map, uint32_t index);
+
 static uint32_t BPF_FUNC(get_route_realm, struct __sk_buff *skb);
+static uint32_t BPF_FUNC(get_hash_recalc, struct __sk_buff *skb);
+static uint32_t BPF_FUNC(set_hash_invalid, struct __sk_buff *skb);
 
 /* Packet redirection */
 static int BPF_FUNC(redirect, int ifindex, uint32_t flags);
@@ -169,6 +179,20 @@ static int BPF_FUNC(l4_csum_replace, struct __sk_buff *skb, uint32_t off,
                    uint32_t from, uint32_t to, uint32_t flags);
 static int BPF_FUNC(csum_diff, const void *from, uint32_t from_size,
                    const void *to, uint32_t to_size, uint32_t seed);
+static int BPF_FUNC(csum_update, struct __sk_buff *skb, uint32_t wsum);
+
+static int BPF_FUNC(skb_change_type, struct __sk_buff *skb, uint32_t type);
+static int BPF_FUNC(skb_change_proto, struct __sk_buff *skb, uint32_t proto,
+                   uint32_t flags);
+static int BPF_FUNC(skb_change_tail, struct __sk_buff *skb, uint32_t nlen,
+                   uint32_t flags);
+
+static int BPF_FUNC(skb_pull_data, struct __sk_buff *skb, uint32_t len);
+
+/* Event notification */
+static int __BPF_FUNC(skb_event_output, struct __sk_buff *skb, void *map,
+                     uint64_t index, const void *data, uint32_t size) =
+                     (void *) BPF_FUNC_perf_event_output;
 
 /* Packet vlan encap/decap */
 static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto,
similarity index 63%
rename from tc/tc_bpf.h
rename to include/bpf_util.h
index 30306deab05211e5c0b2da99a8f8011fee1afe68..05baeecda57f47f255088fc3bc7893be773ddb87 100644 (file)
@@ -1,35 +1,27 @@
 /*
- * tc_bpf.h    BPF common code
+ * bpf_util.h  BPF common code
  *
  *             This program is free software; you can distribute it and/or
  *             modify it under the terms of the GNU General Public License
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
  *
- * Authors:    Daniel Borkmann <dborkman@redhat.com>
+ * Authors:    Daniel Borkmann <daniel@iogearbox.net>
  *             Jiri Pirko <jiri@resnulli.us>
  */
 
-#ifndef _TC_BPF_H_
-#define _TC_BPF_H_ 1
+#ifndef __BPF_UTIL__
+#define __BPF_UTIL__
 
-#include <linux/netlink.h>
 #include <linux/bpf.h>
+#include <linux/filter.h>
 #include <linux/magic.h>
+#include <linux/elf-em.h>
+#include <linux/if_alg.h>
 
 #include "utils.h"
 #include "bpf_scm.h"
 
-enum {
-       BPF_NLA_OPS_LEN = 0,
-       BPF_NLA_OPS,
-       BPF_NLA_FD,
-       BPF_NLA_NAME,
-       __BPF_NLA_MAX,
-};
-
-#define BPF_NLA_MAX    __BPF_NLA_MAX
-
 #define BPF_ENV_UDS    "TC_BPF_UDS"
 #define BPF_ENV_MNT    "TC_BPF_MNT"
 
@@ -37,28 +29,49 @@ enum {
 # define BPF_MAX_LOG   4096
 #endif
 
+#define BPF_DIR_GLOBALS        "globals"
+
 #ifndef BPF_FS_MAGIC
 # define BPF_FS_MAGIC  0xcafe4a11
 #endif
 
 #define BPF_DIR_MNT    "/sys/fs/bpf"
 
-#define BPF_DIR_TC     "tc"
-#define BPF_DIR_GLOBALS        "globals"
-
 #ifndef TRACEFS_MAGIC
 # define TRACEFS_MAGIC 0x74726163
 #endif
 
 #define TRACE_DIR_MNT  "/sys/kernel/tracing"
 
-int bpf_trace_pipe(void);
-const char *bpf_default_section(const enum bpf_prog_type type);
+#ifndef AF_ALG
+# define AF_ALG                38
+#endif
+
+#ifndef EM_BPF
+# define EM_BPF                247
+#endif
+
+struct bpf_cfg_ops {
+       void (*cbpf_cb)(void *nl, const struct sock_filter *ops, int ops_len);
+       void (*ebpf_cb)(void *nl, int fd, const char *annotation);
+};
+
+struct bpf_cfg_in {
+       const char *object;
+       const char *section;
+       const char *uds;
+       int argc;
+       char **argv;
+       struct sock_filter *ops;
+};
+
+int bpf_parse_common(enum bpf_prog_type type, struct bpf_cfg_in *cfg,
+                    const struct bpf_cfg_ops *ops, void *nl);
+
+const char *bpf_prog_to_default_section(enum bpf_prog_type type);
 
-int bpf_parse_common(int *ptr_argc, char ***ptr_argv, const int *nla_tbl,
-                    enum bpf_prog_type type, const char **ptr_object,
-                    const char **ptr_uds_name, struct nlmsghdr *n);
 int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv);
+int bpf_trace_pipe(void);
 
 void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len);
 
@@ -79,4 +92,4 @@ static inline int bpf_recv_map_fds(const char *path, int *fds,
        return -1;
 }
 #endif /* HAVE_ELF */
-#endif /* _TC_BPF_H_ */
+#endif /* __BPF_UTIL__ */
index 52e016db50b6e2cc874fbccbb33390ad3ae04697..5b7ec169048ac32d95718bc64f50c1e8c1bf5a61 100644 (file)
@@ -8,7 +8,7 @@ CFLAGS += -fPIC
 
 UTILOBJ = utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o \
        inet_proto.o namespace.o json_writer.o \
-       names.o color.o
+       names.o color.o bpf.o
 
 NLOBJ=libgenl.o ll_map.o libnetlink.o
 
similarity index 79%
rename from tc/tc_bpf.c
rename to lib/bpf.c
index b390f7e2b1523f2004d3da20d4bce48f0ae8185b..8a5b84bf776993aa32f6116b3bba2dc6c4b0cd87 100644 (file)
+++ b/lib/bpf.c
@@ -1,14 +1,14 @@
 /*
- * tc_bpf.c    BPF common code
+ * bpf.c       BPF common code
  *
  *             This program is free software; you can distribute it and/or
  *             modify it under the terms of the GNU General Public License
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
  *
- * Authors:    Daniel Borkmann <dborkman@redhat.com>
+ * Authors:    Daniel Borkmann <daniel@iogearbox.net>
  *             Jiri Pirko <jiri@resnulli.us>
- *             Alexei Starovoitov <ast@plumgrid.com>
+ *             Alexei Starovoitov <ast@kernel.org>
  */
 
 #include <stdio.h>
@@ -21,6 +21,7 @@
 #include <fcntl.h>
 #include <stdarg.h>
 #include <limits.h>
+#include <assert.h>
 
 #ifdef HAVE_ELF
 #include <libelf.h>
 #include <sys/sendfile.h>
 #include <sys/resource.h>
 
-#include <linux/bpf.h>
-#include <linux/filter.h>
-#include <linux/if_alg.h>
-
 #include <arpa/inet.h>
 
 #include "utils.h"
 
+#include "bpf_util.h"
 #include "bpf_elf.h"
 #include "bpf_scm.h"
 
-#include "tc_util.h"
-#include "tc_bpf.h"
+struct bpf_prog_meta {
+       const char *type;
+       const char *subdir;
+       const char *section;
+       bool may_uds_export;
+};
 
-#ifndef AF_ALG
-#define AF_ALG 38
-#endif
+static const enum bpf_prog_type __bpf_types[] = {
+       BPF_PROG_TYPE_SCHED_CLS,
+       BPF_PROG_TYPE_SCHED_ACT,
+};
 
-#ifndef EM_BPF
-#define EM_BPF 247
-#endif
+static const struct bpf_prog_meta __bpf_prog_meta[] = {
+       [BPF_PROG_TYPE_SCHED_CLS] = {
+               .type           = "cls",
+               .subdir         = "tc",
+               .section        = ELF_SECTION_CLASSIFIER,
+               .may_uds_export = true,
+       },
+       [BPF_PROG_TYPE_SCHED_ACT] = {
+               .type           = "act",
+               .subdir         = "tc",
+               .section        = ELF_SECTION_ACTION,
+               .may_uds_export = true,
+       },
+};
+
+static const char *bpf_prog_to_subdir(enum bpf_prog_type type)
+{
+       assert(type < ARRAY_SIZE(__bpf_prog_meta) &&
+              __bpf_prog_meta[type].subdir);
+       return __bpf_prog_meta[type].subdir;
+}
+
+const char *bpf_prog_to_default_section(enum bpf_prog_type type)
+{
+       assert(type < ARRAY_SIZE(__bpf_prog_meta) &&
+              __bpf_prog_meta[type].section);
+       return __bpf_prog_meta[type].section;
+}
 
 #ifdef HAVE_ELF
 static int bpf_obj_open(const char *path, enum bpf_prog_type type,
@@ -108,7 +136,7 @@ static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
 
        if (from_file) {
                size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,");
-               char *tmp_string;
+               char *tmp_string, *last;
                FILE *fp;
 
                tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len;
@@ -131,6 +159,10 @@ static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
 
                fclose(fp);
 
+               last = &tmp_string[strlen(tmp_string) - 1];
+               if (*last == '\n')
+                       *last = 0;
+
                *need_release = true;
                *bpf_string = tmp_string;
        } else {
@@ -307,7 +339,7 @@ static int bpf_mnt_fs(const char *target)
                bind_done = true;
        }
 
-       if (mount("bpf", target, "bpf", 0, NULL)) {
+       if (mount("bpf", target, "bpf", 0, "mode=0700")) {
                fprintf(stderr, "mount -t bpf bpf %s failed: %s\n",
                        target, strerror(errno));
                return -1;
@@ -407,23 +439,139 @@ int bpf_trace_pipe(void)
        return 0;
 }
 
-static const char *bpf_get_tc_dir(void)
+static int bpf_gen_global(const char *bpf_sub_dir)
 {
-       static bool bpf_mnt_cached;
-       static char bpf_tc_dir[PATH_MAX];
+       char bpf_glo_dir[PATH_MAX];
+       int ret;
+
+       snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s/",
+                bpf_sub_dir, BPF_DIR_GLOBALS);
+
+       ret = mkdir(bpf_glo_dir, S_IRWXU);
+       if (ret && errno != EEXIST) {
+               fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir,
+                       strerror(errno));
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bpf_gen_master(const char *base, const char *name)
+{
+       char bpf_sub_dir[PATH_MAX];
+       int ret;
+
+       snprintf(bpf_sub_dir, sizeof(bpf_sub_dir), "%s%s/", base, name);
+
+       ret = mkdir(bpf_sub_dir, S_IRWXU);
+       if (ret && errno != EEXIST) {
+               fprintf(stderr, "mkdir %s failed: %s\n", bpf_sub_dir,
+                       strerror(errno));
+               return ret;
+       }
+
+       return bpf_gen_global(bpf_sub_dir);
+}
+
+static int bpf_slave_via_bind_mnt(const char *full_name,
+                                 const char *full_link)
+{
+       int ret;
+
+       ret = mkdir(full_name, S_IRWXU);
+       if (ret) {
+               assert(errno != EEXIST);
+               fprintf(stderr, "mkdir %s failed: %s\n", full_name,
+                       strerror(errno));
+               return ret;
+       }
+
+       ret = mount(full_link, full_name, "none", MS_BIND, NULL);
+       if (ret) {
+               rmdir(full_name);
+               fprintf(stderr, "mount --bind %s %s failed: %s\n",
+                       full_link, full_name, strerror(errno));
+       }
+
+       return ret;
+}
+
+static int bpf_gen_slave(const char *base, const char *name,
+                        const char *link)
+{
+       char bpf_lnk_dir[PATH_MAX];
+       char bpf_sub_dir[PATH_MAX];
+       struct stat sb = {};
+       int ret;
+
+       snprintf(bpf_lnk_dir, sizeof(bpf_lnk_dir), "%s%s/", base, link);
+       snprintf(bpf_sub_dir, sizeof(bpf_sub_dir), "%s%s",  base, name);
+
+       ret = symlink(bpf_lnk_dir, bpf_sub_dir);
+       if (ret) {
+               if (errno != EEXIST) {
+                       if (errno != EPERM) {
+                               fprintf(stderr, "symlink %s failed: %s\n",
+                                       bpf_sub_dir, strerror(errno));
+                               return ret;
+                       }
+
+                       return bpf_slave_via_bind_mnt(bpf_sub_dir,
+                                                     bpf_lnk_dir);
+               }
+
+               ret = lstat(bpf_sub_dir, &sb);
+               if (ret) {
+                       fprintf(stderr, "lstat %s failed: %s\n",
+                               bpf_sub_dir, strerror(errno));
+                       return ret;
+               }
+
+               if ((sb.st_mode & S_IFMT) != S_IFLNK)
+                       return bpf_gen_global(bpf_sub_dir);
+       }
+
+       return 0;
+}
+
+static int bpf_gen_hierarchy(const char *base)
+{
+       int ret, i;
+
+       ret = bpf_gen_master(base, bpf_prog_to_subdir(__bpf_types[0]));
+       for (i = 1; i < ARRAY_SIZE(__bpf_types) && !ret; i++)
+               ret = bpf_gen_slave(base,
+                                   bpf_prog_to_subdir(__bpf_types[i]),
+                                   bpf_prog_to_subdir(__bpf_types[0]));
+       return ret;
+}
+
+static const char *bpf_get_work_dir(enum bpf_prog_type type)
+{
+       static char bpf_tmp[PATH_MAX] = BPF_DIR_MNT;
+       static char bpf_wrk_dir[PATH_MAX];
        static const char *mnt;
+       static bool bpf_mnt_cached;
        static const char * const bpf_known_mnts[] = {
                BPF_DIR_MNT,
+               "/bpf",
                0,
        };
-       char bpf_mnt[PATH_MAX] = BPF_DIR_MNT;
-       char bpf_glo_dir[PATH_MAX];
        int ret;
 
-       if (bpf_mnt_cached)
-               goto done;
+       if (bpf_mnt_cached) {
+               const char *out = mnt;
+
+               if (out) {
+                       snprintf(bpf_tmp, sizeof(bpf_tmp), "%s%s/",
+                                out, bpf_prog_to_subdir(type));
+                       out = bpf_tmp;
+               }
+               return out;
+       }
 
-       mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_mnt, sizeof(bpf_mnt),
+       mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_tmp, sizeof(bpf_tmp),
                             bpf_known_mnts);
        if (!mnt) {
                mnt = getenv(BPF_ENV_MNT);
@@ -436,41 +584,29 @@ static const char *bpf_get_tc_dir(void)
                }
        }
 
-       snprintf(bpf_tc_dir, sizeof(bpf_tc_dir), "%s/%s", mnt, BPF_DIR_TC);
-       ret = mkdir(bpf_tc_dir, S_IRWXU);
-       if (ret && errno != EEXIST) {
-               fprintf(stderr, "mkdir %s failed: %s\n", bpf_tc_dir,
-                       strerror(errno));
-               mnt = NULL;
-               goto out;
-       }
+       snprintf(bpf_wrk_dir, sizeof(bpf_wrk_dir), "%s/", mnt);
 
-       snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s",
-                bpf_tc_dir, BPF_DIR_GLOBALS);
-       ret = mkdir(bpf_glo_dir, S_IRWXU);
-       if (ret && errno != EEXIST) {
-               fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir,
-                       strerror(errno));
+       ret = bpf_gen_hierarchy(bpf_wrk_dir);
+       if (ret) {
                mnt = NULL;
                goto out;
        }
 
-       mnt = bpf_tc_dir;
+       mnt = bpf_wrk_dir;
 out:
        bpf_mnt_cached = true;
-done:
        return mnt;
 }
 
-static int bpf_obj_get(const char *pathname)
+static int bpf_obj_get(const char *pathname, enum bpf_prog_type type)
 {
        union bpf_attr attr = {};
        char tmp[PATH_MAX];
 
        if (strlen(pathname) > 2 && pathname[0] == 'm' &&
-           pathname[1] == ':' && bpf_get_tc_dir()) {
+           pathname[1] == ':' && bpf_get_work_dir(type)) {
                snprintf(tmp, sizeof(tmp), "%s/%s",
-                        bpf_get_tc_dir(), pathname + 2);
+                        bpf_get_work_dir(type), pathname + 2);
                pathname = tmp;
        }
 
@@ -479,39 +615,24 @@ static int bpf_obj_get(const char *pathname)
        return bpf(BPF_OBJ_GET, &attr, sizeof(attr));
 }
 
-const char *bpf_default_section(const enum bpf_prog_type type)
-{
-       switch (type) {
-       case BPF_PROG_TYPE_SCHED_CLS:
-               return ELF_SECTION_CLASSIFIER;
-       case BPF_PROG_TYPE_SCHED_ACT:
-               return ELF_SECTION_ACTION;
-       default:
-               return NULL;
-       }
-}
-
 enum bpf_mode {
-       CBPF_BYTECODE = 0,
+       CBPF_BYTECODE,
        CBPF_FILE,
        EBPF_OBJECT,
        EBPF_PINNED,
-       __BPF_MODE_MAX,
-#define BPF_MODE_MAX   __BPF_MODE_MAX
+       BPF_MODE_MAX,
 };
 
-static int bpf_parse(int *ptr_argc, char ***ptr_argv, const bool *opt_tbl,
-                    enum bpf_prog_type *type, enum bpf_mode *mode,
-                    const char **ptr_object, const char **ptr_section,
-                    const char **ptr_uds_name, struct sock_filter *opcodes)
+static int bpf_parse(enum bpf_prog_type *type, enum bpf_mode *mode,
+                    struct bpf_cfg_in *cfg, const bool *opt_tbl)
 {
        const char *file, *section, *uds_name;
        bool verbose = false;
-       int ret, argc;
+       int i, ret, argc;
        char **argv;
 
-       argv = *ptr_argv;
-       argc = *ptr_argc;
+       argv = cfg->argv;
+       argc = cfg->argc;
 
        if (opt_tbl[CBPF_BYTECODE] &&
            (matches(*argv, "bytecode") == 0 ||
@@ -544,11 +665,18 @@ static int bpf_parse(int *ptr_argc, char ***ptr_argv, const bool *opt_tbl,
                if (*type == BPF_PROG_TYPE_UNSPEC) {
                        if (argc > 0 && matches(*argv, "type") == 0) {
                                NEXT_ARG();
-                               if (matches(*argv, "cls") == 0) {
-                                       *type = BPF_PROG_TYPE_SCHED_CLS;
-                               } else if (matches(*argv, "act") == 0) {
-                                       *type = BPF_PROG_TYPE_SCHED_ACT;
-                               } else {
+                               for (i = 0; i < ARRAY_SIZE(__bpf_prog_meta);
+                                    i++) {
+                                       if (!__bpf_prog_meta[i].type)
+                                               continue;
+                                       if (!matches(*argv,
+                                                    __bpf_prog_meta[i].type)) {
+                                               *type = i;
+                                               break;
+                                       }
+                               }
+
+                               if (*type == BPF_PROG_TYPE_UNSPEC) {
                                        fprintf(stderr, "What type is \"%s\"?\n",
                                                *argv);
                                        return -1;
@@ -559,19 +687,21 @@ static int bpf_parse(int *ptr_argc, char ***ptr_argv, const bool *opt_tbl,
                        }
                }
 
-               section = bpf_default_section(*type);
+               section = bpf_prog_to_default_section(*type);
                if (argc > 0 && matches(*argv, "section") == 0) {
                        NEXT_ARG();
                        section = *argv;
                        NEXT_ARG_FWD();
                }
 
-               uds_name = getenv(BPF_ENV_UDS);
-               if (argc > 0 && !uds_name &&
-                   matches(*argv, "export") == 0) {
-                       NEXT_ARG();
-                       uds_name = *argv;
-                       NEXT_ARG_FWD();
+               if (__bpf_prog_meta[*type].may_uds_export) {
+                       uds_name = getenv(BPF_ENV_UDS);
+                       if (argc > 0 && !uds_name &&
+                           matches(*argv, "export") == 0) {
+                               NEXT_ARG();
+                               uds_name = *argv;
+                               NEXT_ARG_FWD();
+                       }
                }
 
                if (argc > 0 && matches(*argv, "verbose") == 0) {
@@ -583,72 +713,72 @@ static int bpf_parse(int *ptr_argc, char ***ptr_argv, const bool *opt_tbl,
        }
 
        if (*mode == CBPF_BYTECODE || *mode == CBPF_FILE)
-               ret = bpf_ops_parse(argc, argv, opcodes, *mode == CBPF_FILE);
+               ret = bpf_ops_parse(argc, argv, cfg->ops, *mode == CBPF_FILE);
        else if (*mode == EBPF_OBJECT)
                ret = bpf_obj_open(file, *type, section, verbose);
        else if (*mode == EBPF_PINNED)
-               ret = bpf_obj_get(file);
+               ret = bpf_obj_get(file, *type);
        else
                return -1;
 
-       if (ptr_object)
-               *ptr_object = file;
-       if (ptr_section)
-               *ptr_section = section;
-       if (ptr_uds_name)
-               *ptr_uds_name = uds_name;
-
-       *ptr_argc = argc;
-       *ptr_argv = argv;
+       cfg->object  = file;
+       cfg->section = section;
+       cfg->uds     = uds_name;
+       cfg->argc    = argc;
+       cfg->argv    = argv;
 
        return ret;
 }
 
-int bpf_parse_common(int *ptr_argc, char ***ptr_argv, const int *nla_tbl,
-                    enum bpf_prog_type type, const char **ptr_object,
-                    const char **ptr_uds_name, struct nlmsghdr *n)
+static int bpf_parse_opt_tbl(enum bpf_prog_type type, struct bpf_cfg_in *cfg,
+                            const struct bpf_cfg_ops *ops, void *nl,
+                            const bool *opt_tbl)
 {
        struct sock_filter opcodes[BPF_MAXINSNS];
-       const bool opt_tbl[BPF_MODE_MAX] = {
-               [CBPF_BYTECODE] = true,
-               [CBPF_FILE]     = true,
-               [EBPF_OBJECT]   = true,
-               [EBPF_PINNED]   = true,
-       };
        char annotation[256];
-       const char *section;
        enum bpf_mode mode;
        int ret;
 
-       ret = bpf_parse(ptr_argc, ptr_argv, opt_tbl, &type, &mode,
-                       ptr_object, &section, ptr_uds_name, opcodes);
+       cfg->ops = opcodes;
+       ret = bpf_parse(&type, &mode, cfg, opt_tbl);
+       cfg->ops = NULL;
        if (ret < 0)
                return ret;
 
-       if (mode == CBPF_BYTECODE || mode == CBPF_FILE) {
-               addattr16(n, MAX_MSG, nla_tbl[BPF_NLA_OPS_LEN], ret);
-               addattr_l(n, MAX_MSG, nla_tbl[BPF_NLA_OPS], opcodes,
-                         ret * sizeof(struct sock_filter));
-       }
-
+       if (mode == CBPF_BYTECODE || mode == CBPF_FILE)
+               ops->cbpf_cb(nl, opcodes, ret);
        if (mode == EBPF_OBJECT || mode == EBPF_PINNED) {
                snprintf(annotation, sizeof(annotation), "%s:[%s]",
-                        basename(*ptr_object), mode == EBPF_PINNED ?
-                        "*fsobj" : section);
-
-               addattr32(n, MAX_MSG, nla_tbl[BPF_NLA_FD], ret);
-               addattrstrz(n, MAX_MSG, nla_tbl[BPF_NLA_NAME], annotation);
+                        basename(cfg->object), mode == EBPF_PINNED ?
+                        "*fsobj" : cfg->section);
+               ops->ebpf_cb(nl, ret, annotation);
        }
 
        return 0;
 }
 
+int bpf_parse_common(enum bpf_prog_type type, struct bpf_cfg_in *cfg,
+                    const struct bpf_cfg_ops *ops, void *nl)
+{
+       bool opt_tbl[BPF_MODE_MAX] = {};
+
+       if (ops->cbpf_cb) {
+               opt_tbl[CBPF_BYTECODE] = true;
+               opt_tbl[CBPF_FILE]     = true;
+       }
+
+       if (ops->ebpf_cb) {
+               opt_tbl[EBPF_OBJECT]   = true;
+               opt_tbl[EBPF_PINNED]   = true;
+       }
+
+       return bpf_parse_opt_tbl(type, cfg, ops, nl, opt_tbl);
+}
+
 int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv)
 {
        enum bpf_prog_type type = BPF_PROG_TYPE_UNSPEC;
        const bool opt_tbl[BPF_MODE_MAX] = {
-               [CBPF_BYTECODE] = false,
-               [CBPF_FILE]     = false,
                [EBPF_OBJECT]   = true,
                [EBPF_PINNED]   = true,
        };
@@ -657,19 +787,21 @@ int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv)
                .size_key       = sizeof(int),
                .size_value     = sizeof(int),
        };
+       struct bpf_cfg_in cfg = {
+               .argc           = argc,
+               .argv           = argv,
+       };
        int ret, prog_fd, map_fd;
-       const char *section;
        enum bpf_mode mode;
        uint32_t map_key;
 
-       prog_fd = bpf_parse(&argc, &argv, opt_tbl, &type, &mode,
-                           NULL, &section, NULL, NULL);
+       prog_fd = bpf_parse(&type, &mode, &cfg, opt_tbl);
        if (prog_fd < 0)
                return prog_fd;
        if (key) {
                map_key = *key;
        } else {
-               ret = sscanf(section, "%*i/%i", &map_key);
+               ret = sscanf(cfg.section, "%*i/%i", &map_key);
                if (ret != 1) {
                        fprintf(stderr, "Couldn\'t infer map key from section name! Please provide \'key\' argument!\n");
                        ret = -EINVAL;
@@ -677,7 +809,7 @@ int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv)
                }
        }
 
-       map_fd = bpf_obj_get(map_path);
+       map_fd = bpf_obj_get(map_path, type);
        if (map_fd < 0) {
                fprintf(stderr, "Couldn\'t retrieve pinned map \'%s\': %s\n",
                        map_path, strerror(errno));
@@ -726,6 +858,7 @@ struct bpf_elf_ctx {
        struct bpf_elf_map      maps[ELF_MAX_MAPS];
        int                     sym_num;
        int                     map_num;
+       int                     map_len;
        bool                    *sec_done;
        int                     sec_maps;
        char                    license[ELF_MAX_LICENSE_LEN];
@@ -952,7 +1085,7 @@ static int bpf_init_env(const char *pathname)
        /* Don't bother in case we fail! */
        setrlimit(RLIMIT_MEMLOCK, &limit);
 
-       if (!bpf_get_tc_dir()) {
+       if (!bpf_get_work_dir(BPF_PROG_TYPE_UNSPEC)) {
                fprintf(stderr, "Continuing without mounted eBPF fs. Too old kernel?\n");
                return 0;
        }
@@ -994,15 +1127,18 @@ static void bpf_make_pathname(char *pathname, size_t len, const char *name,
 {
        switch (pinning) {
        case PIN_OBJECT_NS:
-               snprintf(pathname, len, "%s/%s/%s", bpf_get_tc_dir(),
+               snprintf(pathname, len, "%s/%s/%s",
+                        bpf_get_work_dir(ctx->type),
                         bpf_get_obj_uid(NULL), name);
                break;
        case PIN_GLOBAL_NS:
-               snprintf(pathname, len, "%s/%s/%s", bpf_get_tc_dir(),
+               snprintf(pathname, len, "%s/%s/%s",
+                        bpf_get_work_dir(ctx->type),
                         BPF_DIR_GLOBALS, name);
                break;
        default:
-               snprintf(pathname, len, "%s/../%s/%s", bpf_get_tc_dir(),
+               snprintf(pathname, len, "%s/../%s/%s",
+                        bpf_get_work_dir(ctx->type),
                         bpf_custom_pinning(ctx, pinning), name);
                break;
        }
@@ -1013,19 +1149,19 @@ static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx,
 {
        char pathname[PATH_MAX];
 
-       if (bpf_no_pinning(ctx, pinning) || !bpf_get_tc_dir())
+       if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
                return 0;
 
        bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
-       return bpf_obj_get(pathname);
+       return bpf_obj_get(pathname, ctx->type);
 }
 
-static int bpf_make_obj_path(void)
+static int bpf_make_obj_path(const struct bpf_elf_ctx *ctx)
 {
        char tmp[PATH_MAX];
        int ret;
 
-       snprintf(tmp, sizeof(tmp), "%s/%s", bpf_get_tc_dir(),
+       snprintf(tmp, sizeof(tmp), "%s/%s", bpf_get_work_dir(ctx->type),
                 bpf_get_obj_uid(NULL));
 
        ret = mkdir(tmp, S_IRWXU);
@@ -1037,12 +1173,13 @@ static int bpf_make_obj_path(void)
        return 0;
 }
 
-static int bpf_make_custom_path(const char *todo)
+static int bpf_make_custom_path(const struct bpf_elf_ctx *ctx,
+                               const char *todo)
 {
        char tmp[PATH_MAX], rem[PATH_MAX], *sub;
        int ret;
 
-       snprintf(tmp, sizeof(tmp), "%s/../", bpf_get_tc_dir());
+       snprintf(tmp, sizeof(tmp), "%s/../", bpf_get_work_dir(ctx->type));
        snprintf(rem, sizeof(rem), "%s/", todo);
        sub = strtok(rem, "/");
 
@@ -1073,13 +1210,13 @@ static int bpf_place_pinned(int fd, const char *name,
        const char *tmp;
        int ret = 0;
 
-       if (bpf_no_pinning(ctx, pinning) || !bpf_get_tc_dir())
+       if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
                return 0;
 
        if (pinning == PIN_OBJECT_NS)
-               ret = bpf_make_obj_path();
+               ret = bpf_make_obj_path(ctx);
        else if ((tmp = bpf_custom_pinning(ctx, pinning)))
-               ret = bpf_make_custom_path(tmp);
+               ret = bpf_make_custom_path(ctx, tmp);
        if (ret < 0)
                return ret;
 
@@ -1214,7 +1351,7 @@ static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
                if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
                    GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
                    sym.st_shndx != ctx->sec_maps ||
-                   sym.st_value / sizeof(struct bpf_elf_map) != which)
+                   sym.st_value / ctx->map_len != which)
                        continue;
 
                return bpf_str_tab_name(ctx, &sym);
@@ -1243,6 +1380,25 @@ static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx)
        return 0;
 }
 
+static int bpf_map_num_sym(struct bpf_elf_ctx *ctx)
+{
+       int i, num = 0;
+       GElf_Sym sym;
+
+       for (i = 0; i < ctx->sym_num; i++) {
+               if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
+                       continue;
+
+               if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+                   GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
+                   sym.st_shndx != ctx->sec_maps)
+                       continue;
+               num++;
+       }
+
+       return num;
+}
+
 static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section,
                                 struct bpf_elf_sec_data *data)
 {
@@ -1275,22 +1431,104 @@ static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section,
        return 0;
 }
 
-static int bpf_fetch_maps(struct bpf_elf_ctx *ctx, int section,
-                         struct bpf_elf_sec_data *data)
-{
-       if (data->sec_data->d_size % sizeof(struct bpf_elf_map) != 0)
-               return -EINVAL;
+struct bpf_elf_map_min {
+       __u32 type;
+       __u32 size_key;
+       __u32 size_value;
+       __u32 max_elem;
+};
 
-       ctx->map_num = data->sec_data->d_size / sizeof(struct bpf_elf_map);
+static int bpf_fetch_maps_begin(struct bpf_elf_ctx *ctx, int section,
+                               struct bpf_elf_sec_data *data)
+{
+       ctx->map_num = data->sec_data->d_size;
        ctx->sec_maps = section;
        ctx->sec_done[section] = true;
 
-       if (ctx->map_num > ARRAY_SIZE(ctx->map_fds)) {
+       if (ctx->map_num > sizeof(ctx->maps)) {
                fprintf(stderr, "Too many BPF maps in ELF section!\n");
                return -ENOMEM;
        }
 
-       memcpy(ctx->maps, data->sec_data->d_buf, data->sec_data->d_size);
+       memcpy(ctx->maps, data->sec_data->d_buf, ctx->map_num);
+       return 0;
+}
+
+static int bpf_map_verify_all_offs(struct bpf_elf_ctx *ctx, int end)
+{
+       GElf_Sym sym;
+       int off, i;
+
+       for (off = 0; off < end; off += ctx->map_len) {
+               /* Order doesn't need to be linear here, hence we walk
+                * the table again.
+                */
+               for (i = 0; i < ctx->sym_num; i++) {
+                       if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
+                               continue;
+                       if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+                           GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
+                           sym.st_shndx != ctx->sec_maps)
+                               continue;
+                       if (sym.st_value == off)
+                               break;
+                       if (i == ctx->sym_num - 1)
+                               return -1;
+               }
+       }
+
+       return off == end ? 0 : -1;
+}
+
+static int bpf_fetch_maps_end(struct bpf_elf_ctx *ctx)
+{
+       struct bpf_elf_map fixup[ARRAY_SIZE(ctx->maps)] = {};
+       int i, sym_num = bpf_map_num_sym(ctx);
+       __u8 *buff;
+
+       if (sym_num == 0 || sym_num > ARRAY_SIZE(ctx->maps)) {
+               fprintf(stderr, "%u maps not supported in current map section!\n",
+                       sym_num);
+               return -EINVAL;
+       }
+
+       if (ctx->map_num % sym_num != 0 ||
+           ctx->map_num % sizeof(__u32) != 0) {
+               fprintf(stderr, "Number BPF map symbols are not multiple of struct bpf_elf_map!\n");
+               return -EINVAL;
+       }
+
+       ctx->map_len = ctx->map_num / sym_num;
+       if (bpf_map_verify_all_offs(ctx, ctx->map_num)) {
+               fprintf(stderr, "Different struct bpf_elf_map in use!\n");
+               return -EINVAL;
+       }
+
+       if (ctx->map_len == sizeof(struct bpf_elf_map)) {
+               ctx->map_num = sym_num;
+               return 0;
+       } else if (ctx->map_len > sizeof(struct bpf_elf_map)) {
+               fprintf(stderr, "struct bpf_elf_map not supported, coming from future version?\n");
+               return -EINVAL;
+       } else if (ctx->map_len < sizeof(struct bpf_elf_map_min)) {
+               fprintf(stderr, "struct bpf_elf_map too small, not supported!\n");
+               return -EINVAL;
+       }
+
+       ctx->map_num = sym_num;
+       for (i = 0, buff = (void *)ctx->maps; i < ctx->map_num;
+            i++, buff += ctx->map_len) {
+               /* The fixup leaves the rest of the members as zero, which
+                * is fine currently, but option exist to set some other
+                * default value as well when needed in future.
+                */
+               memcpy(&fixup[i], buff, ctx->map_len);
+       }
+
+       memcpy(ctx->maps, fixup, sizeof(fixup));
+
+       printf("Note: %zu bytes struct bpf_elf_map fixup performed due to size mismatch!\n",
+              sizeof(struct bpf_elf_map) - ctx->map_len);
        return 0;
 }
 
@@ -1339,7 +1577,7 @@ static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx)
 
                if (data.sec_hdr.sh_type == SHT_PROGBITS &&
                    !strcmp(data.sec_name, ELF_SECTION_MAPS))
-                       ret = bpf_fetch_maps(ctx, i, &data);
+                       ret = bpf_fetch_maps_begin(ctx, i, &data);
                else if (data.sec_hdr.sh_type == SHT_PROGBITS &&
                         !strcmp(data.sec_name, ELF_SECTION_LICENSE))
                        ret = bpf_fetch_license(ctx, i, &data);
@@ -1352,11 +1590,17 @@ static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx)
                if (ret < 0) {
                        fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
                                i);
-                       break;
+                       return ret;
                }
        }
 
        if (bpf_has_map_data(ctx)) {
+               ret = bpf_fetch_maps_end(ctx);
+               if (ret < 0) {
+                       fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
+                       return ret;
+               }
+
                ret = bpf_maps_attach_all(ctx);
                if (ret < 0) {
                        fprintf(stderr, "Error loading maps into kernel!\n");
@@ -1367,7 +1611,8 @@ static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx)
        return ret;
 }
 
-static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section)
+static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section,
+                         bool *sseen)
 {
        struct bpf_elf_sec_data data;
        struct bpf_elf_prog prog;
@@ -1384,6 +1629,8 @@ static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section)
                      !strcmp(data.sec_name, section)))
                        continue;
 
+               *sseen = true;
+
                memset(&prog, 0, sizeof(prog));
                prog.type    = ctx->type;
                prog.insns   = data.sec_data->d_buf;
@@ -1392,7 +1639,7 @@ static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section)
 
                fd = bpf_prog_attach(section, &prog, ctx);
                if (fd < 0)
-                       break;
+                       return fd;
 
                ctx->sec_done[i] = true;
                break;
@@ -1438,7 +1685,7 @@ static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx,
                        return -EIO;
                }
 
-               rmap = sym.st_value / sizeof(struct bpf_elf_map);
+               rmap = sym.st_value / ctx->map_len;
                if (rmap >= ARRAY_SIZE(ctx->map_fds))
                        return -EINVAL;
                if (!ctx->map_fds[rmap])
@@ -1457,7 +1704,7 @@ static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx,
 }
 
 static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
-                              bool *lderr)
+                              bool *lderr, bool *sseen)
 {
        struct bpf_elf_sec_data data_relo, data_insn;
        struct bpf_elf_prog prog;
@@ -1469,6 +1716,7 @@ static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
                        continue;
 
                idx = data_relo.sec_hdr.sh_info;
+
                ret = bpf_fill_section_data(ctx, idx, &data_insn);
                if (ret < 0 ||
                    !(data_insn.sec_hdr.sh_type == SHT_PROGBITS &&
@@ -1476,9 +1724,11 @@ static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
                      !strcmp(data_insn.sec_name, section)))
                        continue;
 
+               *sseen = true;
+
                ret = bpf_apply_relo_data(ctx, &data_relo, &data_insn);
                if (ret < 0)
-                       continue;
+                       return ret;
 
                memset(&prog, 0, sizeof(prog));
                prog.type    = ctx->type;
@@ -1489,7 +1739,7 @@ static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
                fd = bpf_prog_attach(section, &prog, ctx);
                if (fd < 0) {
                        *lderr = true;
-                       break;
+                       return fd;
                }
 
                ctx->sec_done[i]   = true;
@@ -1502,14 +1752,16 @@ static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
 
 static int bpf_fetch_prog_sec(struct bpf_elf_ctx *ctx, const char *section)
 {
-       bool lderr = false;
+       bool lderr = false, sseen = false;
        int ret = -1;
 
        if (bpf_has_map_data(ctx))
-               ret = bpf_fetch_prog_relo(ctx, section, &lderr);
+               ret = bpf_fetch_prog_relo(ctx, section, &lderr, &sseen);
        if (ret < 0 && !lderr)
-               ret = bpf_fetch_prog(ctx, section);
-
+               ret = bpf_fetch_prog(ctx, section, &sseen);
+       if (ret < 0 && !sseen)
+               fprintf(stderr, "Program section \'%s\' not found in ELF file!\n",
+                       section);
        return ret;
 }
 
index dfa875b5edafab5b1c4d6aff06d982faf0927dfb..f986fcb9e9fd879243b50cdb1cc5e926e9017923 100644 (file)
@@ -1,5 +1,5 @@
 TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o tc_monitor.o \
-       tc_exec.o tc_bpf.o m_police.o m_estimator.o m_action.o m_ematch.o \
+       tc_exec.o m_police.o m_estimator.o m_action.o m_ematch.o \
        emp_ematch.yacc.o emp_ematch.lex.o
 
 include ../Config
@@ -94,11 +94,6 @@ ifneq ($(TC_CONFIG_NO_XT),y)
   endif
 endif
 
-ifeq ($(TC_CONFIG_ELF),y)
-  CFLAGS += -DHAVE_ELF
-  LDLIBS += -lelf
-endif
-
 TCOBJ += $(TCMODULES)
 LDLIBS += -L. -ltc -lm
 
index d1f5d87fe33e0bc8f709f2ffb007ecb4c128812f..84f43e6c60104f9f107dafbc35aad3b75b4da4d0 100644 (file)
@@ -15,8 +15,8 @@
 #include "utils.h"
 
 #include "tc_util.h"
-#include "tc_bpf.h"
 
+#include "bpf_util.h"
 #include "bpf_elf.h"
 #include "bpf_scm.h"
 
index 665bc6612eeb92891379c4134c46511760eb6205..c4764d8f0a048f91f72a930d2f97e51dfc0be4d0 100644 (file)
@@ -6,7 +6,7 @@
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
  *
- * Authors:    Daniel Borkmann <dborkman@redhat.com>
+ * Authors:    Daniel Borkmann <daniel@iogearbox.net>
  */
 
 #include <stdio.h>
 #include <linux/bpf.h>
 
 #include "utils.h"
+
 #include "tc_util.h"
-#include "tc_bpf.h"
+#include "bpf_util.h"
 
 static const enum bpf_prog_type bpf_type = BPF_PROG_TYPE_SCHED_CLS;
 
-static const int nla_tbl[BPF_NLA_MAX] = {
-       [BPF_NLA_OPS_LEN]       = TCA_BPF_OPS_LEN,
-       [BPF_NLA_OPS]           = TCA_BPF_OPS,
-       [BPF_NLA_FD]            = TCA_BPF_FD,
-       [BPF_NLA_NAME]          = TCA_BPF_NAME,
-};
-
 static void explain(void)
 {
        fprintf(stderr, "Usage: ... bpf ...\n");
@@ -52,7 +46,7 @@ static void explain(void)
        fprintf(stderr, "pinned eBPF program.\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "Where CLS_NAME refers to the section name containing the\n");
-       fprintf(stderr, "classifier (default \'%s\').\n", bpf_default_section(bpf_type));
+       fprintf(stderr, "classifier (default \'%s\').\n", bpf_prog_to_default_section(bpf_type));
        fprintf(stderr, "\n");
        fprintf(stderr, "Where UDS_FILE points to a unix domain socket file in order\n");
        fprintf(stderr, "to hand off control of all created eBPF maps to an agent.\n");
@@ -61,6 +55,24 @@ static void explain(void)
        fprintf(stderr, "NOTE: CLASSID is parsed as hexadecimal input.\n");
 }
 
+static void bpf_cbpf_cb(void *nl, const struct sock_filter *ops, int ops_len)
+{
+       addattr16(nl, MAX_MSG, TCA_BPF_OPS_LEN, ops_len);
+       addattr_l(nl, MAX_MSG, TCA_BPF_OPS, ops,
+                 ops_len * sizeof(struct sock_filter));
+}
+
+static void bpf_ebpf_cb(void *nl, int fd, const char *annotation)
+{
+       addattr32(nl, MAX_MSG, TCA_BPF_FD, fd);
+       addattrstrz(nl, MAX_MSG, TCA_BPF_NAME, annotation);
+}
+
+static const struct bpf_cfg_ops bpf_cb_ops = {
+       .cbpf_cb = bpf_cbpf_cb,
+       .ebpf_cb = bpf_ebpf_cb,
+};
+
 static int bpf_parse_opt(struct filter_util *qu, char *handle,
                         int argc, char **argv, struct nlmsghdr *n)
 {
@@ -68,6 +80,7 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle,
        struct tcmsg *t = NLMSG_DATA(n);
        unsigned int bpf_gen_flags = 0;
        unsigned int bpf_flags = 0;
+       struct bpf_cfg_in cfg = {};
        bool seen_run = false;
        struct rtattr *tail;
        int ret = 0;
@@ -90,11 +103,17 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle,
                        NEXT_ARG();
 opt_bpf:
                        seen_run = true;
-                       if (bpf_parse_common(&argc, &argv, nla_tbl, bpf_type,
-                                            &bpf_obj, &bpf_uds_name, n)) {
-                               fprintf(stderr, "Failed to retrieve (e)BPF data!\n");
+                       cfg.argc = argc;
+                       cfg.argv = argv;
+
+                       if (bpf_parse_common(bpf_type, &cfg, &bpf_cb_ops, n))
                                return -1;
-                       }
+
+                       argc = cfg.argc;
+                       argv = cfg.argv;
+
+                       bpf_obj = cfg.object;
+                       bpf_uds_name = cfg.uds;
                } else if (matches(*argv, "classid") == 0 ||
                           matches(*argv, "flowid") == 0) {
                        unsigned int handle;
@@ -143,7 +162,7 @@ opt_bpf:
 
        if (bpf_gen_flags)
                addattr32(n, MAX_MSG, TCA_BPF_FLAGS_GEN, bpf_gen_flags);
-       if (bpf_obj && bpf_flags)
+       if (bpf_flags)
                addattr32(n, MAX_MSG, TCA_BPF_FLAGS, bpf_flags);
 
        tail->rta_len = (((void *)n) + n->nlmsg_len) - (void *)tail;
@@ -175,8 +194,6 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
 
        if (tb[TCA_BPF_NAME])
                fprintf(f, "%s ", rta_getattr_str(tb[TCA_BPF_NAME]));
-       else if (tb[TCA_BPF_FD])
-               fprintf(f, "pfd %u ", rta_getattr_u32(tb[TCA_BPF_FD]));
 
        if (tb[TCA_BPF_FLAGS]) {
                unsigned int flags = rta_getattr_u32(tb[TCA_BPF_FLAGS]);
@@ -195,20 +212,17 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
                        fprintf(f, "skip_sw ");
        }
 
-       if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN]) {
+       if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN])
                bpf_print_ops(f, tb[TCA_BPF_OPS],
                              rta_getattr_u16(tb[TCA_BPF_OPS_LEN]));
-               fprintf(f, "\n");
-       }
 
        if (tb[TCA_BPF_POLICE]) {
                fprintf(f, "\n");
                tc_print_police(f, tb[TCA_BPF_POLICE]);
        }
 
-       if (tb[TCA_BPF_ACT]) {
+       if (tb[TCA_BPF_ACT])
                tc_print_action(f, tb[TCA_BPF_ACT]);
-       }
 
        return 0;
 }
index 9bf2a85ea24b822e2ba6fc64c809e3d528cdac11..e26b85d007369eade137b4751db9c9859045345a 100644 (file)
 #include <linux/tc_act/tc_bpf.h>
 
 #include "utils.h"
+
 #include "tc_util.h"
-#include "tc_bpf.h"
+#include "bpf_util.h"
 
 static const enum bpf_prog_type bpf_type = BPF_PROG_TYPE_SCHED_ACT;
 
-static const int nla_tbl[BPF_NLA_MAX] = {
-       [BPF_NLA_OPS_LEN]       = TCA_ACT_BPF_OPS_LEN,
-       [BPF_NLA_OPS]           = TCA_ACT_BPF_OPS,
-       [BPF_NLA_FD]            = TCA_ACT_BPF_FD,
-       [BPF_NLA_NAME]          = TCA_ACT_BPF_NAME,
-};
-
 static void explain(void)
 {
        fprintf(stderr, "Usage: ... bpf ... [ index INDEX ]\n");
@@ -50,7 +44,7 @@ static void explain(void)
        fprintf(stderr, "pinned eBPF program.\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "Where ACT_NAME refers to the section name containing the\n");
-       fprintf(stderr, "action (default \'%s\').\n", bpf_default_section(bpf_type));
+       fprintf(stderr, "action (default \'%s\').\n", bpf_prog_to_default_section(bpf_type));
        fprintf(stderr, "\n");
        fprintf(stderr, "Where UDS_FILE points to a unix domain socket file in order\n");
        fprintf(stderr, "to hand off control of all created eBPF maps to an agent.\n");
@@ -59,11 +53,30 @@ static void explain(void)
        fprintf(stderr, "explicitly specifies an action index upon creation.\n");
 }
 
+static void bpf_cbpf_cb(void *nl, const struct sock_filter *ops, int ops_len)
+{
+       addattr16(nl, MAX_MSG, TCA_ACT_BPF_OPS_LEN, ops_len);
+       addattr_l(nl, MAX_MSG, TCA_ACT_BPF_OPS, ops,
+                 ops_len * sizeof(struct sock_filter));
+}
+
+static void bpf_ebpf_cb(void *nl, int fd, const char *annotation)
+{
+       addattr32(nl, MAX_MSG, TCA_ACT_BPF_FD, fd);
+       addattrstrz(nl, MAX_MSG, TCA_ACT_BPF_NAME, annotation);
+}
+
+static const struct bpf_cfg_ops bpf_cb_ops = {
+       .cbpf_cb = bpf_cbpf_cb,
+       .ebpf_cb = bpf_ebpf_cb,
+};
+
 static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv,
                         int tca_id, struct nlmsghdr *n)
 {
        const char *bpf_obj = NULL, *bpf_uds_name = NULL;
        struct tc_act_bpf parm = { .action = TC_ACT_PIPE };
+       struct bpf_cfg_in cfg = {};
        bool seen_run = false;
        struct rtattr *tail;
        int argc, ret = 0;
@@ -85,11 +98,17 @@ static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv,
                        NEXT_ARG();
 opt_bpf:
                        seen_run = true;
-                       if (bpf_parse_common(&argc, &argv, nla_tbl, bpf_type,
-                                            &bpf_obj, &bpf_uds_name, n)) {
-                               fprintf(stderr, "Failed to retrieve (e)BPF data!\n");
+                       cfg.argc = argc;
+                       cfg.argv = argv;
+
+                       if (bpf_parse_common(bpf_type, &cfg, &bpf_cb_ops, n))
                                return -1;
-                       }
+
+                       argc = cfg.argc;
+                       argv = cfg.argv;
+
+                       bpf_obj = cfg.object;
+                       bpf_uds_name = cfg.uds;
                } else if (matches(*argv, "help") == 0) {
                        explain();
                        return -1;
@@ -151,8 +170,6 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
 
        if (tb[TCA_ACT_BPF_NAME])
                fprintf(f, "%s ", rta_getattr_str(tb[TCA_ACT_BPF_NAME]));
-       else if (tb[TCA_ACT_BPF_FD])
-               fprintf(f, "pfd %u ", rta_getattr_u32(tb[TCA_ACT_BPF_FD]));
 
        if (tb[TCA_ACT_BPF_OPS] && tb[TCA_ACT_BPF_OPS_LEN]) {
                bpf_print_ops(f, tb[TCA_ACT_BPF_OPS],