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