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