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