mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-06 07:17:00 +00:00

Make use of 128b user cookies Introduce optional 128-bit action cookie. Like all other cookie schemes in the networking world (eg in protocols like http or existing kernel fib protocol field, etc) the idea is to save user state that when retrieved serves as a correlator. The kernel _should not_ intepret it. The user can store whatever they wish in the 128 bits. Sample exercise(showing variable length use of cookie) .. create an accept action with cookie a1b2c3d4 sudo $TC actions add action ok index 1 cookie a1b2c3d4 .. dump all gact actions.. sudo $TC -s actions ls action gact action order 0: gact action pass random type none pass val 0 index 1 ref 1 bind 0 installed 5 sec used 5 sec Action statistics: Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 cookie a1b2c3d4 .. bind the accept action to a filter.. sudo $TC filter add dev lo parent ffff: protocol ip prio 1 \ u32 match ip dst 127.0.0.1/32 flowid 1:1 action gact index 1 ... send some traffic.. $ ping 127.0.0.1 -c 3 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.020 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.027 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.038 ms Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
679 lines
14 KiB
C
679 lines
14 KiB
C
/*
|
|
* 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)
|
|
*
|
|
* 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"
|
|
#include "tc_util.h"
|
|
|
|
static struct action_util *action_list;
|
|
#ifdef CONFIG_GACT
|
|
int gact_ld; /* f*ckin backward compatibility */
|
|
#endif
|
|
int tab_flush;
|
|
|
|
static void act_usage(void)
|
|
{
|
|
/*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");
|
|
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"
|
|
"\t\tExample ACTNAME is gact, mirred, bpf, etc\n"
|
|
"\t\tEach action has its own parameters (ACTPARAMS)\n"
|
|
"\n");
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
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));
|
|
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;
|
|
}
|
|
|
|
static struct action_util *get_action_kind(char *str)
|
|
{
|
|
static void *aBODY;
|
|
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);
|
|
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);
|
|
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");
|
|
goto restart_s;
|
|
}
|
|
#endif
|
|
a = calloc(1, sizeof(*a));
|
|
if (a) {
|
|
strncpy(a->id, "noact", 15);
|
|
a->parse_aopt = parse_noaopt;
|
|
a->print_aopt = print_noaopt;
|
|
goto reg;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
static int
|
|
new_cmd(char **argv)
|
|
{
|
|
if ((matches(*argv, "change") == 0) ||
|
|
(matches(*argv, "replace") == 0) ||
|
|
(matches(*argv, "delete") == 0) ||
|
|
(matches(*argv, "get") == 0) ||
|
|
(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 act_ck_len = 0;
|
|
int ok = 0;
|
|
int eap = 0; /* expect action parameters */
|
|
|
|
int ret = 0;
|
|
int prio = 0;
|
|
unsigned char act_ck[TC_COOKIE_MAX_SIZE];
|
|
|
|
if (argc <= 0)
|
|
return -1;
|
|
|
|
tail = tail2 = NLMSG_TAIL(n);
|
|
|
|
addattr_l(n, MAX_MSG, tca_id, NULL, 0);
|
|
|
|
while (argc > 0) {
|
|
|
|
memset(k, 0, sizeof(k));
|
|
|
|
if (strcmp(*argv, "action") == 0) {
|
|
argc--;
|
|
argv++;
|
|
eap = 1;
|
|
#ifdef CONFIG_GACT
|
|
if (!gact_ld) {
|
|
get_action_kind("gact");
|
|
}
|
|
#endif
|
|
continue;
|
|
} else if (strcmp(*argv, "flowid") == 0) {
|
|
break;
|
|
} else if (strcmp(*argv, "classid") == 0) {
|
|
break;
|
|
} 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);
|
|
eap = 0;
|
|
if (argc > 0) {
|
|
a = get_action_kind(k);
|
|
} else {
|
|
done0:
|
|
if (ok)
|
|
break;
|
|
else
|
|
goto done;
|
|
}
|
|
|
|
if (a == NULL) {
|
|
goto bad_val;
|
|
}
|
|
|
|
tail = NLMSG_TAIL(n);
|
|
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);
|
|
|
|
if (ret < 0) {
|
|
fprintf(stderr, "bad action parsing\n");
|
|
goto bad_val;
|
|
}
|
|
|
|
if (*argv && strcmp(*argv, "cookie") == 0) {
|
|
size_t slen;
|
|
|
|
NEXT_ARG();
|
|
slen = strlen(*argv);
|
|
if (slen > TC_COOKIE_MAX_SIZE * 2) {
|
|
char cookie_err_m[128];
|
|
|
|
snprintf(cookie_err_m, 128,
|
|
"%zd Max allowed size %d",
|
|
slen, TC_COOKIE_MAX_SIZE*2);
|
|
invarg(cookie_err_m, *argv);
|
|
}
|
|
|
|
if (hex2mem(*argv, act_ck, slen / 2) < 0)
|
|
invarg("cookie must be a hex string\n",
|
|
*argv);
|
|
|
|
act_ck_len = slen;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
if (act_ck_len)
|
|
addattr_l(n, MAX_MSG, TCA_ACT_COOKIE,
|
|
&act_ck, act_ck_len);
|
|
|
|
tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
|
|
ok++;
|
|
}
|
|
}
|
|
|
|
if (eap > 0) {
|
|
fprintf(stderr, "bad action empty %d\n", eap);
|
|
goto bad_val;
|
|
}
|
|
|
|
tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;
|
|
|
|
done:
|
|
*argc_p = argc;
|
|
*argv_p = argv;
|
|
return 0;
|
|
bad_val:
|
|
/* no need to undo things, returning from here should
|
|
* cause enough pain */
|
|
fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv);
|
|
return -1;
|
|
}
|
|
|
|
static int tc_print_one_action(FILE *f, struct rtattr *arg)
|
|
{
|
|
|
|
struct rtattr *tb[TCA_ACT_MAX + 1];
|
|
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) {
|
|
fprintf(stderr, "NULL Action!\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
|
|
if (a == NULL)
|
|
return err;
|
|
|
|
err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]);
|
|
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (show_stats && tb[TCA_ACT_STATS]) {
|
|
|
|
fprintf(f, "\tAction statistics:\n");
|
|
print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
|
|
if (tb[TCA_ACT_COOKIE]) {
|
|
int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
|
|
char b1[strsz+1];
|
|
|
|
fprintf(f, "\n\tcookie len %d %s ", strsz,
|
|
hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
|
|
strsz, b1, sizeof(b1)));
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int
|
|
tc_print_action(FILE *f, const struct rtattr *arg)
|
|
{
|
|
|
|
int i;
|
|
struct rtattr *tb[TCA_ACT_MAX_PRIO + 1];
|
|
|
|
if (arg == NULL)
|
|
return 0;
|
|
|
|
parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg);
|
|
|
|
if (tab_flush && NULL != tb[0] && NULL == tb[1])
|
|
return tc_print_action_flush(f, tb[0]);
|
|
|
|
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) {
|
|
fprintf(f, "Error printing action\n");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int print_action(const struct sockaddr_nl *who,
|
|
struct nlmsghdr *n,
|
|
void *arg)
|
|
{
|
|
FILE *fp = (FILE *)arg;
|
|
struct tcamsg *t = NLMSG_DATA(n);
|
|
int len = n->nlmsg_len;
|
|
struct rtattr *tb[TCAA_MAX+1];
|
|
|
|
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) {
|
|
if (n->nlmsg_type != RTM_GETACTION)
|
|
fprintf(stderr, "print_action: NULL kind\n");
|
|
return -1;
|
|
}
|
|
|
|
if (n->nlmsg_type == RTM_DELACTION) {
|
|
if (n->nlmsg_flags & NLM_F_ROOT) {
|
|
fprintf(fp, "Flushed table ");
|
|
tab_flush = 1;
|
|
} else {
|
|
fprintf(fp, "Deleted action ");
|
|
}
|
|
}
|
|
|
|
if (n->nlmsg_type == RTM_NEWACTION) {
|
|
if ((n->nlmsg_flags & NLM_F_CREATE) &&
|
|
!(n->nlmsg_flags & NLM_F_REPLACE)) {
|
|
fprintf(fp, "Added action ");
|
|
} else if (n->nlmsg_flags & NLM_F_REPLACE) {
|
|
fprintf(fp, "Replaced 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)
|
|
{
|
|
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 rtattr *tail;
|
|
struct rtattr *tail2;
|
|
struct nlmsghdr *ans = NULL;
|
|
|
|
struct {
|
|
struct nlmsghdr n;
|
|
struct tcamsg t;
|
|
char buf[MAX_MSG];
|
|
} req = {
|
|
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
|
|
.n.nlmsg_flags = NLM_F_REQUEST | flags,
|
|
.n.nlmsg_type = cmd,
|
|
.t.tca_family = AF_UNSPEC,
|
|
};
|
|
|
|
argc -= 1;
|
|
argv += 1;
|
|
|
|
|
|
tail = NLMSG_TAIL(&req.n);
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
|
|
|
|
while (argc > 0) {
|
|
if (strcmp(*argv, "action") == 0) {
|
|
argc--;
|
|
argv++;
|
|
continue;
|
|
} else if (strcmp(*argv, "help") == 0) {
|
|
return -1;
|
|
}
|
|
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
|
a = get_action_kind(k);
|
|
if (a == NULL) {
|
|
fprintf(stderr, "Error: non existent action: %s\n", k);
|
|
ret = -1;
|
|
goto bad_val;
|
|
}
|
|
if (strcmp(a->id, k) != 0) {
|
|
fprintf(stderr, "Error: non existent action: %s\n", k);
|
|
ret = -1;
|
|
goto bad_val;
|
|
}
|
|
|
|
argc -= 1;
|
|
argv += 1;
|
|
if (argc <= 0) {
|
|
fprintf(stderr, "Error: no index specified action: %s\n", k);
|
|
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;
|
|
} else {
|
|
fprintf(stderr, "Error: no index specified action: %s\n", k);
|
|
ret = -1;
|
|
goto bad_val;
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
|
|
|
|
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) {
|
|
fprintf(stderr, "We have an error talking to the kernel\n");
|
|
return 1;
|
|
}
|
|
|
|
if (ans && print_action(NULL, &req.n, (void *)stdout) < 0) {
|
|
fprintf(stderr, "Dump terminated\n");
|
|
return 1;
|
|
}
|
|
|
|
*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)
|
|
{
|
|
int argc = *argc_p;
|
|
char **argv = *argv_p;
|
|
int ret = 0;
|
|
struct {
|
|
struct nlmsghdr n;
|
|
struct tcamsg t;
|
|
char buf[MAX_MSG];
|
|
} req = {
|
|
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
|
|
.n.nlmsg_flags = NLM_F_REQUEST | flags,
|
|
.n.nlmsg_type = cmd,
|
|
.t.tca_family = AF_UNSPEC,
|
|
};
|
|
struct rtattr *tail = NLMSG_TAIL(&req.n);
|
|
|
|
argc -= 1;
|
|
argv += 1;
|
|
if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
|
|
fprintf(stderr, "Illegal \"action\"\n");
|
|
return -1;
|
|
}
|
|
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
|
|
|
|
if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) {
|
|
fprintf(stderr, "We have an error talking to the kernel\n");
|
|
ret = -1;
|
|
}
|
|
|
|
*argc_p = argc;
|
|
*argv_p = argv;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tc_act_list_or_flush(int argc, char **argv, int event)
|
|
{
|
|
int ret = 0, prio = 0, msg_size = 0;
|
|
char k[16];
|
|
struct rtattr *tail, *tail2;
|
|
struct action_util *a = NULL;
|
|
struct {
|
|
struct nlmsghdr n;
|
|
struct tcamsg t;
|
|
char buf[MAX_MSG];
|
|
} req = {
|
|
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
|
|
.t.tca_family = AF_UNSPEC,
|
|
};
|
|
|
|
tail = NLMSG_TAIL(&req.n);
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
|
|
tail2 = NLMSG_TAIL(&req.n);
|
|
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
|
#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);
|
|
goto bad_val;
|
|
}
|
|
if (strcmp(a->id, k) != 0) {
|
|
fprintf(stderr, "bad action %s\n", k);
|
|
goto bad_val;
|
|
}
|
|
strncpy(k, *argv, sizeof(k) - 1);
|
|
|
|
addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
|
|
addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
|
|
tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
|
|
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
|
|
|
|
msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
|
|
|
|
if (event == RTM_GETACTION) {
|
|
if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
|
|
perror("Cannot send dump request");
|
|
return 1;
|
|
}
|
|
ret = rtnl_dump_filter(&rth, print_action, stdout);
|
|
}
|
|
|
|
if (event == RTM_DELACTION) {
|
|
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) {
|
|
fprintf(stderr, "We have an error flushing\n");
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
|
|
} else if (matches(*argv, "get") == 0) {
|
|
argc -= 1;
|
|
argv += 1;
|
|
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 {
|
|
fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv);
|
|
return -1;
|
|
}
|
|
|
|
if (ret < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|