1 /* 2 * tc.c "tc" utility frontend. 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 * Fixes: 12 * 13 * Petri Mattila <petri (at) prihateam.fi> 990308: wrong memset's resulted in faults 14 */ 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <unistd.h> 19 #include <syslog.h> 20 #include <fcntl.h> 21 #include <dlfcn.h> 22 #include <sys/socket.h> 23 #include <netinet/in.h> 24 #include <arpa/inet.h> 25 #include <string.h> 26 #include <errno.h> 27 28 #include "SNAPSHOT.h" 29 #include "utils.h" 30 #include "tc_util.h" 31 #include "tc_common.h" 32 #include "namespace.h" 33 34 int show_stats = 0; 35 int show_details = 0; 36 int show_raw = 0; 37 int show_pretty = 0; 38 int show_graph = 0; 39 int timestamp; 40 41 int batch_mode = 0; 42 int resolve_hosts = 0; 43 int use_iec = 0; 44 int force = 0; 45 bool use_names = false; 46 47 static char *conf_file; 48 49 struct rtnl_handle rth; 50 51 static void *BODY = NULL; /* cached handle dlopen(NULL) */ 52 static struct qdisc_util * qdisc_list; 53 static struct filter_util * filter_list; 54 55 #ifdef ANDROID 56 extern struct qdisc_util cbq_qdisc_util; 57 extern struct qdisc_util htb_qdisc_util; 58 extern struct qdisc_util ingress_qdisc_util; 59 extern struct filter_util u32_filter_util; 60 #endif 61 62 static int print_noqopt(struct qdisc_util *qu, FILE *f, 63 struct rtattr *opt) 64 { 65 if (opt && RTA_PAYLOAD(opt)) 66 fprintf(f, "[Unknown qdisc, optlen=%u] ", 67 (unsigned) RTA_PAYLOAD(opt)); 68 return 0; 69 } 70 71 static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) 72 { 73 if (argc) { 74 fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); 75 return -1; 76 } 77 return 0; 78 } 79 80 static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle) 81 { 82 if (opt && RTA_PAYLOAD(opt)) 83 fprintf(f, "fh %08x [Unknown filter, optlen=%u] ", 84 fhandle, (unsigned) RTA_PAYLOAD(opt)); 85 else if (fhandle) 86 fprintf(f, "fh %08x ", fhandle); 87 return 0; 88 } 89 90 static int parse_nofopt(struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n) 91 { 92 __u32 handle; 93 94 if (argc) { 95 fprintf(stderr, "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); 96 return -1; 97 } 98 if (fhandle) { 99 struct tcmsg *t = NLMSG_DATA(n); 100 if (get_u32(&handle, fhandle, 16)) { 101 fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle); 102 return -1; 103 } 104 t->tcm_handle = handle; 105 } 106 return 0; 107 } 108 109 struct qdisc_util *get_qdisc_kind(const char *str) 110 { 111 void *dlh; 112 char buf[256]; 113 struct qdisc_util *q; 114 115 #ifdef ANDROID 116 if (!strcmp(str, "cbq")) 117 return &cbq_qdisc_util; 118 else if (!strcmp(str, "htb")) 119 return &htb_qdisc_util; 120 else if (!strcmp(str, "ingress")) 121 return &ingress_qdisc_util; 122 else { 123 fprintf(stderr, "Android does not support qdisc '%s'\n", str); 124 return NULL; 125 } 126 #endif 127 for (q = qdisc_list; q; q = q->next) 128 if (strcmp(q->id, str) == 0) 129 return q; 130 131 snprintf(buf, sizeof(buf), "%s/q_%s.so", get_tc_lib(), str); 132 dlh = dlopen(buf, RTLD_LAZY); 133 if (!dlh) { 134 /* look in current binary, only open once */ 135 dlh = BODY; 136 if (dlh == NULL) { 137 dlh = BODY = dlopen(NULL, RTLD_LAZY); 138 if (dlh == NULL) 139 goto noexist; 140 } 141 } 142 143 snprintf(buf, sizeof(buf), "%s_qdisc_util", str); 144 q = dlsym(dlh, buf); 145 if (q == NULL) 146 goto noexist; 147 148 reg: 149 q->next = qdisc_list; 150 qdisc_list = q; 151 return q; 152 153 noexist: 154 q = malloc(sizeof(*q)); 155 if (q) { 156 157 memset(q, 0, sizeof(*q)); 158 q->id = strcpy(malloc(strlen(str)+1), str); 159 q->parse_qopt = parse_noqopt; 160 q->print_qopt = print_noqopt; 161 goto reg; 162 } 163 return q; 164 } 165 166 167 struct filter_util *get_filter_kind(const char *str) 168 { 169 void *dlh; 170 char buf[256]; 171 struct filter_util *q; 172 #ifdef ANDROID 173 if (!strcmp(str, "u32")) 174 return &u32_filter_util; 175 else { 176 fprintf(stderr, "Android does not support filter '%s'\n", str); 177 return NULL; 178 } 179 #endif 180 181 for (q = filter_list; q; q = q->next) 182 if (strcmp(q->id, str) == 0) 183 return q; 184 185 snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str); 186 dlh = dlopen(buf, RTLD_LAZY); 187 if (dlh == NULL) { 188 dlh = BODY; 189 if (dlh == NULL) { 190 dlh = BODY = dlopen(NULL, RTLD_LAZY); 191 if (dlh == NULL) 192 goto noexist; 193 } 194 } 195 196 snprintf(buf, sizeof(buf), "%s_filter_util", str); 197 q = dlsym(dlh, buf); 198 if (q == NULL) 199 goto noexist; 200 201 reg: 202 q->next = filter_list; 203 filter_list = q; 204 return q; 205 noexist: 206 q = malloc(sizeof(*q)); 207 if (q) { 208 memset(q, 0, sizeof(*q)); 209 strncpy(q->id, str, 15); 210 q->parse_fopt = parse_nofopt; 211 q->print_fopt = print_nofopt; 212 goto reg; 213 } 214 return q; 215 } 216 217 static void usage(void) 218 { 219 fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n" 220 #ifdef ANDROID 221 " tc [-force]\n" 222 #else 223 " tc [-force] -batch filename\n" 224 #endif 225 "where OBJECT := { qdisc | class | filter | action | monitor | exec }\n" 226 " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | " 227 "-n[etns] name |\n" 228 " -nm | -nam[es] | { -cf | -conf } path }\n"); 229 } 230 231 static int do_cmd(int argc, char **argv) 232 { 233 if (matches(*argv, "qdisc") == 0) 234 return do_qdisc(argc-1, argv+1); 235 if (matches(*argv, "class") == 0) 236 return do_class(argc-1, argv+1); 237 if (matches(*argv, "filter") == 0) 238 return do_filter(argc-1, argv+1); 239 if (matches(*argv, "actions") == 0) 240 return do_action(argc-1, argv+1); 241 if (matches(*argv, "monitor") == 0) 242 return do_tcmonitor(argc-1, argv+1); 243 if (matches(*argv, "exec") == 0) 244 return do_exec(argc-1, argv+1); 245 if (matches(*argv, "help") == 0) { 246 usage(); 247 return 0; 248 } 249 250 fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n", 251 *argv); 252 return -1; 253 } 254 255 #ifndef ANDROID 256 static int batch(const char *name) 257 { 258 char *line = NULL; 259 size_t len = 0; 260 int ret = 0; 261 262 batch_mode = 1; 263 if (name && strcmp(name, "-") != 0) { 264 if (freopen(name, "r", stdin) == NULL) { 265 fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n", 266 name, strerror(errno)); 267 return -1; 268 } 269 } 270 271 tc_core_init(); 272 273 if (rtnl_open(&rth, 0) < 0) { 274 fprintf(stderr, "Cannot open rtnetlink\n"); 275 return -1; 276 } 277 278 cmdlineno = 0; 279 while (getcmdline(&line, &len, stdin) != -1) { 280 char *largv[100]; 281 int largc; 282 283 largc = makeargs(line, largv, 100); 284 if (largc == 0) 285 continue; /* blank line */ 286 287 if (do_cmd(largc, largv)) { 288 fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno); 289 ret = 1; 290 if (!force) 291 break; 292 } 293 } 294 if (line) 295 free(line); 296 297 rtnl_close(&rth); 298 return ret; 299 } 300 #endif 301 302 303 int main(int argc, char **argv) 304 { 305 int ret; 306 #ifndef ANDROID 307 char *batch_file = NULL; 308 #endif 309 310 while (argc > 1) { 311 if (argv[1][0] != '-') 312 break; 313 if (matches(argv[1], "-stats") == 0 || 314 matches(argv[1], "-statistics") == 0) { 315 ++show_stats; 316 } else if (matches(argv[1], "-details") == 0) { 317 ++show_details; 318 } else if (matches(argv[1], "-raw") == 0) { 319 ++show_raw; 320 } else if (matches(argv[1], "-pretty") == 0) { 321 ++show_pretty; 322 } else if (matches(argv[1], "-graph") == 0) { 323 show_graph = 1; 324 } else if (matches(argv[1], "-Version") == 0) { 325 printf("tc utility, iproute2-ss%s\n", SNAPSHOT); 326 return 0; 327 } else if (matches(argv[1], "-iec") == 0) { 328 ++use_iec; 329 } else if (matches(argv[1], "-help") == 0) { 330 usage(); 331 return 0; 332 } else if (matches(argv[1], "-force") == 0) { 333 ++force; 334 #ifndef ANDROID 335 } else if (matches(argv[1], "-batch") == 0) { 336 argc--; argv++; 337 if (argc <= 1) 338 usage(); 339 batch_file = argv[1]; 340 #endif 341 } else if (matches(argv[1], "-netns") == 0) { 342 NEXT_ARG(); 343 if (netns_switch(argv[1])) 344 return -1; 345 } else if (matches(argv[1], "-names") == 0 || 346 matches(argv[1], "-nm") == 0) { 347 use_names = true; 348 } else if (matches(argv[1], "-cf") == 0 || 349 matches(argv[1], "-conf") == 0) { 350 NEXT_ARG(); 351 conf_file = argv[1]; 352 } else if (matches(argv[1], "-timestamp") == 0) { 353 timestamp++; 354 } else if (matches(argv[1], "-tshort") == 0) { 355 ++timestamp; 356 ++timestamp_short; 357 } else { 358 fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]); 359 return -1; 360 } 361 argc--; argv++; 362 } 363 364 #ifndef ANDROID 365 if (batch_file) 366 return batch(batch_file); 367 #endif 368 369 if (argc <= 1) { 370 usage(); 371 return 0; 372 } 373 374 tc_core_init(); 375 if (rtnl_open(&rth, 0) < 0) { 376 fprintf(stderr, "Cannot open rtnetlink\n"); 377 exit(1); 378 } 379 380 if (use_names && cls_names_init(conf_file)) { 381 ret = -1; 382 goto Exit; 383 } 384 385 ret = do_cmd(argc-1, argv+1); 386 Exit: 387 rtnl_close(&rth); 388 389 if (use_names) 390 cls_names_uninit(); 391 392 return ret; 393 } 394