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