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