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