2004-08-14 07:54:55 +08:00
|
|
|
/*
|
|
|
|
* m_action.c Action Management
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2006-12-06 02:10:22 +08:00
|
|
|
* Authors: J Hadi Salim (hadi@cyberus.ca)
|
|
|
|
*
|
2004-08-14 07:54:55 +08:00
|
|
|
* TODO:
|
|
|
|
* - parse to be passed a filedescriptor for logging purposes
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
|
#include "utils.h"
|
2005-03-15 06:19:16 +08:00
|
|
|
#include "tc_common.h"
|
2004-08-14 07:54:55 +08:00
|
|
|
#include "tc_util.h"
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
static struct action_util *action_list;
|
2004-08-14 07:54:55 +08:00
|
|
|
#ifdef CONFIG_GACT
|
2016-03-22 02:48:36 +08:00
|
|
|
int gact_ld; /* f*ckin backward compatibility */
|
2004-08-14 07:54:55 +08:00
|
|
|
#endif
|
2016-03-22 02:48:36 +08:00
|
|
|
int tab_flush;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
2013-02-13 03:09:03 +08:00
|
|
|
static void act_usage(void)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
2006-08-05 01:59:34 +08:00
|
|
|
/*XXX: In the near future add a action->print_help to improve
|
|
|
|
* usability
|
|
|
|
* This would mean new tc will not be backward compatible
|
|
|
|
* with any action .so from the old days. But if someone really
|
|
|
|
* does that, they would know how to fix this ..
|
|
|
|
*
|
2006-09-26 08:08:40 +08:00
|
|
|
*/
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "usage: tc actions <ACTSPECOP>*\n");
|
2006-08-05 01:59:34 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"Where: \tACTSPECOP := ACR | GD | FL\n"
|
2016-03-22 02:48:36 +08:00
|
|
|
"\tACR := add | change | replace <ACTSPEC>*\n"
|
2006-09-26 08:08:40 +08:00
|
|
|
"\tGD := get | delete | <ACTISPEC>*\n"
|
|
|
|
"\tFL := ls | list | flush | <ACTNAMESPEC>\n"
|
|
|
|
"\tACTNAMESPEC := action <ACTNAME>\n"
|
|
|
|
"\tACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n"
|
|
|
|
"\tACTSPEC := action <ACTDETAIL> [INDEXSPEC]\n"
|
|
|
|
"\tINDEXSPEC := index <32 bit indexvalue>\n"
|
|
|
|
"\tACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
|
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
|
|
|
"\t\tExample ACTNAME is gact, mirred, bpf, etc\n"
|
2006-09-26 08:08:40 +08:00
|
|
|
"\t\tEach action has its own parameters (ACTPARAMS)\n"
|
|
|
|
"\n");
|
|
|
|
|
2006-08-09 03:10:08 +08:00
|
|
|
exit(-1);
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
|
|
|
|
{
|
|
|
|
if (opt && RTA_PAYLOAD(opt))
|
2006-12-06 02:10:22 +08:00
|
|
|
fprintf(f, "[Unknown action, optlen=%u] ",
|
2016-03-22 02:48:36 +08:00
|
|
|
(unsigned int) RTA_PAYLOAD(opt));
|
2004-08-14 07:54:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n)
|
|
|
|
{
|
|
|
|
int argc = *argc_p;
|
|
|
|
char **argv = *argv_p;
|
|
|
|
|
|
|
|
if (argc) {
|
|
|
|
fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Unknown action \"%s\"\n", au->id);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-13 03:09:03 +08:00
|
|
|
static struct action_util *get_action_kind(char *str)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
2005-01-18 08:09:02 +08:00
|
|
|
static void *aBODY;
|
2004-08-14 07:54:55 +08:00
|
|
|
void *dlh;
|
|
|
|
char buf[256];
|
|
|
|
struct action_util *a;
|
|
|
|
#ifdef CONFIG_GACT
|
|
|
|
int looked4gact = 0;
|
|
|
|
restart_s:
|
|
|
|
#endif
|
|
|
|
for (a = action_list; a; a = a->next) {
|
|
|
|
if (strcmp(a->id, str) == 0)
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2010-04-13 02:24:23 +08:00
|
|
|
snprintf(buf, sizeof(buf), "%s/m_%s.so", get_tc_lib(), str);
|
2010-08-02 15:30:33 +08:00
|
|
|
dlh = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
|
2004-08-14 07:54:55 +08:00
|
|
|
if (dlh == NULL) {
|
|
|
|
dlh = aBODY;
|
|
|
|
if (dlh == NULL) {
|
|
|
|
dlh = aBODY = dlopen(NULL, RTLD_LAZY);
|
|
|
|
if (dlh == NULL)
|
|
|
|
goto noexist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-29 02:35:49 +08:00
|
|
|
snprintf(buf, sizeof(buf), "%s_action_util", str);
|
2004-08-14 07:54:55 +08:00
|
|
|
a = dlsym(dlh, buf);
|
|
|
|
if (a == NULL)
|
|
|
|
goto noexist;
|
|
|
|
|
|
|
|
reg:
|
|
|
|
a->next = action_list;
|
|
|
|
action_list = a;
|
|
|
|
return a;
|
|
|
|
|
|
|
|
noexist:
|
|
|
|
#ifdef CONFIG_GACT
|
|
|
|
if (!looked4gact) {
|
|
|
|
looked4gact = 1;
|
2016-03-22 02:48:36 +08:00
|
|
|
strcpy(str, "gact");
|
2004-08-14 07:54:55 +08:00
|
|
|
goto restart_s;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
a = malloc(sizeof(*a));
|
|
|
|
if (a) {
|
|
|
|
memset(a, 0, sizeof(*a));
|
|
|
|
strncpy(a->id, "noact", 15);
|
|
|
|
a->parse_aopt = parse_noaopt;
|
|
|
|
a->print_aopt = print_noaopt;
|
|
|
|
goto reg;
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2013-02-13 03:09:03 +08:00
|
|
|
static int
|
2006-12-06 02:10:22 +08:00
|
|
|
new_cmd(char **argv)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
|
|
|
if ((matches(*argv, "change") == 0) ||
|
2016-03-22 02:48:36 +08:00
|
|
|
(matches(*argv, "replace") == 0) ||
|
|
|
|
(matches(*argv, "delete") == 0) ||
|
|
|
|
(matches(*argv, "get") == 0) ||
|
2004-08-14 07:54:55 +08:00
|
|
|
(matches(*argv, "add") == 0))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
|
|
|
|
{
|
|
|
|
int argc = *argc_p;
|
|
|
|
char **argv = *argv_p;
|
|
|
|
struct rtattr *tail, *tail2;
|
|
|
|
char k[16];
|
|
|
|
int ok = 0;
|
|
|
|
int eap = 0; /* expect action parameters */
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
int prio = 0;
|
|
|
|
|
|
|
|
if (argc <= 0)
|
|
|
|
return -1;
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail = tail2 = NLMSG_TAIL(n);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
addattr_l(n, MAX_MSG, tca_id, NULL, 0);
|
|
|
|
|
|
|
|
while (argc > 0) {
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
memset(k, 0, sizeof(k));
|
2004-08-14 07:54:55 +08:00
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
if (strcmp(*argv, "action") == 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
eap = 1;
|
|
|
|
#ifdef CONFIG_GACT
|
|
|
|
if (!gact_ld) {
|
|
|
|
get_action_kind("gact");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
continue;
|
2014-05-24 20:48:41 +08:00
|
|
|
} else if (strcmp(*argv, "flowid") == 0) {
|
|
|
|
break;
|
|
|
|
} else if (strcmp(*argv, "classid") == 0) {
|
|
|
|
break;
|
2004-08-14 07:54:55 +08:00
|
|
|
} else if (strcmp(*argv, "help") == 0) {
|
|
|
|
return -1;
|
|
|
|
} else if (new_cmd(argv)) {
|
|
|
|
goto done0;
|
|
|
|
} else {
|
|
|
|
struct action_util *a = NULL;
|
2016-03-22 02:48:36 +08:00
|
|
|
|
|
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
2004-08-14 07:54:55 +08:00
|
|
|
eap = 0;
|
2016-03-22 02:48:36 +08:00
|
|
|
if (argc > 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
a = get_action_kind(k);
|
|
|
|
} else {
|
|
|
|
done0:
|
|
|
|
if (ok)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
if (a == NULL) {
|
2004-08-14 07:54:55 +08:00
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail = NLMSG_TAIL(n);
|
2004-08-14 07:54:55 +08:00
|
|
|
addattr_l(n, MAX_MSG, ++prio, NULL, 0);
|
|
|
|
addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS, n);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "bad action parsing\n");
|
2004-08-14 07:54:55 +08:00
|
|
|
goto bad_val;
|
|
|
|
}
|
2005-01-18 09:24:18 +08:00
|
|
|
tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
|
2004-08-14 07:54:55 +08:00
|
|
|
ok++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eap > 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "bad action empty %d\n", eap);
|
2004-08-14 07:54:55 +08:00
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
done:
|
|
|
|
*argc_p = argc;
|
|
|
|
*argv_p = argv;
|
|
|
|
return 0;
|
|
|
|
bad_val:
|
2006-12-06 02:10:22 +08:00
|
|
|
/* no need to undo things, returning from here should
|
2004-08-14 07:54:55 +08:00
|
|
|
* cause enough pain */
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv);
|
2004-08-14 07:54:55 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-13 03:09:03 +08:00
|
|
|
static int
|
2016-03-22 02:48:36 +08:00
|
|
|
tc_print_one_action(FILE *f, struct rtattr *arg)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
struct rtattr *tb[TCA_ACT_MAX + 1];
|
2004-08-14 07:54:55 +08:00
|
|
|
int err = 0;
|
|
|
|
struct action_util *a = NULL;
|
|
|
|
|
|
|
|
if (arg == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
parse_rtattr_nested(tb, TCA_ACT_MAX, arg);
|
|
|
|
|
|
|
|
if (tb[TCA_ACT_KIND] == NULL) {
|
2004-08-14 07:54:55 +08:00
|
|
|
fprintf(stderr, "NULL Action!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
|
2016-03-22 02:48:36 +08:00
|
|
|
if (a == NULL)
|
2004-08-14 07:54:55 +08:00
|
|
|
return err;
|
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
if (err < 0)
|
2004-08-14 07:54:55 +08:00
|
|
|
return err;
|
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
if (show_stats && tb[TCA_ACT_STATS]) {
|
2004-12-08 07:52:52 +08:00
|
|
|
fprintf(f, "\tAction statistics:\n");
|
|
|
|
print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
|
2004-09-01 01:45:21 +08:00
|
|
|
fprintf(f, "\n");
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
static int
|
|
|
|
tc_print_action_flush(FILE *f, const struct rtattr *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct rtattr *tb[TCA_MAX + 1];
|
|
|
|
int err = 0;
|
|
|
|
struct action_util *a = NULL;
|
|
|
|
__u32 *delete_count = 0;
|
|
|
|
|
|
|
|
parse_rtattr_nested(tb, TCA_MAX, arg);
|
|
|
|
|
|
|
|
if (tb[TCA_KIND] == NULL) {
|
|
|
|
fprintf(stderr, "NULL Action!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
a = get_action_kind(RTA_DATA(tb[TCA_KIND]));
|
2016-03-22 02:48:36 +08:00
|
|
|
if (a == NULL)
|
2014-09-22 00:29:14 +08:00
|
|
|
return err;
|
|
|
|
|
|
|
|
delete_count = RTA_DATA(tb[TCA_FCNT]);
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
|
2014-09-22 00:29:14 +08:00
|
|
|
tab_flush = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-08-14 07:54:55 +08:00
|
|
|
int
|
2014-09-22 00:29:14 +08:00
|
|
|
tc_print_action(FILE *f, const struct rtattr *arg)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
int i;
|
|
|
|
struct rtattr *tb[TCA_ACT_MAX_PRIO + 1];
|
|
|
|
|
|
|
|
if (arg == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2005-01-19 06:11:58 +08:00
|
|
|
parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
2014-09-22 00:29:14 +08:00
|
|
|
if (tab_flush && NULL != tb[0] && NULL == tb[1])
|
|
|
|
return tc_print_action_flush(f, tb[0]);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
|
|
|
|
if (tb[i]) {
|
2013-12-22 23:37:44 +08:00
|
|
|
fprintf(f, "\n\taction order %d: ", i);
|
2016-03-22 02:48:36 +08:00
|
|
|
if (tc_print_one_action(f, tb[i]) < 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
fprintf(f, "Error printing action\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-08-09 02:55:15 +08:00
|
|
|
int print_action(const struct sockaddr_nl *who,
|
2004-12-08 05:48:29 +08:00
|
|
|
struct nlmsghdr *n,
|
2004-09-01 01:45:21 +08:00
|
|
|
void *arg)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
2016-03-22 02:48:36 +08:00
|
|
|
FILE *fp = (FILE *)arg;
|
2004-08-14 07:54:55 +08:00
|
|
|
struct tcamsg *t = NLMSG_DATA(n);
|
|
|
|
int len = n->nlmsg_len;
|
2016-03-22 02:48:36 +08:00
|
|
|
struct rtattr *tb[TCAA_MAX+1];
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
len -= NLMSG_LENGTH(sizeof(*t));
|
|
|
|
|
|
|
|
if (len < 0) {
|
|
|
|
fprintf(stderr, "Wrong len %d\n", len);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len);
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
if (tb[TCA_ACT_TAB] == NULL) {
|
2004-08-14 07:54:55 +08:00
|
|
|
if (n->nlmsg_type != RTM_GETACTION)
|
2006-08-09 02:55:15 +08:00
|
|
|
fprintf(stderr, "print_action: NULL kind\n");
|
2004-08-14 07:54:55 +08:00
|
|
|
return -1;
|
2006-12-06 02:10:22 +08:00
|
|
|
}
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
if (n->nlmsg_type == RTM_DELACTION) {
|
|
|
|
if (n->nlmsg_flags & NLM_F_ROOT) {
|
2006-12-06 02:10:22 +08:00
|
|
|
fprintf(fp, "Flushed table ");
|
2004-08-14 07:54:55 +08:00
|
|
|
tab_flush = 1;
|
|
|
|
} else {
|
|
|
|
fprintf(fp, "deleted action ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n->nlmsg_type == RTM_NEWACTION)
|
|
|
|
fprintf(fp, "Added action ");
|
|
|
|
tc_print_action(fp, tb[TCA_ACT_TAB]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
|
|
|
char k[16];
|
|
|
|
struct action_util *a = NULL;
|
|
|
|
int argc = *argc_p;
|
|
|
|
char **argv = *argv_p;
|
|
|
|
int prio = 0;
|
|
|
|
int ret = 0;
|
|
|
|
__u32 i;
|
|
|
|
struct sockaddr_nl nladdr;
|
|
|
|
struct rtattr *tail;
|
|
|
|
struct rtattr *tail2;
|
|
|
|
struct nlmsghdr *ans = NULL;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
struct nlmsghdr n;
|
|
|
|
struct tcamsg t;
|
|
|
|
char buf[MAX_MSG];
|
|
|
|
} req;
|
|
|
|
|
|
|
|
req.t.tca_family = AF_UNSPEC;
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
|
|
|
memset(&nladdr, 0, sizeof(nladdr));
|
|
|
|
nladdr.nl_family = AF_NETLINK;
|
|
|
|
|
|
|
|
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
|
|
|
|
req.n.nlmsg_flags = NLM_F_REQUEST|flags;
|
|
|
|
req.n.nlmsg_type = cmd;
|
2016-03-22 02:48:36 +08:00
|
|
|
argc -= 1;
|
|
|
|
argv += 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail = NLMSG_TAIL(&req.n);
|
2004-08-14 07:54:55 +08:00
|
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
|
|
|
|
|
|
|
|
while (argc > 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
if (strcmp(*argv, "action") == 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
continue;
|
|
|
|
} else if (strcmp(*argv, "help") == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
2004-08-14 07:54:55 +08:00
|
|
|
a = get_action_kind(k);
|
2016-03-22 02:48:36 +08:00
|
|
|
if (a == NULL) {
|
|
|
|
fprintf(stderr, "Error: non existent action: %s\n", k);
|
2004-08-14 07:54:55 +08:00
|
|
|
ret = -1;
|
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
if (strcmp(a->id, k) != 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "Error: non existent action: %s\n", k);
|
2004-08-14 07:54:55 +08:00
|
|
|
ret = -1;
|
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
argc -= 1;
|
|
|
|
argv += 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
if (argc <= 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "Error: no index specified action: %s\n", k);
|
2004-08-14 07:54:55 +08:00
|
|
|
ret = -1;
|
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matches(*argv, "index") == 0) {
|
|
|
|
NEXT_ARG();
|
|
|
|
if (get_u32(&i, *argv, 10)) {
|
|
|
|
fprintf(stderr, "Illegal \"index\"\n");
|
|
|
|
ret = -1;
|
|
|
|
goto bad_val;
|
|
|
|
}
|
2016-03-22 02:48:36 +08:00
|
|
|
argc -= 1;
|
|
|
|
argv += 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
} else {
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "Error: no index specified action: %s\n", k);
|
2004-08-14 07:54:55 +08:00
|
|
|
ret = -1;
|
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail2 = NLMSG_TAIL(&req.n);
|
|
|
|
addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
|
|
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
|
|
|
|
addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
|
|
|
|
tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
req.n.nlmsg_seq = rth.dump = ++rth.seq;
|
|
|
|
if (cmd == RTM_GETACTION)
|
|
|
|
ans = &req.n;
|
2005-03-15 06:19:16 +08:00
|
|
|
|
2015-05-28 03:26:14 +08:00
|
|
|
if (rtnl_talk(&rth, &req.n, ans, MAX_MSG) < 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
fprintf(stderr, "We have an error talking to the kernel\n");
|
2005-03-15 03:02:41 +08:00
|
|
|
return 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
if (ans && print_action(NULL, &req.n, (void *)stdout) < 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
fprintf(stderr, "Dump terminated\n");
|
2005-03-15 03:02:41 +08:00
|
|
|
return 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
*argc_p = argc;
|
|
|
|
*argv_p = argv;
|
|
|
|
bad_val:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
static int tc_action_modify(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
|
|
|
int argc = *argc_p;
|
|
|
|
char **argv = *argv_p;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
struct rtattr *tail;
|
|
|
|
struct {
|
|
|
|
struct nlmsghdr n;
|
|
|
|
struct tcamsg t;
|
|
|
|
char buf[MAX_MSG];
|
|
|
|
} req;
|
|
|
|
|
|
|
|
req.t.tca_family = AF_UNSPEC;
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
|
|
|
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
|
|
|
|
req.n.nlmsg_flags = NLM_F_REQUEST|flags;
|
|
|
|
req.n.nlmsg_type = cmd;
|
2005-01-18 09:24:18 +08:00
|
|
|
tail = NLMSG_TAIL(&req.n);
|
2016-03-22 02:48:36 +08:00
|
|
|
argc -= 1;
|
|
|
|
argv += 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
|
|
|
|
fprintf(stderr, "Illegal \"action\"\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2005-01-18 09:24:18 +08:00
|
|
|
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
2015-05-28 03:26:14 +08:00
|
|
|
if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
fprintf(stderr, "We have an error talking to the kernel\n");
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*argc_p = argc;
|
|
|
|
*argv_p = argv;
|
2005-03-15 06:19:16 +08:00
|
|
|
|
2004-08-14 07:54:55 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-13 03:09:03 +08:00
|
|
|
static int tc_act_list_or_flush(int argc, char **argv, int event)
|
2004-08-14 07:54:55 +08:00
|
|
|
{
|
|
|
|
int ret = 0, prio = 0, msg_size = 0;
|
|
|
|
char k[16];
|
2016-03-22 02:48:36 +08:00
|
|
|
struct rtattr *tail, *tail2;
|
2004-08-14 07:54:55 +08:00
|
|
|
struct action_util *a = NULL;
|
|
|
|
struct {
|
|
|
|
struct nlmsghdr n;
|
|
|
|
struct tcamsg t;
|
|
|
|
char buf[MAX_MSG];
|
|
|
|
} req;
|
|
|
|
|
|
|
|
req.t.tca_family = AF_UNSPEC;
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
|
|
|
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
|
|
|
|
|
2005-01-18 09:24:18 +08:00
|
|
|
tail = NLMSG_TAIL(&req.n);
|
2004-08-14 07:54:55 +08:00
|
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
|
2005-01-18 09:24:18 +08:00
|
|
|
tail2 = NLMSG_TAIL(&req.n);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
2016-03-22 02:48:36 +08:00
|
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
2004-08-14 07:54:55 +08:00
|
|
|
#ifdef CONFIG_GACT
|
|
|
|
if (!gact_ld) {
|
|
|
|
get_action_kind("gact");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
a = get_action_kind(k);
|
2016-03-22 02:48:36 +08:00
|
|
|
if (a == NULL) {
|
|
|
|
fprintf(stderr, "bad action %s\n", k);
|
2004-08-14 07:54:55 +08:00
|
|
|
goto bad_val;
|
|
|
|
}
|
|
|
|
if (strcmp(a->id, k) != 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
fprintf(stderr, "bad action %s\n", k);
|
2004-08-14 07:54:55 +08:00
|
|
|
goto bad_val;
|
|
|
|
}
|
2016-03-22 02:48:36 +08:00
|
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
|
|
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
|
2005-01-18 09:24:18 +08:00
|
|
|
tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
|
|
|
|
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
|
2004-08-14 07:54:55 +08:00
|
|
|
|
|
|
|
msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
|
|
|
|
|
2006-12-06 02:10:22 +08:00
|
|
|
if (event == RTM_GETACTION) {
|
2004-08-14 07:54:55 +08:00
|
|
|
if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
|
|
|
|
perror("Cannot send dump request");
|
2005-03-15 03:02:41 +08:00
|
|
|
return 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
2011-12-29 02:37:12 +08:00
|
|
|
ret = rtnl_dump_filter(&rth, print_action, stdout);
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
|
|
|
|
2006-12-06 02:10:22 +08:00
|
|
|
if (event == RTM_DELACTION) {
|
2004-08-14 07:54:55 +08:00
|
|
|
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
|
|
|
|
req.n.nlmsg_type = RTM_DELACTION;
|
|
|
|
req.n.nlmsg_flags |= NLM_F_ROOT;
|
|
|
|
req.n.nlmsg_flags |= NLM_F_REQUEST;
|
2015-05-28 03:26:14 +08:00
|
|
|
if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) {
|
2004-08-14 07:54:55 +08:00
|
|
|
fprintf(stderr, "We have an error flushing\n");
|
2005-03-15 03:02:41 +08:00
|
|
|
return 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bad_val:
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_action(int argc, char **argv)
|
|
|
|
{
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
while (argc > 0) {
|
|
|
|
|
|
|
|
if (matches(*argv, "add") == 0) {
|
|
|
|
ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
|
|
|
|
} else if (matches(*argv, "change") == 0 ||
|
|
|
|
matches(*argv, "replace") == 0) {
|
|
|
|
ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
|
|
|
|
} else if (matches(*argv, "delete") == 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
argc -= 1;
|
|
|
|
argv += 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
|
|
|
|
} else if (matches(*argv, "get") == 0) {
|
2016-03-22 02:48:36 +08:00
|
|
|
argc -= 1;
|
|
|
|
argv += 1;
|
2004-08-14 07:54:55 +08:00
|
|
|
ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv);
|
|
|
|
} else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|
|
|
|
|| matches(*argv, "lst") == 0) {
|
|
|
|
if (argc <= 2) {
|
|
|
|
act_usage();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION);
|
|
|
|
} else if (matches(*argv, "flush") == 0) {
|
|
|
|
if (argc <= 2) {
|
|
|
|
act_usage();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION);
|
|
|
|
} else if (matches(*argv, "help") == 0) {
|
|
|
|
act_usage();
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2006-08-05 01:59:34 +08:00
|
|
|
fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv);
|
2004-08-14 07:54:55 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|