m_action.c revision b159a7f1aeb4c2fc3a9053d314bbc4d235beac67
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@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 31static struct action_util * action_list; 32#ifdef CONFIG_GACT 33int gact_ld = 0 ; //fuckin backward compatibility 34#endif 35int batch_c = 0; 36int tab_flush = 0; 37 38static 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 etc\n" 59 "\t\tEach action has its own parameters (ACTPARAMS)\n" 60 "\n"); 61 62 exit(-1); 63} 64 65static 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) RTA_PAYLOAD(opt)); 70 return 0; 71} 72 73static 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 86static 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; 94restart_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 117reg: 118 a->next = action_list; 119 action_list = a; 120 return a; 121 122noexist: 123#ifdef CONFIG_GACT 124 if (!looked4gact) { 125 looked4gact = 1; 126 strcpy(str,"gact"); 127 goto restart_s; 128 } 129#endif 130 a = malloc(sizeof(*a)); 131 if (a) { 132 memset(a, 0, sizeof(*a)); 133 strncpy(a->id, "noact", 15); 134 a->parse_aopt = parse_noaopt; 135 a->print_aopt = print_noaopt; 136 goto reg; 137 } 138 return a; 139} 140 141static int 142new_cmd(char **argv) 143{ 144 if ((matches(*argv, "change") == 0) || 145 (matches(*argv, "replace") == 0)|| 146 (matches(*argv, "delete") == 0)|| 147 (matches(*argv, "get") == 0)|| 148 (matches(*argv, "add") == 0)) 149 return 1; 150 151 return 0; 152 153} 154 155int 156parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) 157{ 158 int argc = *argc_p; 159 char **argv = *argv_p; 160 struct rtattr *tail, *tail2; 161 char k[16]; 162 int ok = 0; 163 int eap = 0; /* expect action parameters */ 164 165 int ret = 0; 166 int prio = 0; 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, "help") == 0) { 190 return -1; 191 } else if (new_cmd(argv)) { 192 goto done0; 193 } else { 194 struct action_util *a = NULL; 195 strncpy(k, *argv, sizeof (k) - 1); 196 eap = 0; 197 if (argc > 0 ) { 198 a = get_action_kind(k); 199 } else { 200done0: 201 if (ok) 202 break; 203 else 204 goto done; 205 } 206 207 if (NULL == a) { 208 goto bad_val; 209 } 210 211 tail = NLMSG_TAIL(n); 212 addattr_l(n, MAX_MSG, ++prio, NULL, 0); 213 addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); 214 215 ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n); 216 217 if (ret < 0) { 218 fprintf(stderr,"bad action parsing\n"); 219 goto bad_val; 220 } 221 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 222 ok++; 223 } 224 225 } 226 227 if (eap > 0) { 228 fprintf(stderr,"bad action empty %d\n",eap); 229 goto bad_val; 230 } 231 232 tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2; 233 234done: 235 *argc_p = argc; 236 *argv_p = argv; 237 return 0; 238bad_val: 239 /* no need to undo things, returning from here should 240 * cause enough pain */ 241 fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv); 242 return -1; 243} 244 245static int 246tc_print_one_action(FILE * f, struct rtattr *arg) 247{ 248 249 struct rtattr *tb[TCA_ACT_MAX + 1]; 250 int err = 0; 251 struct action_util *a = NULL; 252 253 if (arg == NULL) 254 return -1; 255 256 parse_rtattr_nested(tb, TCA_ACT_MAX, arg); 257 if (tb[TCA_ACT_KIND] == NULL) { 258 fprintf(stderr, "NULL Action!\n"); 259 return -1; 260 } 261 262 263 a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND])); 264 if (NULL == a) 265 return err; 266 267 if (tab_flush) { 268 fprintf(f," %s \n", a->id); 269 tab_flush = 0; 270 return 0; 271 } 272 273 err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]); 274 275 276 if (0 > err) 277 return err; 278 279 if (show_stats && tb[TCA_ACT_STATS]) { 280 fprintf(f, "\tAction statistics:\n"); 281 print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL); 282 fprintf(f, "\n"); 283 } 284 285 return 0; 286} 287 288int 289tc_print_action(FILE * f, const struct rtattr *arg) 290{ 291 292 int i; 293 struct rtattr *tb[TCA_ACT_MAX_PRIO + 1]; 294 295 if (arg == NULL) 296 return 0; 297 298 parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg); 299 300 if (tab_flush && NULL != tb[0] && NULL == tb[1]) { 301 int ret = tc_print_one_action(f, tb[0]); 302 return ret; 303 } 304 305 for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { 306 if (tb[i]) { 307 fprintf(f, "\n\taction order %d: ", i + batch_c); 308 if (0 > tc_print_one_action(f, tb[i])) { 309 fprintf(f, "Error printing action\n"); 310 } 311 } 312 313 } 314 315 batch_c+=TCA_ACT_MAX_PRIO ; 316 return 0; 317} 318 319int print_action(const struct sockaddr_nl *who, 320 struct nlmsghdr *n, 321 void *arg) 322{ 323 FILE *fp = (FILE*)arg; 324 struct tcamsg *t = NLMSG_DATA(n); 325 int len = n->nlmsg_len; 326 struct rtattr * tb[TCAA_MAX+1]; 327 328 len -= NLMSG_LENGTH(sizeof(*t)); 329 330 if (len < 0) { 331 fprintf(stderr, "Wrong len %d\n", len); 332 return -1; 333 } 334 335 parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len); 336 337 if (NULL == tb[TCA_ACT_TAB]) { 338 if (n->nlmsg_type != RTM_GETACTION) 339 fprintf(stderr, "print_action: NULL kind\n"); 340 return -1; 341 } 342 343 if (n->nlmsg_type == RTM_DELACTION) { 344 if (n->nlmsg_flags & NLM_F_ROOT) { 345 fprintf(fp, "Flushed table "); 346 tab_flush = 1; 347 } else { 348 fprintf(fp, "deleted action "); 349 } 350 } 351 352 if (n->nlmsg_type == RTM_NEWACTION) 353 fprintf(fp, "Added action "); 354 tc_print_action(fp, tb[TCA_ACT_TAB]); 355 356 return 0; 357} 358 359static int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p) 360{ 361 char k[16]; 362 struct action_util *a = NULL; 363 int argc = *argc_p; 364 char **argv = *argv_p; 365 int prio = 0; 366 int ret = 0; 367 __u32 i; 368 struct sockaddr_nl nladdr; 369 struct rtattr *tail; 370 struct rtattr *tail2; 371 struct nlmsghdr *ans = NULL; 372 373 struct { 374 struct nlmsghdr n; 375 struct tcamsg t; 376 char buf[MAX_MSG]; 377 } req; 378 379 req.t.tca_family = AF_UNSPEC; 380 381 memset(&req, 0, sizeof(req)); 382 383 memset(&nladdr, 0, sizeof(nladdr)); 384 nladdr.nl_family = AF_NETLINK; 385 386 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); 387 req.n.nlmsg_flags = NLM_F_REQUEST|flags; 388 req.n.nlmsg_type = cmd; 389 argc -=1; 390 argv +=1; 391 392 393 tail = NLMSG_TAIL(&req.n); 394 addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); 395 396 while (argc > 0) { 397 if (strcmp(*argv, "action") == 0 ) { 398 argc--; 399 argv++; 400 continue; 401 } else if (strcmp(*argv, "help") == 0) { 402 return -1; 403 } 404 405 strncpy(k, *argv, sizeof (k) - 1); 406 a = get_action_kind(k); 407 if (NULL == a) { 408 fprintf(stderr, "Error: non existent action: %s\n",k); 409 ret = -1; 410 goto bad_val; 411 } 412 if (strcmp(a->id, k) != 0) { 413 fprintf(stderr, "Error: non existent action: %s\n",k); 414 ret = -1; 415 goto bad_val; 416 } 417 418 argc -=1; 419 argv +=1; 420 if (argc <= 0) { 421 fprintf(stderr, "Error: no index specified action: %s\n",k); 422 ret = -1; 423 goto bad_val; 424 } 425 426 if (matches(*argv, "index") == 0) { 427 NEXT_ARG(); 428 if (get_u32(&i, *argv, 10)) { 429 fprintf(stderr, "Illegal \"index\"\n"); 430 ret = -1; 431 goto bad_val; 432 } 433 argc -=1; 434 argv +=1; 435 } else { 436 fprintf(stderr, "Error: no index specified action: %s\n",k); 437 ret = -1; 438 goto bad_val; 439 } 440 441 tail2 = NLMSG_TAIL(&req.n); 442 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); 443 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); 444 addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); 445 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; 446 447 } 448 449 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; 450 451 req.n.nlmsg_seq = rth.dump = ++rth.seq; 452 if (cmd == RTM_GETACTION) 453 ans = &req.n; 454 455 if (rtnl_talk(&rth, &req.n, 0, 0, ans) < 0) { 456 fprintf(stderr, "We have an error talking to the kernel\n"); 457 return 1; 458 } 459 460 if (ans && print_action(NULL, &req.n, (void*)stdout) < 0) { 461 fprintf(stderr, "Dump terminated\n"); 462 return 1; 463 } 464 465 *argc_p = argc; 466 *argv_p = argv; 467bad_val: 468 return ret; 469} 470 471static int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p) 472{ 473 int argc = *argc_p; 474 char **argv = *argv_p; 475 int ret = 0; 476 477 struct rtattr *tail; 478 struct { 479 struct nlmsghdr n; 480 struct tcamsg t; 481 char buf[MAX_MSG]; 482 } req; 483 484 req.t.tca_family = AF_UNSPEC; 485 486 memset(&req, 0, sizeof(req)); 487 488 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); 489 req.n.nlmsg_flags = NLM_F_REQUEST|flags; 490 req.n.nlmsg_type = cmd; 491 tail = NLMSG_TAIL(&req.n); 492 argc -=1; 493 argv +=1; 494 if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) { 495 fprintf(stderr, "Illegal \"action\"\n"); 496 return -1; 497 } 498 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; 499 500 if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) { 501 fprintf(stderr, "We have an error talking to the kernel\n"); 502 ret = -1; 503 } 504 505 *argc_p = argc; 506 *argv_p = argv; 507 508 return ret; 509} 510 511static int tc_act_list_or_flush(int argc, char **argv, int event) 512{ 513 int ret = 0, prio = 0, msg_size = 0; 514 char k[16]; 515 struct rtattr *tail,*tail2; 516 struct action_util *a = NULL; 517 struct { 518 struct nlmsghdr n; 519 struct tcamsg t; 520 char buf[MAX_MSG]; 521 } req; 522 523 req.t.tca_family = AF_UNSPEC; 524 525 memset(&req, 0, sizeof(req)); 526 527 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); 528 529 tail = NLMSG_TAIL(&req.n); 530 addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); 531 tail2 = NLMSG_TAIL(&req.n); 532 533 strncpy(k, *argv, sizeof (k) - 1); 534#ifdef CONFIG_GACT 535 if (!gact_ld) { 536 get_action_kind("gact"); 537 } 538#endif 539 a = get_action_kind(k); 540 if (NULL == a) { 541 fprintf(stderr,"bad action %s\n",k); 542 goto bad_val; 543 } 544 if (strcmp(a->id, k) != 0) { 545 fprintf(stderr,"bad action %s\n",k); 546 goto bad_val; 547 } 548 strncpy(k, *argv, sizeof (k) - 1); 549 550 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); 551 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); 552 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; 553 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; 554 555 msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr)); 556 557 if (event == RTM_GETACTION) { 558 if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) { 559 perror("Cannot send dump request"); 560 return 1; 561 } 562 ret = rtnl_dump_filter(&rth, print_action, stdout); 563 } 564 565 if (event == RTM_DELACTION) { 566 req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); 567 req.n.nlmsg_type = RTM_DELACTION; 568 req.n.nlmsg_flags |= NLM_F_ROOT; 569 req.n.nlmsg_flags |= NLM_F_REQUEST; 570 if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) { 571 fprintf(stderr, "We have an error flushing\n"); 572 return 1; 573 } 574 575 } 576 577bad_val: 578 579 return ret; 580} 581 582int do_action(int argc, char **argv) 583{ 584 585 int ret = 0; 586 587 while (argc > 0) { 588 589 if (matches(*argv, "add") == 0) { 590 ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv); 591 } else if (matches(*argv, "change") == 0 || 592 matches(*argv, "replace") == 0) { 593 ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv); 594 } else if (matches(*argv, "delete") == 0) { 595 argc -=1; 596 argv +=1; 597 ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv); 598 } else if (matches(*argv, "get") == 0) { 599 argc -=1; 600 argv +=1; 601 ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv); 602 } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 603 || matches(*argv, "lst") == 0) { 604 if (argc <= 2) { 605 act_usage(); 606 return -1; 607 } 608 return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION); 609 } else if (matches(*argv, "flush") == 0) { 610 if (argc <= 2) { 611 act_usage(); 612 return -1; 613 } 614 return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION); 615 } else if (matches(*argv, "help") == 0) { 616 act_usage(); 617 return -1; 618 } else { 619 620 ret = -1; 621 } 622 623 if (ret < 0) { 624 fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv); 625 return -1; 626 } 627 } 628 629 return 0; 630} 631 632