Home | History | Annotate | Download | only in tc
      1 /*
      2  * m_action.c		Action Management
      3  *
      4  *		This program is free software; you can distribute it and/or
      5  *		modify it under the terms of the GNU General Public License
      6  *		as published by the Free Software Foundation; either version
      7  *		2 of the License, or (at your option) any later version.
      8  *
      9  * Authors:  J Hadi Salim (hadi (at) cyberus.ca)
     10  *
     11  * TODO:
     12  * - parse to be passed a filedescriptor for logging purposes
     13  *
     14 */
     15 
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <unistd.h>
     19 #include <syslog.h>
     20 #include <fcntl.h>
     21 #include <sys/socket.h>
     22 #include <netinet/in.h>
     23 #include <arpa/inet.h>
     24 #include <string.h>
     25 #include <dlfcn.h>
     26 
     27 #include "utils.h"
     28 #include "tc_common.h"
     29 #include "tc_util.h"
     30 
     31 static struct action_util *action_list;
     32 
     33 #ifdef CONFIG_GACT
     34 int gact_ld; /* f*ckin backward compatibility */
     35 #endif
     36 int tab_flush;
     37 
     38 static void act_usage(void)
     39 {
     40 	/*XXX: In the near future add a action->print_help to improve
     41 	 * usability
     42 	 * This would mean new tc will not be backward compatible
     43 	 * with any action .so from the old days. But if someone really
     44 	 * does that, they would know how to fix this ..
     45 	 *
     46 	*/
     47 	fprintf(stderr, "usage: tc actions <ACTSPECOP>*\n");
     48 	fprintf(stderr,
     49 		"Where: \tACTSPECOP := ACR | GD | FL\n"
     50 			"\tACR := add | change | replace <ACTSPEC>*\n"
     51 			"\tGD := get | delete | <ACTISPEC>*\n"
     52 			"\tFL := ls | list | flush | <ACTNAMESPEC>\n"
     53 			"\tACTNAMESPEC :=  action <ACTNAME>\n"
     54 			"\tACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n"
     55 			"\tACTSPEC := action <ACTDETAIL> [INDEXSPEC]\n"
     56 			"\tINDEXSPEC := index <32 bit indexvalue>\n"
     57 			"\tACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
     58 			"\t\tExample ACTNAME is gact, mirred, bpf, etc\n"
     59 			"\t\tEach action has its own parameters (ACTPARAMS)\n"
     60 			"\n");
     61 
     62 	exit(-1);
     63 }
     64 
     65 static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
     66 {
     67 	if (opt && RTA_PAYLOAD(opt))
     68 		fprintf(f, "[Unknown action, optlen=%u] ",
     69 			(unsigned int) RTA_PAYLOAD(opt));
     70 	return 0;
     71 }
     72 
     73 static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n)
     74 {
     75 	int argc = *argc_p;
     76 	char **argv = *argv_p;
     77 
     78 	if (argc) {
     79 		fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv);
     80 	} else {
     81 		fprintf(stderr, "Unknown action \"%s\"\n", au->id);
     82 	}
     83 	return -1;
     84 }
     85 
     86 static struct action_util *get_action_kind(char *str)
     87 {
     88 	static void *aBODY;
     89 	void *dlh;
     90 	char buf[256];
     91 	struct action_util *a;
     92 #ifdef CONFIG_GACT
     93 	int looked4gact = 0;
     94 restart_s:
     95 #endif
     96 	for (a = action_list; a; a = a->next) {
     97 		if (strcmp(a->id, str) == 0)
     98 			return a;
     99 	}
    100 
    101 	snprintf(buf, sizeof(buf), "%s/m_%s.so", get_tc_lib(), str);
    102 	dlh = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
    103 	if (dlh == NULL) {
    104 		dlh = aBODY;
    105 		if (dlh == NULL) {
    106 			dlh = aBODY = dlopen(NULL, RTLD_LAZY);
    107 			if (dlh == NULL)
    108 				goto noexist;
    109 		}
    110 	}
    111 
    112 	snprintf(buf, sizeof(buf), "%s_action_util", str);
    113 	a = dlsym(dlh, buf);
    114 	if (a == NULL)
    115 		goto noexist;
    116 
    117 reg:
    118 	a->next = action_list;
    119 	action_list = a;
    120 	return a;
    121 
    122 noexist:
    123 #ifdef CONFIG_GACT
    124 	if (!looked4gact) {
    125 		looked4gact = 1;
    126 		strcpy(str, "gact");
    127 		goto restart_s;
    128 	}
    129 #endif
    130 	a = calloc(1, sizeof(*a));
    131 	if (a) {
    132 		strncpy(a->id, "noact", 15);
    133 		a->parse_aopt = parse_noaopt;
    134 		a->print_aopt = print_noaopt;
    135 		goto reg;
    136 	}
    137 	return a;
    138 }
    139 
    140 static int
    141 new_cmd(char **argv)
    142 {
    143 	if ((matches(*argv, "change") == 0) ||
    144 		(matches(*argv, "replace") == 0) ||
    145 		(matches(*argv, "delete") == 0) ||
    146 		(matches(*argv, "get") == 0) ||
    147 		(matches(*argv, "add") == 0))
    148 			return 1;
    149 
    150 	return 0;
    151 
    152 }
    153 
    154 int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
    155 {
    156 	int argc = *argc_p;
    157 	char **argv = *argv_p;
    158 	struct rtattr *tail, *tail2;
    159 	char k[16];
    160 	int act_ck_len = 0;
    161 	int ok = 0;
    162 	int eap = 0; /* expect action parameters */
    163 
    164 	int ret = 0;
    165 	int prio = 0;
    166 	unsigned char act_ck[TC_COOKIE_MAX_SIZE];
    167 
    168 	if (argc <= 0)
    169 		return -1;
    170 
    171 	tail = tail2 = NLMSG_TAIL(n);
    172 
    173 	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
    174 
    175 	while (argc > 0) {
    176 
    177 		memset(k, 0, sizeof(k));
    178 
    179 		if (strcmp(*argv, "action") == 0) {
    180 			argc--;
    181 			argv++;
    182 			eap = 1;
    183 #ifdef CONFIG_GACT
    184 			if (!gact_ld) {
    185 				get_action_kind("gact");
    186 			}
    187 #endif
    188 			continue;
    189 		} else if (strcmp(*argv, "flowid") == 0) {
    190 			break;
    191 		} else if (strcmp(*argv, "classid") == 0) {
    192 			break;
    193 		} else if (strcmp(*argv, "help") == 0) {
    194 			return -1;
    195 		} else if (new_cmd(argv)) {
    196 			goto done0;
    197 		} else {
    198 			struct action_util *a = NULL;
    199 
    200 			strncpy(k, *argv, sizeof(k) - 1);
    201 			eap = 0;
    202 			if (argc > 0) {
    203 				a = get_action_kind(k);
    204 			} else {
    205 done0:
    206 				if (ok)
    207 					break;
    208 				else
    209 					goto done;
    210 			}
    211 
    212 			if (a == NULL) {
    213 				goto bad_val;
    214 			}
    215 
    216 			tail = NLMSG_TAIL(n);
    217 			addattr_l(n, MAX_MSG, ++prio, NULL, 0);
    218 			addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
    219 
    220 			ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS,
    221 					    n);
    222 
    223 			if (ret < 0) {
    224 				fprintf(stderr, "bad action parsing\n");
    225 				goto bad_val;
    226 			}
    227 
    228 			if (*argv && strcmp(*argv, "cookie") == 0) {
    229 				size_t slen;
    230 
    231 				NEXT_ARG();
    232 				slen = strlen(*argv);
    233 				if (slen > TC_COOKIE_MAX_SIZE * 2) {
    234 					char cookie_err_m[128];
    235 
    236 					snprintf(cookie_err_m, 128,
    237 						 "%zd Max allowed size %d",
    238 						 slen, TC_COOKIE_MAX_SIZE*2);
    239 					invarg(cookie_err_m, *argv);
    240 				}
    241 
    242 				if (hex2mem(*argv, act_ck, slen / 2) < 0)
    243 					invarg("cookie must be a hex string\n",
    244 					       *argv);
    245 
    246 				act_ck_len = slen / 2;
    247 				argc--;
    248 				argv++;
    249 			}
    250 
    251 			if (act_ck_len)
    252 				addattr_l(n, MAX_MSG, TCA_ACT_COOKIE,
    253 					  &act_ck, act_ck_len);
    254 
    255 			tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    256 			ok++;
    257 		}
    258 	}
    259 
    260 	if (eap > 0) {
    261 		fprintf(stderr, "bad action empty %d\n", eap);
    262 		goto bad_val;
    263 	}
    264 
    265 	tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;
    266 
    267 done:
    268 	*argc_p = argc;
    269 	*argv_p = argv;
    270 	return 0;
    271 bad_val:
    272 	/* no need to undo things, returning from here should
    273 	 * cause enough pain */
    274 	fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv);
    275 	return -1;
    276 }
    277 
    278 static int tc_print_one_action(FILE *f, struct rtattr *arg)
    279 {
    280 
    281 	struct rtattr *tb[TCA_ACT_MAX + 1];
    282 	int err = 0;
    283 	struct action_util *a = NULL;
    284 
    285 	if (arg == NULL)
    286 		return -1;
    287 
    288 	parse_rtattr_nested(tb, TCA_ACT_MAX, arg);
    289 
    290 	if (tb[TCA_ACT_KIND] == NULL) {
    291 		fprintf(stderr, "NULL Action!\n");
    292 		return -1;
    293 	}
    294 
    295 
    296 	a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
    297 	if (a == NULL)
    298 		return err;
    299 
    300 	err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]);
    301 
    302 	if (err < 0)
    303 		return err;
    304 
    305 	if (show_stats && tb[TCA_ACT_STATS]) {
    306 
    307 		fprintf(f, "\tAction statistics:\n");
    308 		print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
    309 		if (tb[TCA_ACT_COOKIE]) {
    310 			int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
    311 			char b1[strsz * 2 + 1];
    312 
    313 			fprintf(f, "\n\tcookie len %d %s ", strsz,
    314 				hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
    315 					      strsz, b1, sizeof(b1)));
    316 		}
    317 		fprintf(f, "\n");
    318 	}
    319 
    320 	return 0;
    321 }
    322 
    323 static int
    324 tc_print_action_flush(FILE *f, const struct rtattr *arg)
    325 {
    326 
    327 	struct rtattr *tb[TCA_MAX + 1];
    328 	int err = 0;
    329 	struct action_util *a = NULL;
    330 	__u32 *delete_count = 0;
    331 
    332 	parse_rtattr_nested(tb, TCA_MAX, arg);
    333 
    334 	if (tb[TCA_KIND] == NULL) {
    335 		fprintf(stderr, "NULL Action!\n");
    336 		return -1;
    337 	}
    338 
    339 	a = get_action_kind(RTA_DATA(tb[TCA_KIND]));
    340 	if (a == NULL)
    341 		return err;
    342 
    343 	delete_count = RTA_DATA(tb[TCA_FCNT]);
    344 	fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
    345 	tab_flush = 0;
    346 	return 0;
    347 }
    348 
    349 int
    350 tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
    351 {
    352 
    353 	int i;
    354 
    355 	if (arg == NULL)
    356 		return 0;
    357 
    358 	if (!tot_acts)
    359 		tot_acts = TCA_ACT_MAX_PRIO;
    360 
    361 	struct rtattr *tb[tot_acts + 1];
    362 
    363 	parse_rtattr_nested(tb, tot_acts, arg);
    364 
    365 	if (tab_flush && NULL != tb[0]  && NULL == tb[1])
    366 		return tc_print_action_flush(f, tb[0]);
    367 
    368 	for (i = 0; i < tot_acts; i++) {
    369 		if (tb[i]) {
    370 			fprintf(f, "\n\taction order %d: ", i);
    371 			if (tc_print_one_action(f, tb[i]) < 0) {
    372 				fprintf(f, "Error printing action\n");
    373 			}
    374 		}
    375 
    376 	}
    377 
    378 	return 0;
    379 }
    380 
    381 int print_action(const struct sockaddr_nl *who,
    382 			   struct nlmsghdr *n,
    383 			   void *arg)
    384 {
    385 	FILE *fp = (FILE *)arg;
    386 	struct tcamsg *t = NLMSG_DATA(n);
    387 	int len = n->nlmsg_len;
    388 	__u32 *tot_acts = NULL;
    389 	struct rtattr *tb[TCA_ROOT_MAX+1];
    390 
    391 	len -= NLMSG_LENGTH(sizeof(*t));
    392 
    393 	if (len < 0) {
    394 		fprintf(stderr, "Wrong len %d\n", len);
    395 		return -1;
    396 	}
    397 
    398 	parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len);
    399 
    400 	if (tb[TCA_ROOT_COUNT])
    401 		tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]);
    402 
    403 	fprintf(fp, "total acts %d\n", tot_acts ? *tot_acts:0);
    404 	if (tb[TCA_ACT_TAB] == NULL) {
    405 		if (n->nlmsg_type != RTM_GETACTION)
    406 			fprintf(stderr, "print_action: NULL kind\n");
    407 		return -1;
    408 	}
    409 
    410 	if (n->nlmsg_type == RTM_DELACTION) {
    411 		if (n->nlmsg_flags & NLM_F_ROOT) {
    412 			fprintf(fp, "Flushed table ");
    413 			tab_flush = 1;
    414 		} else {
    415 			fprintf(fp, "Deleted action ");
    416 		}
    417 	}
    418 
    419 	if (n->nlmsg_type == RTM_NEWACTION) {
    420 		if ((n->nlmsg_flags & NLM_F_CREATE) &&
    421 		    !(n->nlmsg_flags & NLM_F_REPLACE)) {
    422 			fprintf(fp, "Added action ");
    423 		} else if (n->nlmsg_flags & NLM_F_REPLACE) {
    424 			fprintf(fp, "Replaced action ");
    425 		}
    426 	}
    427 
    428 
    429 	tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0);
    430 
    431 	return 0;
    432 }
    433 
    434 static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
    435 {
    436 	char k[16];
    437 	struct action_util *a = NULL;
    438 	int argc = *argc_p;
    439 	char **argv = *argv_p;
    440 	int prio = 0;
    441 	int ret = 0;
    442 	__u32 i = 0;
    443 	struct rtattr *tail;
    444 	struct rtattr *tail2;
    445 	struct nlmsghdr *ans = NULL;
    446 
    447 	struct {
    448 		struct nlmsghdr         n;
    449 		struct tcamsg           t;
    450 		char                    buf[MAX_MSG];
    451 	} req = {
    452 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
    453 		.n.nlmsg_flags = NLM_F_REQUEST | flags,
    454 		.n.nlmsg_type = cmd,
    455 		.t.tca_family = AF_UNSPEC,
    456 	};
    457 
    458 	argc -= 1;
    459 	argv += 1;
    460 
    461 
    462 	tail = NLMSG_TAIL(&req.n);
    463 	addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
    464 
    465 	while (argc > 0) {
    466 		if (strcmp(*argv, "action") == 0) {
    467 			argc--;
    468 			argv++;
    469 			continue;
    470 		} else if (strcmp(*argv, "help") == 0) {
    471 			return -1;
    472 		}
    473 
    474 		strncpy(k, *argv, sizeof(k) - 1);
    475 		a = get_action_kind(k);
    476 		if (a == NULL) {
    477 			fprintf(stderr, "Error: non existent action: %s\n", k);
    478 			ret = -1;
    479 			goto bad_val;
    480 		}
    481 		if (strcmp(a->id, k) != 0) {
    482 			fprintf(stderr, "Error: non existent action: %s\n", k);
    483 			ret = -1;
    484 			goto bad_val;
    485 		}
    486 
    487 		argc -= 1;
    488 		argv += 1;
    489 		if (argc <= 0) {
    490 			fprintf(stderr, "Error: no index specified action: %s\n", k);
    491 			ret = -1;
    492 			goto bad_val;
    493 		}
    494 
    495 		if (matches(*argv, "index") == 0) {
    496 			NEXT_ARG();
    497 			if (get_u32(&i, *argv, 10)) {
    498 				fprintf(stderr, "Illegal \"index\"\n");
    499 				ret = -1;
    500 				goto bad_val;
    501 			}
    502 			argc -= 1;
    503 			argv += 1;
    504 		} else {
    505 			fprintf(stderr, "Error: no index specified action: %s\n", k);
    506 			ret = -1;
    507 			goto bad_val;
    508 		}
    509 
    510 		tail2 = NLMSG_TAIL(&req.n);
    511 		addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
    512 		addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
    513 		if (i > 0)
    514 			addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
    515 		tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
    516 
    517 	}
    518 
    519 	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
    520 
    521 	req.n.nlmsg_seq = rth.dump = ++rth.seq;
    522 	if (cmd == RTM_GETACTION)
    523 		ans = &req.n;
    524 
    525 	if (rtnl_talk(&rth, &req.n, ans, MAX_MSG) < 0) {
    526 		fprintf(stderr, "We have an error talking to the kernel\n");
    527 		return 1;
    528 	}
    529 
    530 	if (ans && print_action(NULL, &req.n, (void *)stdout) < 0) {
    531 		fprintf(stderr, "Dump terminated\n");
    532 		return 1;
    533 	}
    534 
    535 	*argc_p = argc;
    536 	*argv_p = argv;
    537 bad_val:
    538 	return ret;
    539 }
    540 
    541 static int tc_action_modify(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
    542 {
    543 	int argc = *argc_p;
    544 	char **argv = *argv_p;
    545 	int ret = 0;
    546 	struct {
    547 		struct nlmsghdr         n;
    548 		struct tcamsg           t;
    549 		char                    buf[MAX_MSG];
    550 	} req = {
    551 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
    552 		.n.nlmsg_flags = NLM_F_REQUEST | flags,
    553 		.n.nlmsg_type = cmd,
    554 		.t.tca_family = AF_UNSPEC,
    555 	};
    556 	struct rtattr *tail = NLMSG_TAIL(&req.n);
    557 
    558 	argc -= 1;
    559 	argv += 1;
    560 	if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
    561 		fprintf(stderr, "Illegal \"action\"\n");
    562 		return -1;
    563 	}
    564 	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
    565 
    566 	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) {
    567 		fprintf(stderr, "We have an error talking to the kernel\n");
    568 		ret = -1;
    569 	}
    570 
    571 	*argc_p = argc;
    572 	*argv_p = argv;
    573 
    574 	return ret;
    575 }
    576 
    577 static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event)
    578 {
    579 	struct rtattr *tail, *tail2, *tail3, *tail4;
    580 	int ret = 0, prio = 0, msg_size = 0;
    581 	struct action_util *a = NULL;
    582 	struct nla_bitfield32 flag_select = { 0 };
    583 	char **argv = *argv_p;
    584 	__u32 msec_since = 0;
    585 	int argc = *argc_p;
    586 	char k[16];
    587 	struct {
    588 		struct nlmsghdr         n;
    589 		struct tcamsg           t;
    590 		char                    buf[MAX_MSG];
    591 	} req = {
    592 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
    593 		.t.tca_family = AF_UNSPEC,
    594 	};
    595 
    596 	tail = NLMSG_TAIL(&req.n);
    597 	addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
    598 	tail2 = NLMSG_TAIL(&req.n);
    599 
    600 	strncpy(k, *argv, sizeof(k) - 1);
    601 #ifdef CONFIG_GACT
    602 	if (!gact_ld) {
    603 		get_action_kind("gact");
    604 	}
    605 #endif
    606 	a = get_action_kind(k);
    607 	if (a == NULL) {
    608 		fprintf(stderr, "bad action %s\n", k);
    609 		goto bad_val;
    610 	}
    611 	if (strcmp(a->id, k) != 0) {
    612 		fprintf(stderr, "bad action %s\n", k);
    613 		goto bad_val;
    614 	}
    615 	strncpy(k, *argv, sizeof(k) - 1);
    616 
    617 	argc -= 1;
    618 	argv += 1;
    619 
    620 	if (argc && (strcmp(*argv, "since") == 0)) {
    621 		NEXT_ARG();
    622 		if (get_u32(&msec_since, *argv, 0))
    623 			invarg("dump time \"since\" is invalid", *argv);
    624 	}
    625 
    626 	addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
    627 	addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
    628 	tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
    629 	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
    630 
    631 	tail3 = NLMSG_TAIL(&req.n);
    632 	flag_select.value |= TCA_FLAG_LARGE_DUMP_ON;
    633 	flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON;
    634 	addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
    635 		  sizeof(struct nla_bitfield32));
    636 	tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
    637 	if (msec_since) {
    638 		tail4 = NLMSG_TAIL(&req.n);
    639 		addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since);
    640 		tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4;
    641 	}
    642 	msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
    643 
    644 	if (event == RTM_GETACTION) {
    645 		if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
    646 			perror("Cannot send dump request");
    647 			return 1;
    648 		}
    649 		ret = rtnl_dump_filter(&rth, print_action, stdout);
    650 	}
    651 
    652 	if (event == RTM_DELACTION) {
    653 		req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
    654 		req.n.nlmsg_type = RTM_DELACTION;
    655 		req.n.nlmsg_flags |= NLM_F_ROOT;
    656 		req.n.nlmsg_flags |= NLM_F_REQUEST;
    657 		if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) {
    658 			fprintf(stderr, "We have an error flushing\n");
    659 			return 1;
    660 		}
    661 
    662 	}
    663 
    664 bad_val:
    665 
    666 	*argc_p = argc;
    667 	*argv_p = argv;
    668 	return ret;
    669 }
    670 
    671 int do_action(int argc, char **argv)
    672 {
    673 
    674 	int ret = 0;
    675 
    676 	while (argc > 0) {
    677 
    678 		if (matches(*argv, "add") == 0) {
    679 			ret =  tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
    680 		} else if (matches(*argv, "change") == 0 ||
    681 			  matches(*argv, "replace") == 0) {
    682 			ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
    683 		} else if (matches(*argv, "delete") == 0) {
    684 			argc -= 1;
    685 			argv += 1;
    686 			ret = tc_action_gd(RTM_DELACTION, 0,  &argc, &argv);
    687 		} else if (matches(*argv, "get") == 0) {
    688 			argc -= 1;
    689 			argv += 1;
    690 			ret = tc_action_gd(RTM_GETACTION, 0,  &argc, &argv);
    691 		} else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
    692 						|| matches(*argv, "lst") == 0) {
    693 			if (argc <= 2) {
    694 				act_usage();
    695 				return -1;
    696 			}
    697 
    698 			argc -= 2;
    699 			argv += 2;
    700 			return tc_act_list_or_flush(&argc, &argv,
    701 						    RTM_GETACTION);
    702 		} else if (matches(*argv, "flush") == 0) {
    703 			if (argc <= 2) {
    704 				act_usage();
    705 				return -1;
    706 			}
    707 
    708 			argc -= 2;
    709 			argv += 2;
    710 			return tc_act_list_or_flush(&argc, &argv,
    711 						    RTM_DELACTION);
    712 		} else if (matches(*argv, "help") == 0) {
    713 			act_usage();
    714 			return -1;
    715 		} else {
    716 			fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv);
    717 			return -1;
    718 		}
    719 
    720 		if (ret < 0)
    721 			return -1;
    722 	}
    723 
    724 	return 0;
    725 }
    726