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, "Usage: tc filter [ add | del | change | replace | show ] dev STRING\n"); 32 fprintf(stderr, " [ pref PRIO ] protocol PROTO\n"); 33 fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); 34 fprintf(stderr, " [ root | ingress | egress | parent CLASSID ]\n"); 35 fprintf(stderr, " [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"); 36 fprintf(stderr, "\n"); 37 fprintf(stderr, " tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]\n"); 38 fprintf(stderr, "Where:\n"); 39 fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n"); 40 fprintf(stderr, "FILTERID := ... format depends on classifier, see there\n"); 41 fprintf(stderr, "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n"); 42 } 43 44 static int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv) 45 { 46 struct { 47 struct nlmsghdr n; 48 struct tcmsg t; 49 char buf[MAX_MSG]; 50 } req; 51 struct filter_util *q = NULL; 52 __u32 prio = 0; 53 __u32 protocol = 0; 54 int protocol_set = 0; 55 char *fhandle = NULL; 56 char d[16]; 57 char k[16]; 58 struct tc_estimator est; 59 60 memset(&req, 0, sizeof(req)); 61 memset(&est, 0, sizeof(est)); 62 memset(d, 0, sizeof(d)); 63 memset(k, 0, sizeof(k)); 64 memset(&req, 0, sizeof(req)); 65 66 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 67 req.n.nlmsg_flags = NLM_F_REQUEST|flags; 68 req.n.nlmsg_type = cmd; 69 req.t.tcm_family = AF_UNSPEC; 70 71 if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE) 72 protocol = htons(ETH_P_ALL); 73 74 while (argc > 0) { 75 if (strcmp(*argv, "dev") == 0) { 76 NEXT_ARG(); 77 if (d[0]) 78 duparg("dev", *argv); 79 strncpy(d, *argv, sizeof(d)-1); 80 } else if (strcmp(*argv, "root") == 0) { 81 if (req.t.tcm_parent) { 82 fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); 83 return -1; 84 } 85 req.t.tcm_parent = TC_H_ROOT; 86 } else if (strcmp(*argv, "ingress") == 0) { 87 if (req.t.tcm_parent) { 88 fprintf(stderr, "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, "Error: \"egress\" is duplicate parent ID\n"); 96 return -1; 97 } 98 req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, 99 TC_H_MIN_EGRESS); 100 } else if (strcmp(*argv, "parent") == 0) { 101 __u32 handle; 102 NEXT_ARG(); 103 if (req.t.tcm_parent) 104 duparg("parent", *argv); 105 if (get_tc_classid(&handle, *argv)) 106 invarg("Invalid parent ID", *argv); 107 req.t.tcm_parent = handle; 108 } else if (strcmp(*argv, "handle") == 0) { 109 NEXT_ARG(); 110 if (fhandle) 111 duparg("handle", *argv); 112 fhandle = *argv; 113 } else if (matches(*argv, "preference") == 0 || 114 matches(*argv, "priority") == 0) { 115 NEXT_ARG(); 116 if (prio) 117 duparg("priority", *argv); 118 if (get_u32(&prio, *argv, 0) || prio > 0xFFFF) 119 invarg("invalid priority value", *argv); 120 } else if (matches(*argv, "protocol") == 0) { 121 __u16 id; 122 NEXT_ARG(); 123 if (protocol_set) 124 duparg("protocol", *argv); 125 if (ll_proto_a2n(&id, *argv)) 126 invarg("invalid protocol", *argv); 127 protocol = id; 128 protocol_set = 1; 129 } else if (matches(*argv, "estimator") == 0) { 130 if (parse_estimator(&argc, &argv, &est) < 0) 131 return -1; 132 } else if (matches(*argv, "help") == 0) { 133 usage(); 134 return 0; 135 } else { 136 strncpy(k, *argv, sizeof(k)-1); 137 138 q = get_filter_kind(k); 139 argc--; argv++; 140 break; 141 } 142 143 argc--; argv++; 144 } 145 146 req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); 147 148 if (k[0]) 149 addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); 150 151 if (q) { 152 if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) 153 return 1; 154 } else { 155 if (fhandle) { 156 fprintf(stderr, "Must specify filter type when using " 157 "\"handle\"\n"); 158 return -1; 159 } 160 if (argc) { 161 if (matches(*argv, "help") == 0) 162 usage(); 163 fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", *argv); 164 return -1; 165 } 166 } 167 if (est.ewma_log) 168 addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); 169 170 171 if (d[0]) { 172 ll_init_map(&rth); 173 174 if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { 175 fprintf(stderr, "Cannot find device \"%s\"\n", d); 176 return 1; 177 } 178 } 179 180 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) { 181 fprintf(stderr, "We have an error talking to the kernel\n"); 182 return 2; 183 } 184 185 return 0; 186 } 187 188 static __u32 filter_parent; 189 static int filter_ifindex; 190 static __u32 filter_prio; 191 static __u32 filter_protocol; 192 __u16 f_proto = 0; 193 194 int print_filter(const struct sockaddr_nl *who, 195 struct nlmsghdr *n, 196 void *arg) 197 { 198 FILE *fp = (FILE*)arg; 199 struct tcmsg *t = NLMSG_DATA(n); 200 int len = n->nlmsg_len; 201 struct rtattr * tb[TCA_MAX+1]; 202 struct filter_util *q; 203 char abuf[256]; 204 205 if (n->nlmsg_type != RTM_NEWTFILTER && n->nlmsg_type != RTM_DELTFILTER) { 206 fprintf(stderr, "Not a filter\n"); 207 return 0; 208 } 209 len -= NLMSG_LENGTH(sizeof(*t)); 210 if (len < 0) { 211 fprintf(stderr, "Wrong len %d\n", len); 212 return -1; 213 } 214 215 memset(tb, 0, sizeof(tb)); 216 parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); 217 218 if (tb[TCA_KIND] == NULL) { 219 fprintf(stderr, "print_filter: NULL kind\n"); 220 return -1; 221 } 222 223 if (n->nlmsg_type == RTM_DELTFILTER) 224 fprintf(fp, "deleted "); 225 226 fprintf(fp, "filter "); 227 if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) 228 fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); 229 230 if (!filter_parent || filter_parent != t->tcm_parent) { 231 if (t->tcm_parent == TC_H_ROOT) 232 fprintf(fp, "root "); 233 else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)) 234 fprintf(fp, "ingress "); 235 else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)) 236 fprintf(fp, "egress "); 237 else { 238 print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); 239 fprintf(fp, "parent %s ", abuf); 240 } 241 } 242 243 if (t->tcm_info) { 244 f_proto = TC_H_MIN(t->tcm_info); 245 __u32 prio = TC_H_MAJ(t->tcm_info)>>16; 246 if (!filter_protocol || filter_protocol != f_proto) { 247 if (f_proto) { 248 SPRINT_BUF(b1); 249 fprintf(fp, "protocol %s ", 250 ll_proto_n2a(f_proto, b1, sizeof(b1))); 251 } 252 } 253 if (!filter_prio || filter_prio != prio) { 254 if (prio) 255 fprintf(fp, "pref %u ", prio); 256 } 257 } 258 fprintf(fp, "%s ", rta_getattr_str(tb[TCA_KIND])); 259 q = get_filter_kind(RTA_DATA(tb[TCA_KIND])); 260 if (tb[TCA_OPTIONS]) { 261 if (q) 262 q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle); 263 else 264 fprintf(fp, "[cannot parse parameters]"); 265 } 266 fprintf(fp, "\n"); 267 268 if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) { 269 print_tcstats_attr(fp, tb, " ", NULL); 270 fprintf(fp, "\n"); 271 } 272 273 fflush(fp); 274 return 0; 275 } 276 277 static int tc_filter_list(int argc, char **argv) 278 { 279 struct tcmsg t; 280 char d[16]; 281 __u32 prio = 0; 282 __u32 protocol = 0; 283 char *fhandle = NULL; 284 285 memset(&t, 0, sizeof(t)); 286 t.tcm_family = AF_UNSPEC; 287 memset(d, 0, sizeof(d)); 288 289 while (argc > 0) { 290 if (strcmp(*argv, "dev") == 0) { 291 NEXT_ARG(); 292 if (d[0]) 293 duparg("dev", *argv); 294 strncpy(d, *argv, sizeof(d)-1); 295 } else if (strcmp(*argv, "root") == 0) { 296 if (t.tcm_parent) { 297 fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); 298 return -1; 299 } 300 filter_parent = t.tcm_parent = TC_H_ROOT; 301 } else if (strcmp(*argv, "ingress") == 0) { 302 if (t.tcm_parent) { 303 fprintf(stderr, "Error: \"ingress\" is duplicate parent ID\n"); 304 return -1; 305 } 306 filter_parent = TC_H_MAKE(TC_H_CLSACT, 307 TC_H_MIN_INGRESS); 308 t.tcm_parent = filter_parent; 309 } else if (strcmp(*argv, "egress") == 0) { 310 if (t.tcm_parent) { 311 fprintf(stderr, "Error: \"egress\" is duplicate parent ID\n"); 312 return -1; 313 } 314 filter_parent = TC_H_MAKE(TC_H_CLSACT, 315 TC_H_MIN_EGRESS); 316 t.tcm_parent = filter_parent; 317 } else if (strcmp(*argv, "parent") == 0) { 318 __u32 handle; 319 NEXT_ARG(); 320 if (t.tcm_parent) 321 duparg("parent", *argv); 322 if (get_tc_classid(&handle, *argv)) 323 invarg("invalid parent ID", *argv); 324 filter_parent = t.tcm_parent = handle; 325 } else if (strcmp(*argv, "handle") == 0) { 326 NEXT_ARG(); 327 if (fhandle) 328 duparg("handle", *argv); 329 fhandle = *argv; 330 } else if (matches(*argv, "preference") == 0 || 331 matches(*argv, "priority") == 0) { 332 NEXT_ARG(); 333 if (prio) 334 duparg("priority", *argv); 335 if (get_u32(&prio, *argv, 0)) 336 invarg("invalid preference", *argv); 337 filter_prio = prio; 338 } else if (matches(*argv, "protocol") == 0) { 339 __u16 res; 340 NEXT_ARG(); 341 if (protocol) 342 duparg("protocol", *argv); 343 if (ll_proto_a2n(&res, *argv)) 344 invarg("invalid protocol", *argv); 345 protocol = res; 346 filter_protocol = protocol; 347 } else if (matches(*argv, "help") == 0) { 348 usage(); 349 } else { 350 fprintf(stderr, " What is \"%s\"? Try \"tc filter help\"\n", *argv); 351 return -1; 352 } 353 354 argc--; argv++; 355 } 356 357 t.tcm_info = TC_H_MAKE(prio<<16, protocol); 358 359 ll_init_map(&rth); 360 361 if (d[0]) { 362 if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { 363 fprintf(stderr, "Cannot find device \"%s\"\n", d); 364 return 1; 365 } 366 filter_ifindex = t.tcm_ifindex; 367 } 368 369 if (rtnl_dump_request(&rth, RTM_GETTFILTER, &t, sizeof(t)) < 0) { 370 perror("Cannot send dump request"); 371 return 1; 372 } 373 374 if (rtnl_dump_filter(&rth, print_filter, stdout) < 0) { 375 fprintf(stderr, "Dump terminated\n"); 376 return 1; 377 } 378 379 return 0; 380 } 381 382 int do_filter(int argc, char **argv) 383 { 384 if (argc < 1) 385 return tc_filter_list(0, NULL); 386 if (matches(*argv, "add") == 0) 387 return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); 388 if (matches(*argv, "change") == 0) 389 return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1); 390 if (matches(*argv, "replace") == 0) 391 return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, argv+1); 392 if (matches(*argv, "delete") == 0) 393 return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1); 394 #if 0 395 if (matches(*argv, "get") == 0) 396 return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1); 397 #endif 398 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 399 || matches(*argv, "lst") == 0) 400 return tc_filter_list(argc-1, argv+1); 401 if (matches(*argv, "help") == 0) { 402 usage(); 403 return 0; 404 } 405 fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", *argv); 406 return -1; 407 } 408