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