1 /* 2 * tc_filter.c "tc filter". 3 * 4 * This program is free software; you can redistribute 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: Alexey Kuznetsov, <kuznet (at) ms2.inr.ac.ru> 10 * 11 */ 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <unistd.h> 16 #include <syslog.h> 17 #include <fcntl.h> 18 #include <sys/socket.h> 19 #include <netinet/in.h> 20 #include <arpa/inet.h> 21 #include <string.h> 22 #include <linux/if_ether.h> 23 24 #include "rt_names.h" 25 #include "utils.h" 26 #include "tc_util.h" 27 #include "tc_common.h" 28 29 static void usage(void) 30 { 31 fprintf(stderr, 32 "Usage: tc filter [ add | del | change | replace | show ] dev STRING\n" 33 "Usage: tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n" 34 " [ pref PRIO ] protocol PROTO [ chain CHAIN_INDEX ]\n" 35 " [ estimator INTERVAL TIME_CONSTANT ]\n" 36 " [ root | ingress | egress | parent CLASSID ]\n" 37 " [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" 38 "\n" 39 " tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]\n" 40 "Where:\n" 41 "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n" 42 "FILTERID := ... format depends on classifier, see there\n" 43 "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n"); 44 } 45 46 static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv) 47 { 48 struct { 49 struct nlmsghdr n; 50 struct tcmsg t; 51 char buf[MAX_MSG]; 52 } req = { 53 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 54 .n.nlmsg_flags = NLM_F_REQUEST | flags, 55 .n.nlmsg_type = cmd, 56 .t.tcm_family = AF_UNSPEC, 57 }; 58 struct filter_util *q = NULL; 59 __u32 prio = 0; 60 __u32 protocol = 0; 61 int protocol_set = 0; 62 __u32 chain_index; 63 int chain_index_set = 0; 64 char *fhandle = NULL; 65 char d[16] = {}; 66 char k[16] = {}; 67 struct tc_estimator est = {}; 68 69 if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE) 70 protocol = htons(ETH_P_ALL); 71 72 while (argc > 0) { 73 if (strcmp(*argv, "dev") == 0) { 74 NEXT_ARG(); 75 if (d[0]) 76 duparg("dev", *argv); 77 strncpy(d, *argv, sizeof(d)-1); 78 } else if (strcmp(*argv, "root") == 0) { 79 if (req.t.tcm_parent) { 80 fprintf(stderr, 81 "Error: \"root\" is duplicate parent ID\n"); 82 return -1; 83 } 84 req.t.tcm_parent = TC_H_ROOT; 85 } else if (strcmp(*argv, "ingress") == 0) { 86 if (req.t.tcm_parent) { 87 fprintf(stderr, 88 "Error: \"ingress\" is duplicate parent ID\n"); 89 return -1; 90 } 91 req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, 92 TC_H_MIN_INGRESS); 93 } else if (strcmp(*argv, "egress") == 0) { 94 if (req.t.tcm_parent) { 95 fprintf(stderr, 96 "Error: \"egress\" is duplicate parent ID\n"); 97 return -1; 98 } 99 req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, 100 TC_H_MIN_EGRESS); 101 } else if (strcmp(*argv, "parent") == 0) { 102 __u32 handle; 103 104 NEXT_ARG(); 105 if (req.t.tcm_parent) 106 duparg("parent", *argv); 107 if (get_tc_classid(&handle, *argv)) 108 invarg("Invalid parent ID", *argv); 109 req.t.tcm_parent = handle; 110 } else if (strcmp(*argv, "handle") == 0) { 111 NEXT_ARG(); 112 if (fhandle) 113 duparg("handle", *argv); 114 fhandle = *argv; 115 } else if (matches(*argv, "preference") == 0 || 116 matches(*argv, "priority") == 0) { 117 NEXT_ARG(); 118 if (prio) 119 duparg("priority", *argv); 120 if (get_u32(&prio, *argv, 0) || prio > 0xFFFF) 121 invarg("invalid priority value", *argv); 122 } else if (matches(*argv, "protocol") == 0) { 123 __u16 id; 124 125 NEXT_ARG(); 126 if (protocol_set) 127 duparg("protocol", *argv); 128 if (ll_proto_a2n(&id, *argv)) 129 invarg("invalid protocol", *argv); 130 protocol = id; 131 protocol_set = 1; 132 } else if (matches(*argv, "chain") == 0) { 133 NEXT_ARG(); 134 if (chain_index_set) 135 duparg("chain", *argv); 136 if (get_u32(&chain_index, *argv, 0)) 137 invarg("invalid chain index value", *argv); 138 chain_index_set = 1; 139 } else if (matches(*argv, "estimator") == 0) { 140 if (parse_estimator(&argc, &argv, &est) < 0) 141 return -1; 142 } else if (matches(*argv, "help") == 0) { 143 usage(); 144 return 0; 145 } else { 146 strncpy(k, *argv, sizeof(k)-1); 147 148 q = get_filter_kind(k); 149 argc--; argv++; 150 break; 151 } 152 153 argc--; argv++; 154 } 155 156 req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); 157 158 if (chain_index_set) 159 addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index); 160 161 if (k[0]) 162 addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); 163 164 if (q) { 165 if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) 166 return 1; 167 } else { 168 if (fhandle) { 169 fprintf(stderr, 170 "Must specify filter type when using \"handle\"\n"); 171 return -1; 172 } 173 if (argc) { 174 if (matches(*argv, "help") == 0) 175 usage(); 176 fprintf(stderr, 177 "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", 178 *argv); 179 return -1; 180 } 181 } 182 183 if (est.ewma_log) 184 addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); 185 186 187 if (d[0]) { 188 ll_init_map(&rth); 189 190 req.t.tcm_ifindex = ll_name_to_index(d); 191 if (req.t.tcm_ifindex == 0) { 192 fprintf(stderr, "Cannot find device \"%s\"\n", d); 193 return 1; 194 } 195 } 196 197 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) { 198 fprintf(stderr, "We have an error talking to the kernel\n"); 199 return 2; 200 } 201 202 return 0; 203 } 204 205 static __u32 filter_parent; 206 static int filter_ifindex; 207 static __u32 filter_prio; 208 static __u32 filter_protocol; 209 static __u32 filter_chain_index; 210 static int filter_chain_index_set; 211 __u16 f_proto; 212 213 int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 214 { 215 FILE *fp = (FILE *)arg; 216 struct tcmsg *t = NLMSG_DATA(n); 217 int len = n->nlmsg_len; 218 struct rtattr *tb[TCA_MAX+1]; 219 struct filter_util *q; 220 char abuf[256]; 221 222 if (n->nlmsg_type != RTM_NEWTFILTER && 223 n->nlmsg_type != RTM_GETTFILTER && 224 n->nlmsg_type != RTM_DELTFILTER) { 225 fprintf(stderr, "Not a filter(cmd %d)\n", n->nlmsg_type); 226 return 0; 227 } 228 len -= NLMSG_LENGTH(sizeof(*t)); 229 if (len < 0) { 230 fprintf(stderr, "Wrong len %d\n", len); 231 return -1; 232 } 233 234 parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); 235 236 if (tb[TCA_KIND] == NULL) { 237 fprintf(stderr, "print_filter: NULL kind\n"); 238 return -1; 239 } 240 241 if (n->nlmsg_type == RTM_DELTFILTER) 242 fprintf(fp, "deleted "); 243 244 if (n->nlmsg_type == RTM_NEWTFILTER && 245 (n->nlmsg_flags & NLM_F_CREATE) && 246 !(n->nlmsg_flags & NLM_F_EXCL)) 247 fprintf(fp, "replaced "); 248 249 if (n->nlmsg_type == RTM_NEWTFILTER && 250 (n->nlmsg_flags & NLM_F_CREATE) && 251 (n->nlmsg_flags & NLM_F_EXCL)) 252 fprintf(fp, "added "); 253 254 fprintf(fp, "filter "); 255 if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) 256 fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); 257 258 if (!filter_parent || filter_parent != t->tcm_parent) { 259 if (t->tcm_parent == TC_H_ROOT) 260 fprintf(fp, "root "); 261 else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)) 262 fprintf(fp, "ingress "); 263 else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)) 264 fprintf(fp, "egress "); 265 else { 266 print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); 267 fprintf(fp, "parent %s ", abuf); 268 } 269 } 270 271 if (t->tcm_info) { 272 f_proto = TC_H_MIN(t->tcm_info); 273 __u32 prio = TC_H_MAJ(t->tcm_info)>>16; 274 275 if (!filter_protocol || filter_protocol != f_proto) { 276 if (f_proto) { 277 SPRINT_BUF(b1); 278 fprintf(fp, "protocol %s ", 279 ll_proto_n2a(f_proto, b1, sizeof(b1))); 280 } 281 } 282 if (!filter_prio || filter_prio != prio) { 283 if (prio) 284 fprintf(fp, "pref %u ", prio); 285 } 286 } 287 fprintf(fp, "%s ", rta_getattr_str(tb[TCA_KIND])); 288 289 if (tb[TCA_CHAIN]) { 290 __u32 chain_index = rta_getattr_u32(tb[TCA_CHAIN]); 291 292 if (!filter_chain_index_set || 293 filter_chain_index != chain_index) 294 fprintf(fp, "chain %u ", chain_index); 295 } 296 297 q = get_filter_kind(RTA_DATA(tb[TCA_KIND])); 298 if (tb[TCA_OPTIONS]) { 299 if (q) 300 q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle); 301 else 302 fprintf(fp, "[cannot parse parameters]"); 303 } 304 fprintf(fp, "\n"); 305 306 if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) { 307 print_tcstats_attr(fp, tb, " ", NULL); 308 fprintf(fp, "\n"); 309 } 310 311 fflush(fp); 312 return 0; 313 } 314 315 static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv) 316 { 317 struct { 318 struct nlmsghdr n; 319 struct tcmsg t; 320 char buf[MAX_MSG]; 321 } req = { 322 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 323 /* NLM_F_ECHO is for backward compatibility. old kernels never 324 * respond without it and newer kernels will ignore it. 325 * In old kernels there is a side effect: 326 * In addition to a response to the GET you will receive an 327 * event (if you do tc mon). 328 */ 329 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO | flags, 330 .n.nlmsg_type = cmd, 331 .t.tcm_parent = TC_H_UNSPEC, 332 .t.tcm_family = AF_UNSPEC, 333 }; 334 struct filter_util *q = NULL; 335 __u32 prio = 0; 336 __u32 protocol = 0; 337 int protocol_set = 0; 338 __u32 chain_index; 339 int chain_index_set = 0; 340 __u32 parent_handle = 0; 341 char *fhandle = NULL; 342 char d[16] = {}; 343 char k[16] = {}; 344 345 while (argc > 0) { 346 if (strcmp(*argv, "dev") == 0) { 347 NEXT_ARG(); 348 if (d[0]) 349 duparg("dev", *argv); 350 strncpy(d, *argv, sizeof(d)-1); 351 } else if (strcmp(*argv, "root") == 0) { 352 if (req.t.tcm_parent) { 353 fprintf(stderr, 354 "Error: \"root\" is duplicate parent ID\n"); 355 return -1; 356 } 357 req.t.tcm_parent = TC_H_ROOT; 358 } else if (strcmp(*argv, "ingress") == 0) { 359 if (req.t.tcm_parent) { 360 fprintf(stderr, 361 "Error: \"ingress\" is duplicate parent ID\n"); 362 return -1; 363 } 364 req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, 365 TC_H_MIN_INGRESS); 366 } else if (strcmp(*argv, "egress") == 0) { 367 if (req.t.tcm_parent) { 368 fprintf(stderr, 369 "Error: \"egress\" is duplicate parent ID\n"); 370 return -1; 371 } 372 req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, 373 TC_H_MIN_EGRESS); 374 } else if (strcmp(*argv, "parent") == 0) { 375 376 NEXT_ARG(); 377 if (req.t.tcm_parent) 378 duparg("parent", *argv); 379 if (get_tc_classid(&parent_handle, *argv)) 380 invarg("Invalid parent ID", *argv); 381 req.t.tcm_parent = parent_handle; 382 } else if (strcmp(*argv, "handle") == 0) { 383 NEXT_ARG(); 384 if (fhandle) 385 duparg("handle", *argv); 386 fhandle = *argv; 387 } else if (matches(*argv, "preference") == 0 || 388 matches(*argv, "priority") == 0) { 389 NEXT_ARG(); 390 if (prio) 391 duparg("priority", *argv); 392 if (get_u32(&prio, *argv, 0) || prio > 0xFFFF) 393 invarg("invalid priority value", *argv); 394 } else if (matches(*argv, "protocol") == 0) { 395 __u16 id; 396 397 NEXT_ARG(); 398 if (protocol_set) 399 duparg("protocol", *argv); 400 if (ll_proto_a2n(&id, *argv)) 401 invarg("invalid protocol", *argv); 402 protocol = id; 403 protocol_set = 1; 404 } else if (matches(*argv, "chain") == 0) { 405 NEXT_ARG(); 406 if (chain_index_set) 407 duparg("chain", *argv); 408 if (get_u32(&chain_index, *argv, 0)) 409 invarg("invalid chain index value", *argv); 410 chain_index_set = 1; 411 } else if (matches(*argv, "help") == 0) { 412 usage(); 413 return 0; 414 } else { 415 if (!**argv) 416 invarg("invalid filter name", *argv); 417 418 strncpy(k, *argv, sizeof(k)-1); 419 420 q = get_filter_kind(k); 421 argc--; argv++; 422 break; 423 } 424 425 argc--; argv++; 426 } 427 428 if (!protocol_set) { 429 fprintf(stderr, "Must specify filter protocol\n"); 430 return -1; 431 } 432 433 if (!prio) { 434 fprintf(stderr, "Must specify filter priority\n"); 435 return -1; 436 } 437 438 req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); 439 440 if (chain_index_set) 441 addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index); 442 443 if (req.t.tcm_parent == TC_H_UNSPEC) { 444 fprintf(stderr, "Must specify filter parent\n"); 445 return -1; 446 } 447 448 if (k[0]) 449 addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); 450 else { 451 fprintf(stderr, "Must specify filter type\n"); 452 return -1; 453 } 454 455 if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) 456 return 1; 457 458 459 if (!fhandle) { 460 fprintf(stderr, "Must specify filter \"handle\"\n"); 461 return -1; 462 } 463 464 if (argc) { 465 if (matches(*argv, "help") == 0) 466 usage(); 467 fprintf(stderr, 468 "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", 469 *argv); 470 return -1; 471 } 472 473 if (d[0]) { 474 ll_init_map(&rth); 475 476 req.t.tcm_ifindex = ll_name_to_index(d); 477 if (req.t.tcm_ifindex == 0) { 478 fprintf(stderr, "Cannot find device \"%s\"\n", d); 479 return 1; 480 } 481 filter_ifindex = req.t.tcm_ifindex; 482 } else { 483 fprintf(stderr, "Must specify netdevice \"dev\"\n"); 484 return -1; 485 } 486 487 if (rtnl_talk(&rth, &req.n, &req.n, MAX_MSG) < 0) { 488 fprintf(stderr, "We have an error talking to the kernel\n"); 489 return 2; 490 } 491 492 print_filter(NULL, &req.n, (void *)stdout); 493 494 return 0; 495 } 496 497 static int tc_filter_list(int argc, char **argv) 498 { 499 struct { 500 struct nlmsghdr n; 501 struct tcmsg t; 502 char buf[MAX_MSG]; 503 } req = { 504 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 505 .n.nlmsg_type = RTM_GETTFILTER, 506 .t.tcm_parent = TC_H_UNSPEC, 507 .t.tcm_family = AF_UNSPEC, 508 }; 509 char d[16] = {}; 510 __u32 prio = 0; 511 __u32 protocol = 0; 512 __u32 chain_index; 513 char *fhandle = NULL; 514 515 while (argc > 0) { 516 if (strcmp(*argv, "dev") == 0) { 517 NEXT_ARG(); 518 if (d[0]) 519 duparg("dev", *argv); 520 strncpy(d, *argv, sizeof(d)-1); 521 } else if (strcmp(*argv, "root") == 0) { 522 if (req.t.tcm_parent) { 523 fprintf(stderr, 524 "Error: \"root\" is duplicate parent ID\n"); 525 return -1; 526 } 527 filter_parent = req.t.tcm_parent = TC_H_ROOT; 528 } else if (strcmp(*argv, "ingress") == 0) { 529 if (req.t.tcm_parent) { 530 fprintf(stderr, 531 "Error: \"ingress\" is duplicate parent ID\n"); 532 return -1; 533 } 534 filter_parent = TC_H_MAKE(TC_H_CLSACT, 535 TC_H_MIN_INGRESS); 536 req.t.tcm_parent = filter_parent; 537 } else if (strcmp(*argv, "egress") == 0) { 538 if (req.t.tcm_parent) { 539 fprintf(stderr, 540 "Error: \"egress\" is duplicate parent ID\n"); 541 return -1; 542 } 543 filter_parent = TC_H_MAKE(TC_H_CLSACT, 544 TC_H_MIN_EGRESS); 545 req.t.tcm_parent = filter_parent; 546 } else if (strcmp(*argv, "parent") == 0) { 547 __u32 handle; 548 549 NEXT_ARG(); 550 if (req.t.tcm_parent) 551 duparg("parent", *argv); 552 if (get_tc_classid(&handle, *argv)) 553 invarg("invalid parent ID", *argv); 554 filter_parent = req.t.tcm_parent = handle; 555 } else if (strcmp(*argv, "handle") == 0) { 556 NEXT_ARG(); 557 if (fhandle) 558 duparg("handle", *argv); 559 fhandle = *argv; 560 } else if (matches(*argv, "preference") == 0 || 561 matches(*argv, "priority") == 0) { 562 NEXT_ARG(); 563 if (prio) 564 duparg("priority", *argv); 565 if (get_u32(&prio, *argv, 0)) 566 invarg("invalid preference", *argv); 567 filter_prio = prio; 568 } else if (matches(*argv, "protocol") == 0) { 569 __u16 res; 570 571 NEXT_ARG(); 572 if (protocol) 573 duparg("protocol", *argv); 574 if (ll_proto_a2n(&res, *argv)) 575 invarg("invalid protocol", *argv); 576 protocol = res; 577 filter_protocol = protocol; 578 } else if (matches(*argv, "chain") == 0) { 579 NEXT_ARG(); 580 if (filter_chain_index_set) 581 duparg("chain", *argv); 582 if (get_u32(&chain_index, *argv, 0)) 583 invarg("invalid chain index value", *argv); 584 filter_chain_index_set = 1; 585 filter_chain_index = chain_index; 586 } else if (matches(*argv, "help") == 0) { 587 usage(); 588 } else { 589 fprintf(stderr, 590 " What is \"%s\"? Try \"tc filter help\"\n", 591 *argv); 592 return -1; 593 } 594 595 argc--; argv++; 596 } 597 598 req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); 599 600 ll_init_map(&rth); 601 602 if (d[0]) { 603 req.t.tcm_ifindex = ll_name_to_index(d); 604 if (req.t.tcm_ifindex == 0) { 605 fprintf(stderr, "Cannot find device \"%s\"\n", d); 606 return 1; 607 } 608 filter_ifindex = req.t.tcm_ifindex; 609 } 610 611 if (filter_chain_index_set) 612 addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index); 613 614 if (rtnl_dump_request_n(&rth, &req.n) < 0) { 615 perror("Cannot send dump request"); 616 return 1; 617 } 618 619 if (rtnl_dump_filter(&rth, print_filter, stdout) < 0) { 620 fprintf(stderr, "Dump terminated\n"); 621 return 1; 622 } 623 624 return 0; 625 } 626 627 int do_filter(int argc, char **argv) 628 { 629 if (argc < 1) 630 return tc_filter_list(0, NULL); 631 if (matches(*argv, "add") == 0) 632 return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, 633 argc-1, argv+1); 634 if (matches(*argv, "change") == 0) 635 return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1); 636 if (matches(*argv, "replace") == 0) 637 return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, 638 argv+1); 639 if (matches(*argv, "delete") == 0) 640 return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1); 641 if (matches(*argv, "get") == 0) 642 return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1); 643 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 644 || matches(*argv, "lst") == 0) 645 return tc_filter_list(argc-1, argv+1); 646 if (matches(*argv, "help") == 0) { 647 usage(); 648 return 0; 649 } 650 fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", 651 *argv); 652 return -1; 653 } 654