]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
tc: built-in eBPF exec proxy
authorDaniel Borkmann <daniel@iogearbox.net>
Thu, 16 Apr 2015 19:20:06 +0000 (21:20 +0200)
committerStephen Hemminger <shemming@brocade.com>
Mon, 27 Apr 2015 23:39:23 +0000 (16:39 -0700)
This work follows upon commit 6256f8c9e45f ("tc, bpf: finalize eBPF
support for cls and act front-end") and takes up the idea proposed by
Hannes Frederic Sowa to spawn a shell (or any other command) that holds
generated eBPF map file descriptors.

File descriptors, based on their id, are being fetched from the same
unix domain socket as demonstrated in the bpf_agent, the shell spawned
via execvpe(2) and the map fds passed over the environment, and thus
are made available to applications in the fashion of std{in,out,err}
for read/write access, for example in case of iproute2's examples/bpf/:

  # env | grep BPF
  BPF_NUM_MAPS=3
  BPF_MAP1=6        <- BPF_MAP_ID_QUEUE (id 1)
  BPF_MAP0=5        <- BPF_MAP_ID_PROTO (id 0)
  BPF_MAP2=7        <- BPF_MAP_ID_DROPS (id 2)

  # ls -la /proc/self/fd
  [...]
  lrwx------. 1 root root 64 Apr 14 16:46 0 -> /dev/pts/4
  lrwx------. 1 root root 64 Apr 14 16:46 1 -> /dev/pts/4
  lrwx------. 1 root root 64 Apr 14 16:46 2 -> /dev/pts/4
  [...]
  lrwx------. 1 root root 64 Apr 14 16:46 5 -> anon_inode:bpf-map
  lrwx------. 1 root root 64 Apr 14 16:46 6 -> anon_inode:bpf-map
  lrwx------. 1 root root 64 Apr 14 16:46 7 -> anon_inode:bpf-map

The advantage (as opposed to the direct/native usage) is that now the
shell is map fd owner and applications can terminate and easily reattach
to descriptors w/o any kernel changes. Moreover, multiple applications
can easily read/write eBPF maps simultaneously.

To further allow users for experimenting with that, next step is to add
a small helper that can get along with simple data types, so that also
shell scripts can make use of bpf syscall, f.e to read/write into maps.

Generally, this allows for prepopulating maps, or any runtime altering
which could influence eBPF program behaviour (f.e. different run-time
classifications, skb modifications, ...), dumping of statistics, etc.

Reference: http://thread.gmane.org/gmane.linux.network/357471/focus=357860
Suggested-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
14 files changed:
examples/bpf/bpf_agent.c
examples/bpf/bpf_prog.c
tc/Makefile
tc/e_bpf.c [new file with mode: 0644]
tc/f_bpf.c
tc/m_action.c
tc/m_bpf.c
tc/tc.c
tc/tc_bpf.c
tc/tc_bpf.h
tc/tc_common.h
tc/tc_exec.c [new file with mode: 0644]
tc/tc_filter.c
tc/tc_util.h

index 0f481b1af9758751e431d3c421fa32fff9c77791..426b8800bf4e2f55381c1ed58939fccfeda90649 100644 (file)
@@ -24,6 +24,8 @@
  *   -- Happy eBPF hacking! ;)
  */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -31,6 +33,7 @@
 #include <unistd.h>
 #include <stdint.h>
 #include <assert.h>
+
 #include <sys/un.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -102,6 +105,23 @@ static void bpf_dump_proto(int fd)
        printf("\n");
 }
 
+static void bpf_dump_map_data(int *tfd)
+{
+       int i;
+
+       for (i = 0; i < 30; i++) {
+               const int period = 5;
+
+               printf("data, period: %dsec\n", period);
+
+               bpf_dump_drops(tfd[BPF_MAP_ID_DROPS]);
+               bpf_dump_queue(tfd[BPF_MAP_ID_QUEUE]);
+               bpf_dump_proto(tfd[BPF_MAP_ID_PROTO]);
+
+               sleep(period);
+       }
+}
+
 static void bpf_info_loop(int *fds, struct bpf_map_aux *aux)
 {
        int i, tfd[BPF_MAP_ID_MAX];
@@ -122,16 +142,22 @@ static void bpf_info_loop(int *fds, struct bpf_map_aux *aux)
                tfd[aux->ent[i].id] = fds[i];
        }
 
-       for (i = 0; i < 30; i++) {
-               int period = 5;
+       bpf_dump_map_data(tfd);
+}
 
-               printf("data, period: %dsec\n", period);
+static void bpf_map_get_from_env(int *tfd)
+{
+       char key[64], *val;
+       int i;
 
-               bpf_dump_drops(tfd[BPF_MAP_ID_DROPS]);
-               bpf_dump_queue(tfd[BPF_MAP_ID_QUEUE]);
-               bpf_dump_proto(tfd[BPF_MAP_ID_PROTO]);
+       for (i = 0; i < BPF_MAP_ID_MAX; i++) {
+               memset(key, 0, sizeof(key));
+               snprintf(key, sizeof(key), "BPF_MAP%d", i);
 
-               sleep(period);
+               val = secure_getenv(key);
+               assert(val != NULL);
+
+               tfd[i] = atoi(val);
        }
 }
 
@@ -186,9 +212,17 @@ int main(int argc, char **argv)
        struct sockaddr_un addr;
        int fd, ret, i;
 
-       if (argc < 2) {
-               fprintf(stderr, "Usage: %s <path-uds>\n", argv[0]);
-               exit(1);
+       /* When arguments are being passed, we take it as a path
+        * to a Unix domain socket, otherwise we grab the fds
+        * from the environment to demonstrate both possibilities.
+        */
+       if (argc == 1) {
+               int tfd[BPF_MAP_ID_MAX];
+
+               bpf_map_get_from_env(tfd);
+               bpf_dump_map_data(tfd);
+
+               return 0;
        }
 
        fd = socket(AF_UNIX, SOCK_DGRAM, 0);
@@ -218,6 +252,7 @@ int main(int argc, char **argv)
 
        for (i = 0; i < aux.num_ent; i++)
                close(fds[i]);
+
        close(fd);
        return 0;
 }
index ca9b54f9dcdce5c8981fc155b7decaac070fc11f..4dc00c3e88ae636e0911be2fa143b56dc9bf479e 100644 (file)
  *    random type none pass val 0
  *    index 38 ref 1 bind 1
  *
+ * Notes on BPF agent:
+ *
+ * In the above example, the bpf_agent creates the unix domain socket
+ * natively. "tc exec" can also spawn a shell and hold the socktes there:
+ *
+ *  # tc exec bpf imp /tmp/bpf-uds
+ *  # tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf-uds flowid 1:1 \
+ *                             action bpf obj bpf.o sec action-mark            \
+ *                             action bpf obj bpf.o sec action-rand ok
+ *  sh-4.2# (shell spawned from tc exec)
+ *  sh-4.2# bpf_agent
+ *  [...]
+ *
+ * This will read out fds over environment and produce the same data dump
+ * as below. This has the advantage that the spawned shell owns the fds
+ * and thus if the agent is restarted, it can reattach to the same fds, also
+ * various programs can easily read/modify the data simultaneously from user
+ * space side.
+ *
+ * If the shell is unnecessary, the agent can also just be spawned directly
+ * via tc exec:
+ *
+ *  # tc exec bpf imp /tmp/bpf-uds run bpf_agent
+ *  # tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf-uds flowid 1:1 \
+ *                             action bpf obj bpf.o sec action-mark            \
+ *                             action bpf obj bpf.o sec action-rand ok
+ *
  * BPF agent example output:
  *
  * ver: 1
index f9916c3001761869d5d3bf27dc0eb029cfebeead..96e84937df6a732cba0583d445b7694721cd84cb 100644 (file)
@@ -1,6 +1,6 @@
-TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o \
-       tc_monitor.o tc_bpf.o m_police.o m_estimator.o m_action.o \
-       m_ematch.o emp_ematch.yacc.o emp_ematch.lex.o
+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 \
+       emp_ematch.yacc.o emp_ematch.lex.o
 
 include ../Config
 
@@ -63,6 +63,7 @@ TCMODULES += q_fq_codel.o
 TCMODULES += q_fq.o
 TCMODULES += q_pie.o
 TCMODULES += q_hhf.o
+TCMODULES += e_bpf.o
 
 ifeq ($(TC_CONFIG_IPSET), y)
   ifeq ($(TC_CONFIG_XT), y)
diff --git a/tc/e_bpf.c b/tc/e_bpf.c
new file mode 100644 (file)
index 0000000..218ba40
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * e_bpf.c     BPF exec proxy
+ *
+ *             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 <daniel@iogearbox.net>
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#include "tc_util.h"
+#include "tc_bpf.h"
+
+#include "bpf_elf.h"
+#include "bpf_scm.h"
+
+#define BPF_DEFAULT_CMD        "/bin/sh"
+
+static char *argv_default[] = { BPF_DEFAULT_CMD, NULL };
+
+static void explain(void)
+{
+       fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n\n");
+       fprintf(stderr, "Where UDS_FILE provides the name of a unix domain socket file\n");
+       fprintf(stderr, "to import eBPF maps and the optional CMD denotes the command\n");
+       fprintf(stderr, "to be executed (default: \'%s\').\n", BPF_DEFAULT_CMD);
+}
+
+static int bpf_num_env_entries(void)
+{
+       char **envp;
+       int num;
+
+       for (num = 0, envp = environ; *envp != NULL; envp++)
+               num++;
+       return num;
+}
+
+static int parse_bpf(struct exec_util *eu, int argc, char **argv)
+{
+       char **argv_run = argv_default, **envp_run, *tmp;
+       int ret, i, env_old, env_num, env_map;
+       const char *bpf_uds_name = NULL;
+       int fds[BPF_SCM_MAX_FDS];
+       struct bpf_map_aux aux;
+
+       if (argc == 0)
+               return 0;
+
+       while (argc > 0) {
+               if (matches(*argv, "run") == 0) {
+                       NEXT_ARG();
+                       argv_run = argv;
+                       break;
+               } else if (matches(*argv, "import") == 0 ||
+                          matches(*argv, "imp") == 0) {
+                       NEXT_ARG();
+                       bpf_uds_name = *argv;
+               } else {
+                       explain();
+                       return -1;
+               }
+
+               argc--;
+               argv++;
+       }
+
+       if (!bpf_uds_name) {
+               fprintf(stderr, "bpf: No import parameter provided!\n");
+               explain();
+               return -1;
+       }
+
+       if (argv_run != argv_default && argc == 0) {
+               fprintf(stderr, "bpf: No run command provided!\n");
+               explain();
+               return -1;
+       }
+
+       memset(fds, 0, sizeof(fds));
+       memset(&aux, 0, sizeof(aux));
+
+       ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds));
+       if (ret < 0) {
+               fprintf(stderr, "bpf: Could not receive fds!\n");
+               return -1;
+       }
+
+       if (aux.num_ent == 0) {
+               envp_run = environ;
+               goto out;
+       }
+
+       env_old = bpf_num_env_entries();
+       env_num = env_old + aux.num_ent + 2;
+       env_map = env_old + 1;
+
+       envp_run = malloc(sizeof(*envp_run) * env_num);
+       if (!envp_run) {
+               fprintf(stderr, "bpf: No memory left to allocate env!\n");
+               goto err;
+       }
+
+       for (i = 0; i < env_old; i++)
+               envp_run[i] = environ[i];
+
+       ret = asprintf(&tmp, "BPF_NUM_MAPS=%u", aux.num_ent);
+       if (ret < 0)
+               goto err_free;
+
+       envp_run[env_old] = tmp;
+
+       for (i = env_map; i < env_num - 1; i++) {
+               ret = asprintf(&tmp, "BPF_MAP%u=%u",
+                              aux.ent[i - env_map].id,
+                              fds[i - env_map]);
+               if (ret < 0)
+                       goto err_free_env;
+
+               envp_run[i] = tmp;
+       }
+
+       envp_run[env_num - 1] = NULL;
+out:
+       return execvpe(argv_run[0], argv_run, envp_run);
+
+err_free_env:
+       for (--i; i >= env_old; i--)
+               free(envp_run[i]);
+err_free:
+       free(envp_run);
+err:
+       for (i = 0; i < aux.num_ent; i++)
+               close(fds[i]);
+       return -1;
+}
+
+struct exec_util bpf_exec_util = {
+       .id = "bpf",
+       .parse_eopt = parse_bpf,
+};
index 8bdd6026d76e90cf9599d480ff08eff08d7ff9ac..11d6db0cab7ef16be7b92c32f6d245d37e489653 100644 (file)
@@ -205,7 +205,7 @@ opt_bpf:
        tail->rta_len = (((void *)n) + n->nlmsg_len) - (void *)tail;
 
        if (bpf_uds_name)
-               ret = bpf_handoff_map_fds(bpf_uds_name, bpf_obj);
+               ret = bpf_send_map_fds(bpf_uds_name, bpf_obj);
 
        return ret;
 }
index 486123ea5c4a3009999984eab9ec0499c4991deb..7a83f0d57b7529e5438fab860865784b597e8f1a 100644 (file)
@@ -54,7 +54,7 @@ static void act_usage(void)
                        "\tACTSPEC := action <ACTDETAIL> [INDEXSPEC]\n"
                        "\tINDEXSPEC := index <32 bit indexvalue>\n"
                        "\tACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
-                       "\t\tExample ACTNAME is gact, mirred etc\n"
+                       "\t\tExample ACTNAME is gact, mirred, bpf, etc\n"
                        "\t\tEach action has its own parameters (ACTPARAMS)\n"
                        "\n");
 
index c817579162b6e72c5c9ee752d749efbe30138263..16468f264d9c0ff61a67d37c833fdadfabf655dd 100644 (file)
@@ -216,7 +216,7 @@ opt_bpf:
        *argv_p = argv;
 
        if (bpf_uds_name)
-               ret = bpf_handoff_map_fds(bpf_uds_name, bpf_obj);
+               ret = bpf_send_map_fds(bpf_uds_name, bpf_obj);
 
        return ret;
 }
diff --git a/tc/tc.c b/tc/tc.c
index 22c3be4101a2fd015b43cebde4e7b16da6cd49ce..46ff3714a2e9b687d2a7183fca8ed378faef5e47 100644 (file)
--- a/tc/tc.c
+++ b/tc/tc.c
@@ -190,7 +190,7 @@ static void usage(void)
 {
        fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
                        "       tc [-force] -batch filename\n"
-                       "where  OBJECT := { qdisc | class | filter | action | monitor }\n"
+                       "where  OBJECT := { qdisc | class | filter | action | monitor | exec }\n"
                        "       OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | "
                        "-n[etns] name |\n"
                        "                    -nm | -nam[es] | { -cf | -conf } path }\n");
@@ -200,19 +200,16 @@ static int do_cmd(int argc, char **argv)
 {
        if (matches(*argv, "qdisc") == 0)
                return do_qdisc(argc-1, argv+1);
-
        if (matches(*argv, "class") == 0)
                return do_class(argc-1, argv+1);
-
        if (matches(*argv, "filter") == 0)
                return do_filter(argc-1, argv+1);
-
        if (matches(*argv, "actions") == 0)
                return do_action(argc-1, argv+1);
-
        if (matches(*argv, "monitor") == 0)
                return do_tcmonitor(argc-1, argv+1);
-
+       if (matches(*argv, "exec") == 0)
+               return do_exec(argc-1, argv+1);
        if (matches(*argv, "help") == 0) {
                usage();
                return 0;
index 326d0986d9aa1643733e71b694e0810dcf808783..7c282aaba6cb69813b5c65d07049eacc9d1f29ec 100644 (file)
@@ -626,8 +626,8 @@ out:
 }
 
 static int
-bpf_map_set_xmit(int fd, struct sockaddr_un *addr, unsigned int addr_len,
-                const struct bpf_map_data *aux, unsigned int ents)
+bpf_map_set_send(int fd, struct sockaddr_un *addr, unsigned int addr_len,
+                const struct bpf_map_data *aux, unsigned int entries)
 {
        struct bpf_map_set_msg msg;
        int *cmsg_buf, min_fd;
@@ -637,7 +637,7 @@ bpf_map_set_xmit(int fd, struct sockaddr_un *addr, unsigned int addr_len,
        memset(&msg, 0, sizeof(msg));
 
        msg.aux.uds_ver = BPF_SCM_AUX_VER;
-       msg.aux.num_ent = ents;
+       msg.aux.num_ent = entries;
 
        strncpy(msg.aux.obj_name, aux->obj, sizeof(msg.aux.obj_name));
        memcpy(&msg.aux.obj_st, aux->st, sizeof(msg.aux.obj_st));
@@ -645,11 +645,10 @@ bpf_map_set_xmit(int fd, struct sockaddr_un *addr, unsigned int addr_len,
        cmsg_buf = bpf_map_set_init(&msg, addr, addr_len);
        amsg_buf = (char *)msg.aux.ent;
 
-       for (i = 0; i < ents; i += min_fd) {
+       for (i = 0; i < entries; i += min_fd) {
                int ret;
 
-               min_fd = min(BPF_SCM_MAX_FDS * 1U, ents - i);
-
+               min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
                bpf_map_set_init_single(&msg, min_fd);
 
                memcpy(cmsg_buf, &aux->fds[i], sizeof(aux->fds[0]) * min_fd);
@@ -663,7 +662,54 @@ bpf_map_set_xmit(int fd, struct sockaddr_un *addr, unsigned int addr_len,
        return 0;
 }
 
-int bpf_handoff_map_fds(const char *path, const char *obj)
+static int
+bpf_map_set_recv(int fd, int *fds,  struct bpf_map_aux *aux,
+                unsigned int entries)
+{
+       struct bpf_map_set_msg msg;
+       int *cmsg_buf, min_fd;
+       char *amsg_buf, *mmsg_buf;
+       unsigned int needed = 1;
+       int i;
+
+       cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
+       amsg_buf = (char *)msg.aux.ent;
+       mmsg_buf = (char *)&msg.aux;
+
+       for (i = 0; i < min(entries, needed); i += min_fd) {
+               struct cmsghdr *cmsg;
+               int ret;
+
+               min_fd = min(entries, entries - i);
+               bpf_map_set_init_single(&msg, min_fd);
+
+               ret = recvmsg(fd, &msg.hdr, 0);
+               if (ret <= 0)
+                       return ret ? : -1;
+
+               cmsg = CMSG_FIRSTHDR(&msg.hdr);
+               if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
+                       return -EINVAL;
+               if (msg.hdr.msg_flags & MSG_CTRUNC)
+                       return -EIO;
+               if (msg.aux.uds_ver != BPF_SCM_AUX_VER)
+                       return -ENOSYS;
+
+               min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
+               if (min_fd > entries || min_fd <= 0)
+                       return -EINVAL;
+
+               memcpy(&fds[i], cmsg_buf, sizeof(fds[0]) * min_fd);
+               memcpy(&aux->ent[i], amsg_buf, sizeof(aux->ent[0]) * min_fd);
+               memcpy(aux, mmsg_buf, offsetof(struct bpf_map_aux, ent));
+
+               needed = aux->num_ent;
+       }
+
+       return 0;
+}
+
+int bpf_send_map_fds(const char *path, const char *obj)
 {
        struct sockaddr_un addr;
        struct bpf_map_data bpf_aux;
@@ -695,12 +741,46 @@ int bpf_handoff_map_fds(const char *path, const char *obj)
        bpf_aux.obj = obj;
        bpf_aux.st = &bpf_st;
 
-       ret = bpf_map_set_xmit(fd, &addr, sizeof(addr), &bpf_aux,
+       ret = bpf_map_set_send(fd, &addr, sizeof(addr), &bpf_aux,
                               bpf_maps_count());
        if (ret < 0)
-               fprintf(stderr, "Cannot xmit fds to %s: %s\n",
+               fprintf(stderr, "Cannot send fds to %s: %s\n",
+                       path, strerror(errno));
+
+       close(fd);
+       return ret;
+}
+
+int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
+                    unsigned int entries)
+{
+       struct sockaddr_un addr;
+       int fd, ret;
+
+       fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               fprintf(stderr, "Cannot open socket: %s\n",
+                       strerror(errno));
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+       ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+       if (ret < 0) {
+               fprintf(stderr, "Cannot bind to socket: %s\n",
+                       strerror(errno));
+               return -1;
+       }
+
+       ret = bpf_map_set_recv(fd, fds, aux, entries);
+       if (ret < 0)
+               fprintf(stderr, "Cannot recv fds from %s: %s\n",
                        path, strerror(errno));
 
+       unlink(addr.sun_path);
        close(fd);
        return ret;
 }
index 8b214b83f74fce51aa1c9b44d51e5ce4b4da82e8..4a239aab0f390d9ae8b9b8f5d28feca1764b25b5 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdint.h>
 
 #include "utils.h"
+#include "bpf_scm.h"
 
 int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
                     char **bpf_string, bool *need_release,
@@ -36,7 +37,10 @@ const char *bpf_default_section(const enum bpf_prog_type type);
 #ifdef HAVE_ELF
 int bpf_open_object(const char *path, enum bpf_prog_type type,
                    const char *sec);
-int bpf_handoff_map_fds(const char *path, const char *obj);
+
+int bpf_send_map_fds(const char *path, const char *obj);
+int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
+                    unsigned int entries);
 
 static inline __u64 bpf_ptr_to_u64(const void *ptr)
 {
@@ -62,9 +66,16 @@ static inline int bpf_open_object(const char *path, enum bpf_prog_type type,
        return -1;
 }
 
-static inline int bpf_handoff_map_fds(const char *path, const char *obj)
+static inline int bpf_send_map_fds(const char *path, const char *obj)
 {
        return 0;
 }
+
+static inline int bpf_recv_map_fds(const char *path, int *fds,
+                                  struct bpf_map_aux *aux,
+                                  unsigned int entries)
+{
+       return -1;
+}
 #endif /* HAVE_ELF */
 #endif /* _TC_BPF_H_ */
index 96a0e20fe40ff786352dff3920dd22f249910128..a2f389845feee72f207eaf3d444bbea258d56bb7 100644 (file)
@@ -2,11 +2,14 @@
 #define TCA_BUF_MAX    (64*1024)
 
 extern struct rtnl_handle rth;
+
 extern int do_qdisc(int argc, char **argv);
 extern int do_class(int argc, char **argv);
 extern int do_filter(int argc, char **argv);
 extern int do_action(int argc, char **argv);
 extern int do_tcmonitor(int argc, char **argv);
+extern int do_exec(int argc, char **argv);
+
 extern int print_action(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
 extern int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
 extern int print_qdisc(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
diff --git a/tc/tc_exec.c b/tc/tc_exec.c
new file mode 100644 (file)
index 0000000..61be672
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * tc_exec.c   "tc exec".
+ *
+ *             This program is free software; you can redistribute 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 <daniel@iogearbox.net>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include "utils.h"
+
+#include "tc_util.h"
+#include "tc_common.h"
+
+static struct exec_util *exec_list;
+static void *BODY = NULL;
+
+static void usage(void)
+{
+       fprintf(stderr, "Usage: tc exec [ EXEC_TYPE ] [ help | OPTIONS ]\n");
+       fprintf(stderr, "Where:\n");
+       fprintf(stderr, "EXEC_TYPE := { bpf | etc. }\n");
+       fprintf(stderr, "OPTIONS := ... try tc exec <desired EXEC_KIND> help\n");
+}
+
+static int parse_noeopt(struct exec_util *eu, int argc, char **argv)
+{
+       if (argc) {
+               fprintf(stderr, "Unknown exec \"%s\", hence option \"%s\" "
+                       "is unparsable\n", eu->id, *argv);
+               return -1;
+       }
+
+       return 0;
+}
+
+static struct exec_util *get_exec_kind(const char *name)
+{
+       struct exec_util *eu;
+       char buf[256];
+       void *dlh;
+
+       for (eu = exec_list; eu; eu = eu->next)
+               if (strcmp(eu->id, name) == 0)
+                       return eu;
+
+       snprintf(buf, sizeof(buf), "%s/e_%s.so", get_tc_lib(), name);
+       dlh = dlopen(buf, RTLD_LAZY);
+       if (dlh == NULL) {
+               dlh = BODY;
+               if (dlh == NULL) {
+                       dlh = BODY = dlopen(NULL, RTLD_LAZY);
+                       if (dlh == NULL)
+                               goto noexist;
+               }
+       }
+
+       snprintf(buf, sizeof(buf), "%s_exec_util", name);
+       eu = dlsym(dlh, buf);
+       if (eu == NULL)
+               goto noexist;
+reg:
+       eu->next = exec_list;
+       exec_list = eu;
+
+       return eu;
+noexist:
+       eu = malloc(sizeof(*eu));
+       if (eu) {
+               memset(eu, 0, sizeof(*eu));
+               strncpy(eu->id, name, sizeof(eu->id) - 1);
+               eu->parse_eopt = parse_noeopt;
+               goto reg;
+       }
+
+       return eu;
+}
+
+int do_exec(int argc, char **argv)
+{
+       struct exec_util *eu;
+       char kind[16];
+
+       if (argc < 1) {
+               fprintf(stderr, "No command given, try \"tc exec help\".\n");
+               return -1;
+       }
+
+       if (matches(*argv, "help") == 0) {
+               usage();
+               return 0;
+       }
+
+       memset(kind, 0, sizeof(kind));
+       strncpy(kind, *argv, sizeof(kind) - 1);
+
+       eu = get_exec_kind(kind);
+
+       argc--;
+       argv++;
+
+       return eu->parse_eopt(eu, argc, argv);
+}
index 609fbe9b78b294fded2ae3e562d5b1581b1d92b4..c1038a4bc8271b524b685538559ca3bf46967ed9 100644 (file)
@@ -38,7 +38,7 @@ static void usage(void)
        fprintf(stderr, "\n");
        fprintf(stderr, "       tc filter show [ dev STRING ] [ root | parent CLASSID ]\n");
        fprintf(stderr, "Where:\n");
-       fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | fw | route | etc. }\n");
+       fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n");
        fprintf(stderr, "FILTERID := ... format depends on classifier, see there\n");
        fprintf(stderr, "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n");
        return;
index 1be1b5017832489c20c123deba18060086d25c75..61e60b1c55b7081825809f68fd3505d16cf77545 100644 (file)
@@ -19,8 +19,7 @@ enum
 #define TCA_PRIO_MAX    (__TCA_PRIO_MAX - 1)
 #endif
 
-struct qdisc_util
-{
+struct qdisc_util {
        struct  qdisc_util *next;
        const char *id;
        int     (*parse_qopt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n);
@@ -32,8 +31,7 @@ struct qdisc_util
 };
 
 extern __u16 f_proto;
-struct filter_util
-{
+struct filter_util {
        struct filter_util *next;
        char    id[16];
        int     (*parse_fopt)(struct filter_util *qu, char *fhandle, int argc,
@@ -41,8 +39,7 @@ struct filter_util
        int     (*print_fopt)(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle);
 };
 
-struct action_util
-{
+struct action_util {
        struct  action_util *next;
        char    id[16];
        int     (*parse_aopt)(struct action_util *a, int *argc, char ***argv,
@@ -51,6 +48,12 @@ struct action_util
        int     (*print_xstats)(struct action_util *au, FILE *f, struct rtattr *xstats);
 };
 
+struct exec_util {
+       struct  exec_util *next;
+       char    id[16];
+       int     (*parse_eopt)(struct exec_util *eu, int argc, char **argv);
+};
+
 extern const char *get_tc_lib(void);
 
 extern struct qdisc_util *get_qdisc_kind(const char *str);
@@ -69,6 +72,7 @@ extern void print_size(char *buf, int len, __u32 size);
 extern void print_qdisc_handle(char *buf, int len, __u32 h);
 extern void print_time(char *buf, int len, __u32 time);
 extern void print_linklayer(char *buf, int len, unsigned linklayer);
+
 extern char * sprint_rate(__u64 rate, char *buf);
 extern char * sprint_size(__u32 size, char *buf);
 extern char * sprint_qdisc_handle(__u32 h, char *buf);