1 /* 2 * tc_qdisc.c "tc qdisc". 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 * J Hadi Salim: Extension to ingress 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 <math.h> 23 #include <malloc.h> 24 25 #include "utils.h" 26 #include "tc_util.h" 27 #include "tc_common.h" 28 29 static int usage(void) 30 { 31 fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | show ] dev STRING\n"); 32 fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | clsact | parent CLASSID ]\n"); 33 fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); 34 fprintf(stderr, " [ stab [ help | STAB_OPTIONS] ]\n"); 35 fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); 36 fprintf(stderr, "\n"); 37 fprintf(stderr, " tc qdisc show [ dev STRING ] [ ingress | clsact ] [ invisible ]\n"); 38 fprintf(stderr, "Where:\n"); 39 fprintf(stderr, "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"); 40 fprintf(stderr, "OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help\n"); 41 fprintf(stderr, "STAB_OPTIONS := ... try tc qdisc add stab help\n"); 42 return -1; 43 } 44 45 static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) 46 { 47 struct qdisc_util *q = NULL; 48 struct tc_estimator est = {}; 49 struct { 50 struct tc_sizespec szopts; 51 __u16 *data; 52 } stab = {}; 53 char d[16] = {}; 54 char k[16] = {}; 55 struct { 56 struct nlmsghdr n; 57 struct tcmsg t; 58 char buf[TCA_BUF_MAX]; 59 } req = { 60 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 61 .n.nlmsg_flags = NLM_F_REQUEST | flags, 62 .n.nlmsg_type = cmd, 63 .t.tcm_family = AF_UNSPEC, 64 }; 65 66 while (argc > 0) { 67 if (strcmp(*argv, "dev") == 0) { 68 NEXT_ARG(); 69 if (d[0]) 70 duparg("dev", *argv); 71 strncpy(d, *argv, sizeof(d)-1); 72 } else if (strcmp(*argv, "handle") == 0) { 73 __u32 handle; 74 75 if (req.t.tcm_handle) 76 duparg("handle", *argv); 77 NEXT_ARG(); 78 if (get_qdisc_handle(&handle, *argv)) 79 invarg("invalid qdisc ID", *argv); 80 req.t.tcm_handle = handle; 81 } else if (strcmp(*argv, "root") == 0) { 82 if (req.t.tcm_parent) { 83 fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); 84 return -1; 85 } 86 req.t.tcm_parent = TC_H_ROOT; 87 } else if (strcmp(*argv, "clsact") == 0) { 88 if (req.t.tcm_parent) { 89 fprintf(stderr, "Error: \"clsact\" is a duplicate parent ID\n"); 90 return -1; 91 } 92 req.t.tcm_parent = TC_H_CLSACT; 93 strncpy(k, "clsact", sizeof(k) - 1); 94 q = get_qdisc_kind(k); 95 req.t.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); 96 NEXT_ARG_FWD(); 97 break; 98 } else if (strcmp(*argv, "ingress") == 0) { 99 if (req.t.tcm_parent) { 100 fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n"); 101 return -1; 102 } 103 req.t.tcm_parent = TC_H_INGRESS; 104 strncpy(k, "ingress", sizeof(k) - 1); 105 q = get_qdisc_kind(k); 106 req.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0); 107 NEXT_ARG_FWD(); 108 break; 109 } else if (strcmp(*argv, "parent") == 0) { 110 __u32 handle; 111 112 NEXT_ARG(); 113 if (req.t.tcm_parent) 114 duparg("parent", *argv); 115 if (get_tc_classid(&handle, *argv)) 116 invarg("invalid parent ID", *argv); 117 req.t.tcm_parent = handle; 118 } else if (matches(*argv, "estimator") == 0) { 119 if (parse_estimator(&argc, &argv, &est)) 120 return -1; 121 } else if (matches(*argv, "stab") == 0) { 122 if (parse_size_table(&argc, &argv, &stab.szopts) < 0) 123 return -1; 124 continue; 125 } else if (matches(*argv, "help") == 0) { 126 usage(); 127 } else { 128 strncpy(k, *argv, sizeof(k)-1); 129 130 q = get_qdisc_kind(k); 131 argc--; argv++; 132 break; 133 } 134 argc--; argv++; 135 } 136 137 if (k[0]) 138 addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); 139 if (est.ewma_log) 140 addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); 141 142 if (q) { 143 if (q->parse_qopt) { 144 if (q->parse_qopt(q, argc, argv, &req.n)) 145 return 1; 146 } else if (argc) { 147 fprintf(stderr, "qdisc '%s' does not support option parsing\n", k); 148 return -1; 149 } 150 } else { 151 if (argc) { 152 if (matches(*argv, "help") == 0) 153 usage(); 154 155 fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv); 156 return -1; 157 } 158 } 159 160 if (check_size_table_opts(&stab.szopts)) { 161 struct rtattr *tail; 162 163 if (tc_calc_size_table(&stab.szopts, &stab.data) < 0) { 164 fprintf(stderr, "failed to calculate size table.\n"); 165 return -1; 166 } 167 168 tail = NLMSG_TAIL(&req.n); 169 addattr_l(&req.n, sizeof(req), TCA_STAB, NULL, 0); 170 addattr_l(&req.n, sizeof(req), TCA_STAB_BASE, &stab.szopts, 171 sizeof(stab.szopts)); 172 if (stab.data) 173 addattr_l(&req.n, sizeof(req), TCA_STAB_DATA, stab.data, 174 stab.szopts.tsize * sizeof(__u16)); 175 tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail; 176 if (stab.data) 177 free(stab.data); 178 } 179 180 if (d[0]) { 181 int idx; 182 183 ll_init_map(&rth); 184 185 idx = ll_name_to_index(d); 186 if (idx == 0) { 187 fprintf(stderr, "Cannot find device \"%s\"\n", d); 188 return 1; 189 } 190 req.t.tcm_ifindex = idx; 191 } 192 193 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) 194 return 2; 195 196 return 0; 197 } 198 199 static int filter_ifindex; 200 201 int print_qdisc(const struct sockaddr_nl *who, 202 struct nlmsghdr *n, void *arg) 203 { 204 FILE *fp = (FILE *)arg; 205 struct tcmsg *t = NLMSG_DATA(n); 206 int len = n->nlmsg_len; 207 struct rtattr *tb[TCA_MAX+1]; 208 struct qdisc_util *q; 209 char abuf[256]; 210 211 if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) { 212 fprintf(stderr, "Not a qdisc\n"); 213 return 0; 214 } 215 len -= NLMSG_LENGTH(sizeof(*t)); 216 if (len < 0) { 217 fprintf(stderr, "Wrong len %d\n", len); 218 return -1; 219 } 220 221 if (filter_ifindex && filter_ifindex != t->tcm_ifindex) 222 return 0; 223 224 parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); 225 226 if (tb[TCA_KIND] == NULL) { 227 fprintf(stderr, "print_qdisc: NULL kind\n"); 228 return -1; 229 } 230 231 if (n->nlmsg_type == RTM_DELQDISC) 232 fprintf(fp, "deleted "); 233 234 if (n->nlmsg_type == RTM_NEWQDISC && 235 (n->nlmsg_flags & NLM_F_CREATE) && 236 (n->nlmsg_flags & NLM_F_REPLACE)) 237 fprintf(fp, "replaced "); 238 239 if (n->nlmsg_type == RTM_NEWQDISC && 240 (n->nlmsg_flags & NLM_F_CREATE) && 241 (n->nlmsg_flags & NLM_F_EXCL)) 242 fprintf(fp, "added "); 243 244 if (show_raw) 245 fprintf(fp, "qdisc %s %x:[%08x] ", 246 rta_getattr_str(tb[TCA_KIND]), 247 t->tcm_handle >> 16, t->tcm_handle); 248 else 249 fprintf(fp, "qdisc %s %x: ", rta_getattr_str(tb[TCA_KIND]), 250 t->tcm_handle >> 16); 251 252 if (filter_ifindex == 0) 253 fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); 254 255 if (t->tcm_parent == TC_H_ROOT) 256 fprintf(fp, "root "); 257 else if (t->tcm_parent) { 258 print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); 259 fprintf(fp, "parent %s ", abuf); 260 } 261 262 if (t->tcm_info != 1) 263 fprintf(fp, "refcnt %d ", t->tcm_info); 264 265 /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ 266 if (strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND])) == 0) 267 q = get_qdisc_kind("prio"); 268 else 269 q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); 270 271 if (tb[TCA_OPTIONS]) { 272 if (q) 273 q->print_qopt(q, fp, tb[TCA_OPTIONS]); 274 else 275 fprintf(fp, "[cannot parse qdisc parameters]"); 276 } 277 fprintf(fp, "\n"); 278 279 if (show_details && tb[TCA_STAB]) { 280 print_size_table(fp, " ", tb[TCA_STAB]); 281 fprintf(fp, "\n"); 282 } 283 284 if (show_stats) { 285 struct rtattr *xstats = NULL; 286 287 if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) { 288 print_tcstats_attr(fp, tb, " ", &xstats); 289 fprintf(fp, "\n"); 290 } 291 292 if (q && xstats && q->print_xstats) { 293 q->print_xstats(q, fp, xstats); 294 fprintf(fp, "\n"); 295 } 296 } 297 fflush(fp); 298 return 0; 299 } 300 301 static int tc_qdisc_list(int argc, char **argv) 302 { 303 struct tcmsg t = { .tcm_family = AF_UNSPEC }; 304 char d[16] = {}; 305 bool dump_invisible = false; 306 307 while (argc > 0) { 308 if (strcmp(*argv, "dev") == 0) { 309 NEXT_ARG(); 310 strncpy(d, *argv, sizeof(d)-1); 311 } else if (strcmp(*argv, "ingress") == 0 || 312 strcmp(*argv, "clsact") == 0) { 313 if (t.tcm_parent) { 314 fprintf(stderr, "Duplicate parent ID\n"); 315 usage(); 316 } 317 t.tcm_parent = TC_H_INGRESS; 318 } else if (matches(*argv, "help") == 0) { 319 usage(); 320 } else if (strcmp(*argv, "invisible") == 0) { 321 dump_invisible = true; 322 } else { 323 fprintf(stderr, "What is \"%s\"? Try \"tc qdisc help\".\n", *argv); 324 return -1; 325 } 326 327 argc--; argv++; 328 } 329 330 ll_init_map(&rth); 331 332 if (d[0]) { 333 t.tcm_ifindex = ll_name_to_index(d); 334 if (t.tcm_ifindex == 0) { 335 fprintf(stderr, "Cannot find device \"%s\"\n", d); 336 return 1; 337 } 338 filter_ifindex = t.tcm_ifindex; 339 } 340 341 if (dump_invisible) { 342 struct { 343 struct nlmsghdr n; 344 struct tcmsg t; 345 char buf[256]; 346 } req = { 347 .n.nlmsg_type = RTM_GETQDISC, 348 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 349 }; 350 351 req.t.tcm_family = AF_UNSPEC; 352 353 addattr(&req.n, 256, TCA_DUMP_INVISIBLE); 354 if (rtnl_dump_request_n(&rth, &req.n) < 0) { 355 perror("Cannot send dump request"); 356 return 1; 357 } 358 359 } else if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) { 360 perror("Cannot send dump request"); 361 return 1; 362 } 363 364 if (rtnl_dump_filter(&rth, print_qdisc, stdout) < 0) { 365 fprintf(stderr, "Dump terminated\n"); 366 return 1; 367 } 368 369 return 0; 370 } 371 372 int do_qdisc(int argc, char **argv) 373 { 374 if (argc < 1) 375 return tc_qdisc_list(0, NULL); 376 if (matches(*argv, "add") == 0) 377 return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); 378 if (matches(*argv, "change") == 0) 379 return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1); 380 if (matches(*argv, "replace") == 0) 381 return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); 382 if (matches(*argv, "link") == 0) 383 return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1); 384 if (matches(*argv, "delete") == 0) 385 return tc_qdisc_modify(RTM_DELQDISC, 0, argc-1, argv+1); 386 #if 0 387 if (matches(*argv, "get") == 0) 388 return tc_qdisc_get(RTM_GETQDISC, 0, argc-1, argv+1); 389 #endif 390 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 391 || matches(*argv, "lst") == 0) 392 return tc_qdisc_list(argc-1, argv+1); 393 if (matches(*argv, "help") == 0) { 394 usage(); 395 return 0; 396 } 397 fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv); 398 return -1; 399 } 400