tc_qdisc.c revision 302d3fb720d920ca15b7bfb979532cc0e5d7f20f
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@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 24#include "utils.h" 25#include "tc_util.h" 26#include "tc_common.h" 27 28static int usage(void); 29 30static int usage(void) 31{ 32 fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | get ] dev STRING\n"); 33 fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | parent CLASSID ]\n"); 34 fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); 35 fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); 36 fprintf(stderr, "\n"); 37 fprintf(stderr, " tc qdisc show [ dev STRING ] [ingress]\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 return -1; 42} 43 44int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv) 45{ 46 struct qdisc_util *q = NULL; 47 struct tc_estimator est; 48 char d[16]; 49 char k[16]; 50 struct { 51 struct nlmsghdr n; 52 struct tcmsg t; 53 char buf[TCA_BUF_MAX]; 54 } req; 55 56 memset(&req, 0, sizeof(req)); 57 memset(&est, 0, sizeof(est)); 58 memset(&d, 0, sizeof(d)); 59 memset(&k, 0, sizeof(k)); 60 61 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 62 req.n.nlmsg_flags = NLM_F_REQUEST|flags; 63 req.n.nlmsg_type = cmd; 64 req.t.tcm_family = AF_UNSPEC; 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 if (req.t.tcm_handle) 75 duparg("handle", *argv); 76 NEXT_ARG(); 77 if (get_qdisc_handle(&handle, *argv)) 78 invarg(*argv, "invalid qdisc ID"); 79 req.t.tcm_handle = handle; 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#ifdef TC_H_INGRESS 87 } else if (strcmp(*argv, "ingress") == 0) { 88 if (req.t.tcm_parent) { 89 fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n"); 90 return -1; 91 } 92 req.t.tcm_parent = TC_H_INGRESS; 93 strncpy(k, "ingress", sizeof(k)-1); 94 q = get_qdisc_kind(k); 95 req.t.tcm_handle = 0xffff0000; 96 97 argc--; argv++; 98 break; 99#endif 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(*argv, "invalid parent ID"); 107 req.t.tcm_parent = handle; 108 } else if (matches(*argv, "estimator") == 0) { 109 if (parse_estimator(&argc, &argv, &est)) 110 return -1; 111 } else if (matches(*argv, "help") == 0) { 112 usage(); 113 } else { 114 strncpy(k, *argv, sizeof(k)-1); 115 116 q = get_qdisc_kind(k); 117 argc--; argv++; 118 break; 119 } 120 argc--; argv++; 121 } 122 123 if (k[0]) 124 addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); 125 if (est.ewma_log) 126 addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); 127 128 if (q) { 129 if (!q->parse_qopt) { 130 fprintf(stderr, "qdisc '%s' does not support option parsing\n", k); 131 return -1; 132 } 133 if (q->parse_qopt(q, argc, argv, &req.n)) 134 return 1; 135 } else { 136 if (argc) { 137 if (matches(*argv, "help") == 0) 138 usage(); 139 140 fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv); 141 return -1; 142 } 143 } 144 145 if (d[0]) { 146 int idx; 147 148 ll_init_map(&rth); 149 150 if ((idx = ll_name_to_index(d)) == 0) { 151 fprintf(stderr, "Cannot find device \"%s\"\n", d); 152 return 1; 153 } 154 req.t.tcm_ifindex = idx; 155 } 156 157 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) 158 return 2; 159 160 return 0; 161} 162 163static int filter_ifindex; 164 165int print_qdisc(const struct sockaddr_nl *who, 166 struct nlmsghdr *n, 167 void *arg) 168{ 169 FILE *fp = (FILE*)arg; 170 struct tcmsg *t = NLMSG_DATA(n); 171 int len = n->nlmsg_len; 172 struct rtattr * tb[TCA_MAX+1]; 173 struct qdisc_util *q; 174 char abuf[256]; 175 176 if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) { 177 fprintf(stderr, "Not a qdisc\n"); 178 return 0; 179 } 180 len -= NLMSG_LENGTH(sizeof(*t)); 181 if (len < 0) { 182 fprintf(stderr, "Wrong len %d\n", len); 183 return -1; 184 } 185 186 if (filter_ifindex && filter_ifindex != t->tcm_ifindex) 187 return 0; 188 189 memset(tb, 0, sizeof(tb)); 190 parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); 191 192 if (tb[TCA_KIND] == NULL) { 193 fprintf(stderr, "print_qdisc: NULL kind\n"); 194 return -1; 195 } 196 197 if (n->nlmsg_type == RTM_DELQDISC) 198 fprintf(fp, "deleted "); 199 200 fprintf(fp, "qdisc %s %x: ", (char*)RTA_DATA(tb[TCA_KIND]), t->tcm_handle>>16); 201 if (filter_ifindex == 0) 202 fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); 203 if (t->tcm_parent == TC_H_ROOT) 204 fprintf(fp, "root "); 205 else if (t->tcm_parent) { 206 print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); 207 fprintf(fp, "parent %s ", abuf); 208 } 209 if (t->tcm_info != 1) { 210 fprintf(fp, "refcnt %d ", t->tcm_info); 211 } 212 /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ 213 214 if (0 == strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND]))) 215 q = get_qdisc_kind("prio"); 216 else 217 q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); 218 219 if (tb[TCA_OPTIONS]) { 220 if (q) 221 q->print_qopt(q, fp, tb[TCA_OPTIONS]); 222 else 223 fprintf(fp, "[cannot parse qdisc parameters]"); 224 } 225 fprintf(fp, "\n"); 226 if (show_stats) { 227 struct rtattr *xstats = NULL; 228 229 if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) { 230 print_tcstats_attr(fp, tb, " ", &xstats); 231 fprintf(fp, "\n"); 232 } 233 234 if (q && xstats && q->print_xstats) { 235 q->print_xstats(q, fp, xstats); 236 fprintf(fp, "\n"); 237 } 238 } 239 fflush(fp); 240 return 0; 241} 242 243 244int tc_qdisc_list(int argc, char **argv) 245{ 246 struct tcmsg t; 247 char d[16]; 248 249 memset(&t, 0, sizeof(t)); 250 t.tcm_family = AF_UNSPEC; 251 memset(&d, 0, sizeof(d)); 252 253 while (argc > 0) { 254 if (strcmp(*argv, "dev") == 0) { 255 NEXT_ARG(); 256 strncpy(d, *argv, sizeof(d)-1); 257#ifdef TC_H_INGRESS 258 } else if (strcmp(*argv, "ingress") == 0) { 259 if (t.tcm_parent) { 260 fprintf(stderr, "Duplicate parent ID\n"); 261 usage(); 262 } 263 t.tcm_parent = TC_H_INGRESS; 264#endif 265 } else if (matches(*argv, "help") == 0) { 266 usage(); 267 } else { 268 fprintf(stderr, "What is \"%s\"? Try \"tc qdisc help\".\n", *argv); 269 return -1; 270 } 271 272 argc--; argv++; 273 } 274 275 ll_init_map(&rth); 276 277 if (d[0]) { 278 if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { 279 fprintf(stderr, "Cannot find device \"%s\"\n", d); 280 return 1; 281 } 282 filter_ifindex = t.tcm_ifindex; 283 } 284 285 if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) { 286 perror("Cannot send dump request"); 287 return 1; 288 } 289 290 if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) { 291 fprintf(stderr, "Dump terminated\n"); 292 return 1; 293 } 294 295 return 0; 296} 297 298int do_qdisc(int argc, char **argv) 299{ 300 if (argc < 1) 301 return tc_qdisc_list(0, NULL); 302 if (matches(*argv, "add") == 0) 303 return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); 304 if (matches(*argv, "change") == 0) 305 return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1); 306 if (matches(*argv, "replace") == 0) 307 return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); 308 if (matches(*argv, "link") == 0) 309 return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1); 310 if (matches(*argv, "delete") == 0) 311 return tc_qdisc_modify(RTM_DELQDISC, 0, argc-1, argv+1); 312#if 0 313 if (matches(*argv, "get") == 0) 314 return tc_qdisc_get(RTM_GETQDISC, 0, argc-1, argv+1); 315#endif 316 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 317 || matches(*argv, "lst") == 0) 318 return tc_qdisc_list(argc-1, argv+1); 319 if (matches(*argv, "help") == 0) 320 usage(); 321 fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv); 322 return -1; 323} 324