tc_class.c revision d81b135b6f659900c0a51ca1a733c165e49aaaec
1/* 2 * tc_class.c "tc class". 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 <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 void usage(void) __attribute__((noreturn)); 29 30static void usage(void) 31{ 32 fprintf(stderr, "Usage: tc class [ add | del | change | get ] dev STRING\n"); 33 fprintf(stderr, " [ classid CLASSID ] [ root | parent CLASSID ]\n"); 34 fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); 35 fprintf(stderr, "\n"); 36 fprintf(stderr, " tc class show [ dev STRING ] [ root | parent CLASSID ]\n"); 37 fprintf(stderr, "Where:\n"); 38 fprintf(stderr, "QDISC_KIND := { prio | cbq | etc. }\n"); 39 fprintf(stderr, "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n"); 40 exit(-1); 41} 42 43int tc_class_modify(int cmd, unsigned flags, int argc, char **argv) 44{ 45 struct rtnl_handle rth; 46 struct { 47 struct nlmsghdr n; 48 struct tcmsg t; 49 char buf[4096]; 50 } req; 51 struct qdisc_util *q = NULL; 52 struct tc_estimator est; 53 char d[16]; 54 char k[16]; 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, "classid") == 0) { 73 __u32 handle; 74 NEXT_ARG(); 75 if (req.t.tcm_handle) 76 duparg("classid", *argv); 77 if (get_tc_classid(&handle, *argv)) 78 invarg(*argv, "invalid class 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 exit(-1); 84 } 85 req.t.tcm_parent = TC_H_ROOT; 86 } else if (strcmp(*argv, "parent") == 0) { 87 __u32 handle; 88 NEXT_ARG(); 89 if (req.t.tcm_parent) 90 duparg("parent", *argv); 91 if (get_tc_classid(&handle, *argv)) 92 invarg(*argv, "invalid parent ID"); 93 req.t.tcm_parent = handle; 94 } else if (matches(*argv, "estimator") == 0) { 95 if (parse_estimator(&argc, &argv, &est)) 96 return -1; 97 } else if (matches(*argv, "help") == 0) { 98 usage(); 99 } else { 100 strncpy(k, *argv, sizeof(k)-1); 101 102 q = get_qdisc_kind(k); 103 argc--; argv++; 104 break; 105 } 106 argc--; argv++; 107 } 108 109 if (k[0]) 110 addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); 111 if (est.ewma_log) 112 addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); 113 114 if (q) { 115 if (q->parse_copt == NULL) { 116 fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k); 117 exit(1); 118 } 119 if (q->parse_copt(q, argc, argv, &req.n)) 120 exit(1); 121 } else { 122 if (argc) { 123 if (matches(*argv, "help") == 0) 124 usage(); 125 fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv); 126 exit(-1); 127 } 128 } 129 130 if (rtnl_open(&rth, 0) < 0) { 131 fprintf(stderr, "Cannot open rtnetlink\n"); 132 exit(1); 133 } 134 135 if (d[0]) { 136 ll_init_map(&rth); 137 138 if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { 139 fprintf(stderr, "Cannot find device \"%s\"\n", d); 140 exit(1); 141 } 142 } 143 144 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) 145 exit(2); 146 147 rtnl_close(&rth); 148 return 0; 149} 150 151void print_class_tcstats(FILE *fp, struct tc_stats *st) 152{ 153 SPRINT_BUF(b1); 154 155 fprintf(fp, " Sent %llu bytes %u pkts (dropped %u, overlimits %u) ", 156 (unsigned long long)st->bytes, st->packets, st->drops, st->overlimits); 157 if (st->bps || st->pps || st->qlen || st->backlog) { 158 fprintf(fp, "\n "); 159 if (st->bps || st->pps) { 160 fprintf(fp, "rate "); 161 if (st->bps) 162 fprintf(fp, "%s ", sprint_rate(st->bps, b1)); 163 if (st->pps) 164 fprintf(fp, "%upps ", st->pps); 165 } 166 if (st->qlen || st->backlog) { 167 fprintf(fp, "backlog "); 168 if (st->backlog) 169 fprintf(fp, "%s ", sprint_size(st->backlog, b1)); 170 if (st->qlen) 171 fprintf(fp, "%up ", st->qlen); 172 } 173 } 174} 175 176int filter_ifindex; 177__u32 filter_qdisc; 178 179int print_class(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) 180{ 181 FILE *fp = (FILE*)arg; 182 struct tcmsg *t = NLMSG_DATA(n); 183 int len = n->nlmsg_len; 184 struct rtattr * tb[TCA_MAX+1]; 185 struct qdisc_util *q; 186 char abuf[256]; 187 188 if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) { 189 fprintf(stderr, "Not a class\n"); 190 return 0; 191 } 192 len -= NLMSG_LENGTH(sizeof(*t)); 193 if (len < 0) { 194 fprintf(stderr, "Wrong len %d\n", len); 195 return -1; 196 } 197 if (filter_qdisc && TC_H_MAJ(t->tcm_handle^filter_qdisc)) 198 return 0; 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_class: NULL kind\n"); 205 return -1; 206 } 207 208 if (n->nlmsg_type == RTM_DELTCLASS) 209 fprintf(fp, "deleted "); 210 211 abuf[0] = 0; 212 if (t->tcm_handle) { 213 if (filter_qdisc) 214 print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_handle)); 215 else 216 print_tc_classid(abuf, sizeof(abuf), t->tcm_handle); 217 } 218 fprintf(fp, "class %s %s ", (char*)RTA_DATA(tb[TCA_KIND]), abuf); 219 220 if (filter_ifindex == 0) 221 fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); 222 223 if (t->tcm_parent == TC_H_ROOT) 224 fprintf(fp, "root "); 225 else { 226 if (filter_qdisc) 227 print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_parent)); 228 else 229 print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); 230 fprintf(fp, "parent %s ", abuf); 231 } 232 if (t->tcm_info) 233 fprintf(fp, "leaf %x: ", t->tcm_info>>16); 234 q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); 235 if (tb[TCA_OPTIONS]) { 236 if (q && q->print_copt) 237 q->print_copt(q, fp, tb[TCA_OPTIONS]); 238 else 239 fprintf(fp, "[cannot parse class parameters]"); 240 } 241 fprintf(fp, "\n"); 242 if (show_stats) { 243 if (tb[TCA_STATS]) { 244 if (RTA_PAYLOAD(tb[TCA_STATS]) < sizeof(struct tc_stats)) 245 fprintf(fp, "statistics truncated"); 246 else { 247 struct tc_stats st; 248 memcpy(&st, RTA_DATA(tb[TCA_STATS]), sizeof(st)); 249 print_class_tcstats(fp, &st); 250 fprintf(fp, "\n"); 251 } 252 } 253 if (q && tb[TCA_XSTATS]) { 254 q->print_xstats(q, fp, tb[TCA_XSTATS]); 255 fprintf(fp, "\n"); 256 } 257 } 258 fflush(fp); 259 return 0; 260} 261 262 263int tc_class_list(int argc, char **argv) 264{ 265 struct tcmsg t; 266 struct rtnl_handle rth; 267 char d[16]; 268 269 memset(&t, 0, sizeof(t)); 270 t.tcm_family = AF_UNSPEC; 271 memset(d, 0, sizeof(d)); 272 273 while (argc > 0) { 274 if (strcmp(*argv, "dev") == 0) { 275 NEXT_ARG(); 276 if (d[0]) 277 duparg("dev", *argv); 278 strncpy(d, *argv, sizeof(d)-1); 279 } else if (strcmp(*argv, "qdisc") == 0) { 280 NEXT_ARG(); 281 if (filter_qdisc) 282 duparg("qdisc", *argv); 283 if (get_qdisc_handle(&filter_qdisc, *argv)) 284 invarg(*argv, "invalid qdisc ID"); 285 } else if (strcmp(*argv, "root") == 0) { 286 if (t.tcm_parent) { 287 fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); 288 exit(-1); 289 } 290 t.tcm_parent = TC_H_ROOT; 291 } else if (strcmp(*argv, "parent") == 0) { 292 __u32 handle; 293 if (t.tcm_parent) 294 duparg("parent", *argv); 295 NEXT_ARG(); 296 if (get_tc_classid(&handle, *argv)) 297 invarg(*argv, "invalid parent ID"); 298 t.tcm_parent = handle; 299 } else if (matches(*argv, "help") == 0) { 300 usage(); 301 } else { 302 fprintf(stderr, "What is \"%s\"? Try \"tc class help\".\n", *argv); 303 exit(-1); 304 } 305 306 argc--; argv++; 307 } 308 309 if (rtnl_open(&rth, 0) < 0) { 310 fprintf(stderr, "Cannot open rtnetlink\n"); 311 exit(1); 312 } 313 314 ll_init_map(&rth); 315 316 if (d[0]) { 317 if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { 318 fprintf(stderr, "Cannot find device \"%s\"\n", d); 319 exit(1); 320 } 321 filter_ifindex = t.tcm_ifindex; 322 } 323 324 if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) { 325 perror("Cannot send dump request"); 326 rtnl_close(&rth); 327 exit(1); 328 } 329 330 if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) { 331 fprintf(stderr, "Dump terminated\n"); 332 rtnl_close(&rth); 333 exit(1); 334 } 335 336 rtnl_close(&rth); 337 return 0; 338} 339 340int do_class(int argc, char **argv) 341{ 342 if (argc < 1) 343 return tc_class_list(0, NULL); 344 if (matches(*argv, "add") == 0) 345 return tc_class_modify(RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); 346 if (matches(*argv, "change") == 0) 347 return tc_class_modify(RTM_NEWTCLASS, 0, argc-1, argv+1); 348 if (matches(*argv, "replace") == 0) 349 return tc_class_modify(RTM_NEWTCLASS, NLM_F_CREATE, argc-1, argv+1); 350 if (matches(*argv, "delete") == 0) 351 return tc_class_modify(RTM_DELTCLASS, 0, argc-1, argv+1); 352#if 0 353 if (matches(*argv, "get") == 0) 354 return tc_class_get(RTM_GETTCLASS, 0, argc-1, argv+1); 355#endif 356 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 357 || matches(*argv, "lst") == 0) 358 return tc_class_list(argc-1, argv+1); 359 if (matches(*argv, "help") == 0) 360 usage(); 361 fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv); 362 return -1; 363} 364