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