diff --git a/Makefile b/Makefile index fa200ddb..37b68ad8 100644 --- 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`"\";" \ diff --git a/configure b/configure index c978da34..6c431c31 100755 --- 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" diff --git a/include/bpf_api.h b/include/bpf_api.h index 1b250d2e..76426235 100644 --- a/include/bpf_api.h +++ b/include/bpf_api.h @@ -107,9 +107,14 @@ /** 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, diff --git a/tc/tc_bpf.h b/include/bpf_util.h similarity index 63% rename from tc/tc_bpf.h rename to include/bpf_util.h index 30306dea..05baeecd 100644 --- a/tc/tc_bpf.h +++ b/include/bpf_util.h @@ -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 + * Authors: Daniel Borkmann * Jiri Pirko */ -#ifndef _TC_BPF_H_ -#define _TC_BPF_H_ 1 +#ifndef __BPF_UTIL__ +#define __BPF_UTIL__ -#include #include +#include #include +#include +#include #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__ */ diff --git a/lib/Makefile b/lib/Makefile index 52e016db..5b7ec169 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/tc/tc_bpf.c b/lib/bpf.c similarity index 79% rename from tc/tc_bpf.c rename to lib/bpf.c index b390f7e2..8a5b84bf 100644 --- a/tc/tc_bpf.c +++ 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 + * Authors: Daniel Borkmann * Jiri Pirko - * Alexei Starovoitov + * Alexei Starovoitov */ #include @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef HAVE_ELF #include @@ -36,27 +37,54 @@ #include #include -#include -#include -#include - #include #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]; - static const char *mnt; - static const char * const bpf_known_mnts[] = { - BPF_DIR_MNT, - 0, - }; - char bpf_mnt[PATH_MAX] = BPF_DIR_MNT; char bpf_glo_dir[PATH_MAX]; int ret; - if (bpf_mnt_cached) - goto done; + snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s/", + bpf_sub_dir, BPF_DIR_GLOBALS); - mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_mnt, sizeof(bpf_mnt), + 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, + }; + int ret; + + 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_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)); + snprintf(bpf_wrk_dir, sizeof(bpf_wrk_dir), "%s/", mnt); + + ret = bpf_gen_hierarchy(bpf_wrk_dir); + if (ret) { mnt = NULL; goto out; } - 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)); - 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, §ion, 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, §ion, 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; } diff --git a/tc/Makefile b/tc/Makefile index dfa875b5..f986fcb9 100644 --- a/tc/Makefile +++ b/tc/Makefile @@ -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 diff --git a/tc/e_bpf.c b/tc/e_bpf.c index d1f5d87f..84f43e6c 100644 --- a/tc/e_bpf.c +++ b/tc/e_bpf.c @@ -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" diff --git a/tc/f_bpf.c b/tc/f_bpf.c index 665bc661..c4764d8f 100644 --- a/tc/f_bpf.c +++ b/tc/f_bpf.c @@ -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 + * Authors: Daniel Borkmann */ #include @@ -15,18 +15,12 @@ #include #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; } diff --git a/tc/m_bpf.c b/tc/m_bpf.c index 9bf2a85e..e26b85d0 100644 --- a/tc/m_bpf.c +++ b/tc/m_bpf.c @@ -17,18 +17,12 @@ #include #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],