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