iproute2/tc/m_action.c

652 lines
14 KiB
C
Raw Normal View History

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.
*
* 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"
#include "tc_common.h"
2004-08-14 07:54:55 +08:00
#include "tc_util.h"
static struct action_util *action_list;
2004-08-14 07:54:55 +08:00
#ifdef CONFIG_GACT
int gact_ld; /* f*ckin backward compatibility */
2004-08-14 07:54:55 +08:00
#endif
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 ..
*
*/
fprintf(stderr, "usage: tc actions <ACTSPECOP>*\n");
2006-08-05 01:59:34 +08:00
fprintf(stderr,
"Where: \tACTSPECOP := ACR | GD | FL\n"
"\tACR := add | change | replace <ACTSPEC>*\n"
"\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"
"\t\tEach action has its own parameters (ACTPARAMS)\n"
"\n");
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))
fprintf(f, "[Unknown action, optlen=%u] ",
(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
{
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;
}
snprintf(buf, sizeof(buf), "%s/m_%s.so", get_tc_lib(), str);
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;
}
}
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;
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
new_cmd(char **argv)
2004-08-14 07:54:55 +08:00
{
if ((matches(*argv, "change") == 0) ||
(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) {
memset(k, 0, sizeof(k));
2004-08-14 07:54:55 +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;
actions: keyword flowid or classid terminates action pipeline scenario testcase: TC="sudo ./tc/tc" DEV="dev eth0" $TC qdisc del $DEV ingress $TC qdisc add $DEV ingress $TC filter add $DEV parent ffff: protocol ip u32 match ip src 10.0.0.0/24 action police rate 6Mbit burst 6Mbit drop flowid :1 $TC filter add $DEV parent ffff: protocol ip u32 match ip dst 10.0.0.0/24 action police rate 1Gbit burst 1Gbit pass flowid :1 $TC -s filter ls $DEV parent ffff: protocol ip $TC qdisc del $DEV ingress $TC qdisc add $DEV ingress $TC filter add $DEV parent ffff: protocol ip u32 match ip src 10.0.0.0/24 flowid 1:1 action police rate 6Mbit burst 6Mbit drop $TC filter add $DEV parent ffff: protocol ip u32 match ip dst 10.0.0.0/24 flowid 1:2 action police rate 1Gbit burst 1Gbit pass $TC -s filter ls $DEV parent ffff: protocol ip $TC qdisc del $DEV ingress $TC qdisc add $DEV ingress $TC filter add $DEV parent ffff: protocol ip pref 10 \ u32 match ip protocol 1 0xff \ flowid 1:10 \ action skbedit mark 11 \ action police rate 10kbit burst 10k pipe index 1 \ action skbedit mark 12 \ action police rate 20kbit burst 20k pipe index 2 \ action mirred egress mirror dev dummy0 $TC -s filter ls $DEV parent ffff: protocol ip $TC qdisc del $DEV ingress $TC qdisc add $DEV ingress $TC filter add $DEV parent ffff: protocol ip pref 10 \ u32 match ip protocol 1 0xff \ action skbedit mark 11 \ action police rate 10kbit burst 10k pipe index 1 \ action skbedit mark 12 \ action police rate 20kbit burst 20k pipe index 2 \ action mirred egress mirror dev dummy0 \ flowid 1:10 $TC -s filter ls $DEV parent ffff: protocol ip Reported-by: Seann Herdejurgen <seann@herdejurgen.com> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
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;
strncpy(k, *argv, sizeof(k) - 1);
2004-08-14 07:54:55 +08:00
eap = 0;
if (argc > 0) {
2004-08-14 07:54:55 +08:00
a = get_action_kind(k);
} else {
done0:
if (ok)
break;
else
goto done;
}
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);
ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS, n);
2004-08-14 07:54:55 +08:00
if (ret < 0) {
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) {
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:
/* no need to undo things, returning from here should
2004-08-14 07:54:55 +08:00
* cause enough pain */
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
tc_print_one_action(FILE *f, struct rtattr *arg)
2004-08-14 07:54:55 +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;
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;
}
a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
if (a == NULL)
2004-08-14 07:54:55 +08:00
return err;
err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]);
2004-08-14 07:54:55 +08:00
if (err < 0)
2004-08-14 07:54:55 +08:00
return err;
if (show_stats && tb[TCA_ACT_STATS]) {
fprintf(f, "\tAction statistics:\n");
print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
fprintf(f, "\n");
2004-08-14 07:54:55 +08:00
}
return 0;
}
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]));
if (a == NULL)
return err;
delete_count = RTA_DATA(tb[TCA_FCNT]);
fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
tab_flush = 0;
return 0;
}
2004-08-14 07:54:55 +08:00
int
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;
parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg);
2004-08-14 07:54:55 +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]) {
fprintf(f, "\n\taction order %d: ", i);
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;
}
int print_action(const struct sockaddr_nl *who,
struct nlmsghdr *n,
void *arg)
2004-08-14 07:54:55 +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;
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);
if (tb[TCA_ACT_TAB] == NULL) {
2004-08-14 07:54:55 +08:00
if (n->nlmsg_type != RTM_GETACTION)
fprintf(stderr, "print_action: NULL kind\n");
2004-08-14 07:54:55 +08:00
return -1;
}
2004-08-14 07:54:55 +08:00
if (n->nlmsg_type == RTM_DELACTION) {
if (n->nlmsg_flags & NLM_F_ROOT) {
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;
}
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;
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) {
if (strcmp(*argv, "action") == 0) {
2004-08-14 07:54:55 +08:00
argc--;
argv++;
continue;
} else if (strcmp(*argv, "help") == 0) {
return -1;
}
strncpy(k, *argv, sizeof(k) - 1);
2004-08-14 07:54:55 +08:00
a = get_action_kind(k);
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) {
fprintf(stderr, "Error: non existent action: %s\n", k);
2004-08-14 07:54:55 +08:00
ret = -1;
goto bad_val;
}
argc -= 1;
argv += 1;
2004-08-14 07:54:55 +08:00
if (argc <= 0) {
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;
}
argc -= 1;
argv += 1;
2004-08-14 07:54:55 +08:00
} else {
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;
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");
return 1;
2004-08-14 07:54:55 +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");
return 1;
2004-08-14 07:54:55 +08:00
}
*argc_p = argc;
*argv_p = argv;
bad_val:
return ret;
}
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);
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
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;
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];
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
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);
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) {
fprintf(stderr, "bad action %s\n", k);
2004-08-14 07:54:55 +08:00
goto bad_val;
}
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));
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");
return 1;
2004-08-14 07:54:55 +08:00
}
ret = rtnl_dump_filter(&rth, print_action, stdout);
2004-08-14 07:54:55 +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;
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");
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) {
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) {
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;
}