Home | History | Annotate | Download | only in tc
      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