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