2004-04-16 04:56:59 +08:00
|
|
|
/*
|
|
|
|
* tc.c "tc" utility frontend.
|
|
|
|
*
|
|
|
|
* 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
|
|
|
*
|
|
|
|
* Fixes:
|
|
|
|
*
|
|
|
|
* Petri Mattila <petri@prihateam.fi> 990308: wrong memset's resulted in faults
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "SNAPSHOT.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "tc_util.h"
|
|
|
|
#include "tc_common.h"
|
2014-12-25 05:04:11 +08:00
|
|
|
#include "namespace.h"
|
2004-04-16 04:56:59 +08:00
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
int show_stats;
|
|
|
|
int show_details;
|
|
|
|
int show_raw;
|
|
|
|
int show_pretty;
|
|
|
|
int show_graph;
|
2015-09-24 07:40:04 +08:00
|
|
|
int timestamp;
|
2008-05-10 06:42:34 +08:00
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
int batch_mode;
|
|
|
|
int resolve_hosts;
|
|
|
|
int use_iec;
|
|
|
|
int force;
|
|
|
|
bool use_names;
|
2015-03-04 00:41:18 +08:00
|
|
|
|
|
|
|
static char *conf_file;
|
|
|
|
|
2005-03-15 03:34:12 +08:00
|
|
|
struct rtnl_handle rth;
|
2004-04-16 04:56:59 +08:00
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
static void *BODY; /* cached handle dlopen(NULL) */
|
|
|
|
static struct qdisc_util *qdisc_list;
|
|
|
|
static struct filter_util *filter_list;
|
2004-04-16 04:56:59 +08:00
|
|
|
|
2006-12-06 02:10:22 +08:00
|
|
|
static int print_noqopt(struct qdisc_util *qu, FILE *f,
|
2004-09-01 01:45:21 +08:00
|
|
|
struct rtattr *opt)
|
2004-04-16 04:56:59 +08:00
|
|
|
{
|
|
|
|
if (opt && RTA_PAYLOAD(opt))
|
2006-12-06 02:10:22 +08:00
|
|
|
fprintf(f, "[Unknown qdisc, optlen=%u] ",
|
2016-03-22 02:48:36 +08:00
|
|
|
(unsigned int) RTA_PAYLOAD(opt));
|
2004-04-16 04:56:59 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
|
|
|
|
{
|
|
|
|
if (argc) {
|
|
|
|
fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle)
|
|
|
|
{
|
|
|
|
if (opt && RTA_PAYLOAD(opt))
|
2006-12-06 02:10:22 +08:00
|
|
|
fprintf(f, "fh %08x [Unknown filter, optlen=%u] ",
|
2016-03-22 02:48:36 +08:00
|
|
|
fhandle, (unsigned int) RTA_PAYLOAD(opt));
|
2004-04-16 04:56:59 +08:00
|
|
|
else if (fhandle)
|
|
|
|
fprintf(f, "fh %08x ", fhandle);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_nofopt(struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n)
|
|
|
|
{
|
|
|
|
__u32 handle;
|
|
|
|
|
|
|
|
if (argc) {
|
|
|
|
fprintf(stderr, "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (fhandle) {
|
|
|
|
struct tcmsg *t = NLMSG_DATA(n);
|
2016-03-22 02:48:36 +08:00
|
|
|
|
2004-04-16 04:56:59 +08:00
|
|
|
if (get_u32(&handle, fhandle, 16)) {
|
|
|
|
fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
t->tcm_handle = handle;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-06-03 04:22:08 +08:00
|
|
|
struct qdisc_util *get_qdisc_kind(const char *str)
|
2004-04-16 04:56:59 +08:00
|
|
|
{
|
|
|
|
void *dlh;
|
|
|
|
char buf[256];
|
|
|
|
struct qdisc_util *q;
|
|
|
|
|
|
|
|
for (q = qdisc_list; q; q = q->next)
|
|
|
|
if (strcmp(q->id, str) == 0)
|
|
|
|
return q;
|
|
|
|
|
2007-06-21 06:27:22 +08:00
|
|
|
snprintf(buf, sizeof(buf), "%s/q_%s.so", get_tc_lib(), str);
|
2004-04-16 04:56:59 +08:00
|
|
|
dlh = dlopen(buf, RTLD_LAZY);
|
2004-07-03 01:47:53 +08:00
|
|
|
if (!dlh) {
|
|
|
|
/* look in current binary, only open once */
|
2004-04-16 04:56:59 +08:00
|
|
|
dlh = BODY;
|
|
|
|
if (dlh == NULL) {
|
|
|
|
dlh = BODY = dlopen(NULL, RTLD_LAZY);
|
|
|
|
if (dlh == NULL)
|
|
|
|
goto noexist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-29 02:35:49 +08:00
|
|
|
snprintf(buf, sizeof(buf), "%s_qdisc_util", str);
|
2004-04-16 04:56:59 +08:00
|
|
|
q = dlsym(dlh, buf);
|
|
|
|
if (q == NULL)
|
|
|
|
goto noexist;
|
|
|
|
|
|
|
|
reg:
|
|
|
|
q->next = qdisc_list;
|
|
|
|
qdisc_list = q;
|
|
|
|
return q;
|
|
|
|
|
|
|
|
noexist:
|
|
|
|
q = malloc(sizeof(*q));
|
|
|
|
if (q) {
|
2004-09-01 01:45:21 +08:00
|
|
|
|
2004-04-16 04:56:59 +08:00
|
|
|
memset(q, 0, sizeof(*q));
|
2004-09-01 01:45:21 +08:00
|
|
|
q->id = strcpy(malloc(strlen(str)+1), str);
|
2004-04-16 04:56:59 +08:00
|
|
|
q->parse_qopt = parse_noqopt;
|
|
|
|
q->print_qopt = print_noqopt;
|
|
|
|
goto reg;
|
|
|
|
}
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-03 04:22:08 +08:00
|
|
|
struct filter_util *get_filter_kind(const char *str)
|
2004-04-16 04:56:59 +08:00
|
|
|
{
|
|
|
|
void *dlh;
|
|
|
|
char buf[256];
|
|
|
|
struct filter_util *q;
|
|
|
|
|
|
|
|
for (q = filter_list; q; q = q->next)
|
|
|
|
if (strcmp(q->id, str) == 0)
|
|
|
|
return q;
|
|
|
|
|
2007-06-21 06:27:22 +08:00
|
|
|
snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
|
2004-04-16 04:56:59 +08:00
|
|
|
dlh = dlopen(buf, RTLD_LAZY);
|
|
|
|
if (dlh == NULL) {
|
|
|
|
dlh = BODY;
|
|
|
|
if (dlh == NULL) {
|
|
|
|
dlh = BODY = dlopen(NULL, RTLD_LAZY);
|
|
|
|
if (dlh == NULL)
|
|
|
|
goto noexist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-29 02:35:49 +08:00
|
|
|
snprintf(buf, sizeof(buf), "%s_filter_util", str);
|
2004-04-16 04:56:59 +08:00
|
|
|
q = dlsym(dlh, buf);
|
|
|
|
if (q == NULL)
|
|
|
|
goto noexist;
|
|
|
|
|
|
|
|
reg:
|
|
|
|
q->next = filter_list;
|
|
|
|
filter_list = q;
|
|
|
|
return q;
|
|
|
|
noexist:
|
|
|
|
q = malloc(sizeof(*q));
|
|
|
|
if (q) {
|
|
|
|
memset(q, 0, sizeof(*q));
|
|
|
|
strncpy(q->id, str, 15);
|
|
|
|
q->parse_fopt = parse_nofopt;
|
|
|
|
q->print_fopt = print_nofopt;
|
|
|
|
goto reg;
|
|
|
|
}
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
static void usage(void)
|
2004-04-16 04:56:59 +08:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
|
2008-08-07 22:45:33 +08:00
|
|
|
" tc [-force] -batch filename\n"
|
2016-03-22 02:48:36 +08:00
|
|
|
"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"
|
2015-03-04 00:41:18 +08:00
|
|
|
" -nm | -nam[es] | { -cf | -conf } path }\n");
|
2005-03-15 06:19:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2006-08-09 02:55:15 +08:00
|
|
|
if (matches(*argv, "monitor") == 0)
|
|
|
|
return do_tcmonitor(argc-1, argv+1);
|
tc: built-in eBPF exec proxy
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>
2015-04-17 03:20:06 +08:00
|
|
|
if (matches(*argv, "exec") == 0)
|
|
|
|
return do_exec(argc-1, argv+1);
|
2005-03-15 06:19:16 +08:00
|
|
|
if (matches(*argv, "help") == 0) {
|
|
|
|
usage();
|
|
|
|
return 0;
|
|
|
|
}
|
2006-12-06 02:10:22 +08:00
|
|
|
|
|
|
|
fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n",
|
2005-03-15 06:19:16 +08:00
|
|
|
*argv);
|
2005-03-15 03:02:41 +08:00
|
|
|
return -1;
|
2004-04-16 04:56:59 +08:00
|
|
|
}
|
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
static int batch(const char *name)
|
2004-04-16 04:56:59 +08:00
|
|
|
{
|
2005-03-15 06:19:16 +08:00
|
|
|
char *line = NULL;
|
|
|
|
size_t len = 0;
|
2005-03-19 03:40:55 +08:00
|
|
|
int ret = 0;
|
2005-03-15 06:19:16 +08:00
|
|
|
|
2013-07-17 01:04:05 +08:00
|
|
|
batch_mode = 1;
|
2005-06-24 01:32:22 +08:00
|
|
|
if (name && strcmp(name, "-") != 0) {
|
2005-03-15 06:19:16 +08:00
|
|
|
if (freopen(name, "r", stdin) == NULL) {
|
2008-02-08 14:10:14 +08:00
|
|
|
fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
|
2005-03-15 06:19:16 +08:00
|
|
|
name, strerror(errno));
|
2005-03-15 03:02:41 +08:00
|
|
|
return -1;
|
2004-04-16 04:56:59 +08:00
|
|
|
}
|
2005-03-15 06:19:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
tc_core_init();
|
|
|
|
|
|
|
|
if (rtnl_open(&rth, 0) < 0) {
|
|
|
|
fprintf(stderr, "Cannot open rtnetlink\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-09-02 03:21:50 +08:00
|
|
|
cmdlineno = 0;
|
2005-03-19 03:40:55 +08:00
|
|
|
while (getcmdline(&line, &len, stdin) != -1) {
|
|
|
|
char *largv[100];
|
|
|
|
int largc;
|
2004-04-16 04:56:59 +08:00
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
largc = makeargs(line, largv, 100);
|
2005-03-19 03:40:55 +08:00
|
|
|
if (largc == 0)
|
|
|
|
continue; /* blank line */
|
2004-04-16 04:56:59 +08:00
|
|
|
|
2005-03-19 03:40:55 +08:00
|
|
|
if (do_cmd(largc, largv)) {
|
2005-09-02 03:21:50 +08:00
|
|
|
fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
|
2005-03-19 03:40:55 +08:00
|
|
|
ret = 1;
|
|
|
|
if (!force)
|
|
|
|
break;
|
2004-04-16 04:56:59 +08:00
|
|
|
}
|
2005-03-15 06:19:16 +08:00
|
|
|
}
|
2005-09-22 03:33:17 +08:00
|
|
|
if (line)
|
|
|
|
free(line);
|
2005-03-15 03:34:12 +08:00
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
rtnl_close(&rth);
|
|
|
|
return ret;
|
|
|
|
}
|
2005-03-15 03:34:12 +08:00
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int ret;
|
2013-07-17 01:04:05 +08:00
|
|
|
char *batch_file = NULL;
|
2004-04-16 04:56:59 +08:00
|
|
|
|
|
|
|
while (argc > 1) {
|
|
|
|
if (argv[1][0] != '-')
|
|
|
|
break;
|
|
|
|
if (matches(argv[1], "-stats") == 0 ||
|
2004-06-29 04:42:59 +08:00
|
|
|
matches(argv[1], "-statistics") == 0) {
|
2004-04-16 04:56:59 +08:00
|
|
|
++show_stats;
|
|
|
|
} else if (matches(argv[1], "-details") == 0) {
|
|
|
|
++show_details;
|
|
|
|
} else if (matches(argv[1], "-raw") == 0) {
|
|
|
|
++show_raw;
|
2008-05-10 06:42:34 +08:00
|
|
|
} else if (matches(argv[1], "-pretty") == 0) {
|
|
|
|
++show_pretty;
|
2014-12-26 08:10:06 +08:00
|
|
|
} else if (matches(argv[1], "-graph") == 0) {
|
|
|
|
show_graph = 1;
|
2004-04-16 04:56:59 +08:00
|
|
|
} else if (matches(argv[1], "-Version") == 0) {
|
|
|
|
printf("tc utility, iproute2-ss%s\n", SNAPSHOT);
|
2005-03-15 03:02:41 +08:00
|
|
|
return 0;
|
2004-06-29 04:42:59 +08:00
|
|
|
} else if (matches(argv[1], "-iec") == 0) {
|
|
|
|
++use_iec;
|
2004-04-16 04:56:59 +08:00
|
|
|
} else if (matches(argv[1], "-help") == 0) {
|
|
|
|
usage();
|
2005-03-15 06:19:16 +08:00
|
|
|
return 0;
|
2005-03-19 03:40:55 +08:00
|
|
|
} else if (matches(argv[1], "-force") == 0) {
|
|
|
|
++force;
|
2015-03-04 00:41:18 +08:00
|
|
|
} else if (matches(argv[1], "-batch") == 0) {
|
2005-03-19 03:40:55 +08:00
|
|
|
argc--; argv++;
|
2013-07-17 01:04:05 +08:00
|
|
|
if (argc <= 1)
|
|
|
|
usage();
|
|
|
|
batch_file = argv[1];
|
2014-12-25 05:04:11 +08:00
|
|
|
} else if (matches(argv[1], "-netns") == 0) {
|
|
|
|
NEXT_ARG();
|
|
|
|
if (netns_switch(argv[1]))
|
|
|
|
return -1;
|
2015-03-04 00:41:18 +08:00
|
|
|
} else if (matches(argv[1], "-names") == 0 ||
|
|
|
|
matches(argv[1], "-nm") == 0) {
|
|
|
|
use_names = true;
|
|
|
|
} else if (matches(argv[1], "-cf") == 0 ||
|
|
|
|
matches(argv[1], "-conf") == 0) {
|
|
|
|
NEXT_ARG();
|
|
|
|
conf_file = argv[1];
|
2015-09-24 07:40:04 +08:00
|
|
|
} else if (matches(argv[1], "-timestamp") == 0) {
|
|
|
|
timestamp++;
|
|
|
|
} else if (matches(argv[1], "-tshort") == 0) {
|
|
|
|
++timestamp;
|
|
|
|
++timestamp_short;
|
2004-04-16 04:56:59 +08:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]);
|
2005-03-15 03:02:41 +08:00
|
|
|
return -1;
|
2004-04-16 04:56:59 +08:00
|
|
|
}
|
|
|
|
argc--; argv++;
|
|
|
|
}
|
|
|
|
|
2013-07-17 01:04:05 +08:00
|
|
|
if (batch_file)
|
|
|
|
return batch(batch_file);
|
2005-03-19 03:40:55 +08:00
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
if (argc <= 1) {
|
|
|
|
usage();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-04-16 04:56:59 +08:00
|
|
|
tc_core_init();
|
2005-03-15 03:34:12 +08:00
|
|
|
if (rtnl_open(&rth, 0) < 0) {
|
|
|
|
fprintf(stderr, "Cannot open rtnetlink\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2004-04-16 04:56:59 +08:00
|
|
|
|
2015-03-04 00:41:18 +08:00
|
|
|
if (use_names && cls_names_init(conf_file)) {
|
|
|
|
ret = -1;
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
ret = do_cmd(argc-1, argv+1);
|
2015-03-04 00:41:18 +08:00
|
|
|
Exit:
|
2005-03-15 03:34:12 +08:00
|
|
|
rtnl_close(&rth);
|
2005-03-15 06:19:16 +08:00
|
|
|
|
2015-03-04 00:41:18 +08:00
|
|
|
if (use_names)
|
|
|
|
cls_names_uninit();
|
|
|
|
|
2005-03-15 06:19:16 +08:00
|
|
|
return ret;
|
2004-04-16 04:56:59 +08:00
|
|
|
}
|