• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * tc_qdisc.c		"tc qdisc".
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  *		J Hadi Salim: Extension to ingress
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <syslog.h>
17 #include <fcntl.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <string.h>
22 #include <math.h>
23 #include <malloc.h>
24 
25 #include "utils.h"
26 #include "tc_util.h"
27 #include "tc_common.h"
28 
usage(void)29 static int usage(void)
30 {
31 	fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | show ] dev STRING\n");
32 	fprintf(stderr, "       [ handle QHANDLE ] [ root | ingress | clsact | parent CLASSID ]\n");
33 	fprintf(stderr, "       [ estimator INTERVAL TIME_CONSTANT ]\n");
34 	fprintf(stderr, "       [ stab [ help | STAB_OPTIONS] ]\n");
35 	fprintf(stderr, "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n");
36 	fprintf(stderr, "\n");
37 	fprintf(stderr, "       tc qdisc show [ dev STRING ] [ ingress | clsact ] [ invisible ]\n");
38 	fprintf(stderr, "Where:\n");
39 	fprintf(stderr, "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n");
40 	fprintf(stderr, "OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help\n");
41 	fprintf(stderr, "STAB_OPTIONS := ... try tc qdisc add stab help\n");
42 	return -1;
43 }
44 
tc_qdisc_modify(int cmd,unsigned int flags,int argc,char ** argv)45 static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv)
46 {
47 	struct qdisc_util *q = NULL;
48 	struct tc_estimator est = {};
49 	struct {
50 		struct tc_sizespec	szopts;
51 		__u16			*data;
52 	} stab = {};
53 	char  d[16] = {};
54 	char  k[16] = {};
55 	struct {
56 		struct nlmsghdr	n;
57 		struct tcmsg		t;
58 		char			buf[TCA_BUF_MAX];
59 	} req = {
60 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
61 		.n.nlmsg_flags = NLM_F_REQUEST | flags,
62 		.n.nlmsg_type = cmd,
63 		.t.tcm_family = AF_UNSPEC,
64 	};
65 
66 	while (argc > 0) {
67 		if (strcmp(*argv, "dev") == 0) {
68 			NEXT_ARG();
69 			if (d[0])
70 				duparg("dev", *argv);
71 			strncpy(d, *argv, sizeof(d)-1);
72 		} else if (strcmp(*argv, "handle") == 0) {
73 			__u32 handle;
74 
75 			if (req.t.tcm_handle)
76 				duparg("handle", *argv);
77 			NEXT_ARG();
78 			if (get_qdisc_handle(&handle, *argv))
79 				invarg("invalid qdisc ID", *argv);
80 			req.t.tcm_handle = handle;
81 		} else if (strcmp(*argv, "root") == 0) {
82 			if (req.t.tcm_parent) {
83 				fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
84 				return -1;
85 			}
86 			req.t.tcm_parent = TC_H_ROOT;
87 		} else if (strcmp(*argv, "clsact") == 0) {
88 			if (req.t.tcm_parent) {
89 				fprintf(stderr, "Error: \"clsact\" is a duplicate parent ID\n");
90 				return -1;
91 			}
92 			req.t.tcm_parent = TC_H_CLSACT;
93 			strncpy(k, "clsact", sizeof(k) - 1);
94 			q = get_qdisc_kind(k);
95 			req.t.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
96 			NEXT_ARG_FWD();
97 			break;
98 		} else if (strcmp(*argv, "ingress") == 0) {
99 			if (req.t.tcm_parent) {
100 				fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n");
101 				return -1;
102 			}
103 			req.t.tcm_parent = TC_H_INGRESS;
104 			strncpy(k, "ingress", sizeof(k) - 1);
105 			q = get_qdisc_kind(k);
106 			req.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
107 			NEXT_ARG_FWD();
108 			break;
109 		} else if (strcmp(*argv, "parent") == 0) {
110 			__u32 handle;
111 
112 			NEXT_ARG();
113 			if (req.t.tcm_parent)
114 				duparg("parent", *argv);
115 			if (get_tc_classid(&handle, *argv))
116 				invarg("invalid parent ID", *argv);
117 			req.t.tcm_parent = handle;
118 		} else if (matches(*argv, "estimator") == 0) {
119 			if (parse_estimator(&argc, &argv, &est))
120 				return -1;
121 		} else if (matches(*argv, "stab") == 0) {
122 			if (parse_size_table(&argc, &argv, &stab.szopts) < 0)
123 				return -1;
124 			continue;
125 		} else if (matches(*argv, "help") == 0) {
126 			usage();
127 		} else {
128 			strncpy(k, *argv, sizeof(k)-1);
129 
130 			q = get_qdisc_kind(k);
131 			argc--; argv++;
132 			break;
133 		}
134 		argc--; argv++;
135 	}
136 
137 	if (k[0])
138 		addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
139 	if (est.ewma_log)
140 		addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
141 
142 	if (q) {
143 		if (q->parse_qopt) {
144 			if (q->parse_qopt(q, argc, argv, &req.n))
145 				return 1;
146 		} else if (argc) {
147 			fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
148 			return -1;
149 		}
150 	} else {
151 		if (argc) {
152 			if (matches(*argv, "help") == 0)
153 				usage();
154 
155 			fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv);
156 			return -1;
157 		}
158 	}
159 
160 	if (check_size_table_opts(&stab.szopts)) {
161 		struct rtattr *tail;
162 
163 		if (tc_calc_size_table(&stab.szopts, &stab.data) < 0) {
164 			fprintf(stderr, "failed to calculate size table.\n");
165 			return -1;
166 		}
167 
168 		tail = NLMSG_TAIL(&req.n);
169 		addattr_l(&req.n, sizeof(req), TCA_STAB, NULL, 0);
170 		addattr_l(&req.n, sizeof(req), TCA_STAB_BASE, &stab.szopts,
171 			  sizeof(stab.szopts));
172 		if (stab.data)
173 			addattr_l(&req.n, sizeof(req), TCA_STAB_DATA, stab.data,
174 				  stab.szopts.tsize * sizeof(__u16));
175 		tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
176 		if (stab.data)
177 			free(stab.data);
178 	}
179 
180 	if (d[0])  {
181 		int idx;
182 
183 		ll_init_map(&rth);
184 
185 		idx = ll_name_to_index(d);
186 		if (idx == 0) {
187 			fprintf(stderr, "Cannot find device \"%s\"\n", d);
188 			return 1;
189 		}
190 		req.t.tcm_ifindex = idx;
191 	}
192 
193 	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
194 		return 2;
195 
196 	return 0;
197 }
198 
199 static int filter_ifindex;
200 
print_qdisc(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)201 int print_qdisc(const struct sockaddr_nl *who,
202 		struct nlmsghdr *n, void *arg)
203 {
204 	FILE *fp = (FILE *)arg;
205 	struct tcmsg *t = NLMSG_DATA(n);
206 	int len = n->nlmsg_len;
207 	struct rtattr *tb[TCA_MAX+1];
208 	struct qdisc_util *q;
209 	char abuf[256];
210 
211 	if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) {
212 		fprintf(stderr, "Not a qdisc\n");
213 		return 0;
214 	}
215 	len -= NLMSG_LENGTH(sizeof(*t));
216 	if (len < 0) {
217 		fprintf(stderr, "Wrong len %d\n", len);
218 		return -1;
219 	}
220 
221 	if (filter_ifindex && filter_ifindex != t->tcm_ifindex)
222 		return 0;
223 
224 	parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
225 
226 	if (tb[TCA_KIND] == NULL) {
227 		fprintf(stderr, "print_qdisc: NULL kind\n");
228 		return -1;
229 	}
230 
231 	if (n->nlmsg_type == RTM_DELQDISC)
232 		fprintf(fp, "deleted ");
233 
234 	if (n->nlmsg_type == RTM_NEWQDISC &&
235 			(n->nlmsg_flags & NLM_F_CREATE) &&
236 			(n->nlmsg_flags & NLM_F_REPLACE))
237 		fprintf(fp, "replaced ");
238 
239 	if (n->nlmsg_type == RTM_NEWQDISC &&
240 			(n->nlmsg_flags & NLM_F_CREATE) &&
241 			(n->nlmsg_flags & NLM_F_EXCL))
242 		fprintf(fp, "added ");
243 
244 	if (show_raw)
245 		fprintf(fp, "qdisc %s %x:[%08x]  ",
246 			rta_getattr_str(tb[TCA_KIND]),
247 			t->tcm_handle >> 16, t->tcm_handle);
248 	else
249 		fprintf(fp, "qdisc %s %x: ", rta_getattr_str(tb[TCA_KIND]),
250 			t->tcm_handle >> 16);
251 
252 	if (filter_ifindex == 0)
253 		fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex));
254 
255 	if (t->tcm_parent == TC_H_ROOT)
256 		fprintf(fp, "root ");
257 	else if (t->tcm_parent) {
258 		print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
259 		fprintf(fp, "parent %s ", abuf);
260 	}
261 
262 	if (t->tcm_info != 1)
263 		fprintf(fp, "refcnt %d ", t->tcm_info);
264 
265 	/* pfifo_fast is generic enough to warrant the hardcoding --JHS */
266 	if (strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND])) == 0)
267 		q = get_qdisc_kind("prio");
268 	else
269 		q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
270 
271 	if (tb[TCA_OPTIONS]) {
272 		if (q)
273 			q->print_qopt(q, fp, tb[TCA_OPTIONS]);
274 		else
275 			fprintf(fp, "[cannot parse qdisc parameters]");
276 	}
277 	fprintf(fp, "\n");
278 
279 	if (show_details && tb[TCA_STAB]) {
280 		print_size_table(fp, " ", tb[TCA_STAB]);
281 		fprintf(fp, "\n");
282 	}
283 
284 	if (show_stats) {
285 		struct rtattr *xstats = NULL;
286 
287 		if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) {
288 			print_tcstats_attr(fp, tb, " ", &xstats);
289 			fprintf(fp, "\n");
290 		}
291 
292 		if (q && xstats && q->print_xstats) {
293 			q->print_xstats(q, fp, xstats);
294 			fprintf(fp, "\n");
295 		}
296 	}
297 	fflush(fp);
298 	return 0;
299 }
300 
tc_qdisc_list(int argc,char ** argv)301 static int tc_qdisc_list(int argc, char **argv)
302 {
303 	struct tcmsg t = { .tcm_family = AF_UNSPEC };
304 	char d[16] = {};
305 	bool dump_invisible = false;
306 
307 	while (argc > 0) {
308 		if (strcmp(*argv, "dev") == 0) {
309 			NEXT_ARG();
310 			strncpy(d, *argv, sizeof(d)-1);
311 		} else if (strcmp(*argv, "ingress") == 0 ||
312 			   strcmp(*argv, "clsact") == 0) {
313 			if (t.tcm_parent) {
314 				fprintf(stderr, "Duplicate parent ID\n");
315 				usage();
316 			}
317 			t.tcm_parent = TC_H_INGRESS;
318 		} else if (matches(*argv, "help") == 0) {
319 			usage();
320 		} else if (strcmp(*argv, "invisible") == 0) {
321 			dump_invisible = true;
322 		} else {
323 			fprintf(stderr, "What is \"%s\"? Try \"tc qdisc help\".\n", *argv);
324 			return -1;
325 		}
326 
327 		argc--; argv++;
328 	}
329 
330 	ll_init_map(&rth);
331 
332 	if (d[0]) {
333 		t.tcm_ifindex = ll_name_to_index(d);
334 		if (t.tcm_ifindex == 0) {
335 			fprintf(stderr, "Cannot find device \"%s\"\n", d);
336 			return 1;
337 		}
338 		filter_ifindex = t.tcm_ifindex;
339 	}
340 
341 	if (dump_invisible) {
342 		struct {
343 			struct nlmsghdr n;
344 			struct tcmsg t;
345 			char buf[256];
346 		} req = {
347 			.n.nlmsg_type = RTM_GETQDISC,
348 			.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
349 		};
350 
351 		req.t.tcm_family = AF_UNSPEC;
352 
353 		addattr(&req.n, 256, TCA_DUMP_INVISIBLE);
354 		if (rtnl_dump_request_n(&rth, &req.n) < 0) {
355 			perror("Cannot send dump request");
356 			return 1;
357 		}
358 
359 	} else if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) {
360 		perror("Cannot send dump request");
361 		return 1;
362 	}
363 
364 	if (rtnl_dump_filter(&rth, print_qdisc, stdout) < 0) {
365 		fprintf(stderr, "Dump terminated\n");
366 		return 1;
367 	}
368 
369 	return 0;
370 }
371 
do_qdisc(int argc,char ** argv)372 int do_qdisc(int argc, char **argv)
373 {
374 	if (argc < 1)
375 		return tc_qdisc_list(0, NULL);
376 	if (matches(*argv, "add") == 0)
377 		return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
378 	if (matches(*argv, "change") == 0)
379 		return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1);
380 	if (matches(*argv, "replace") == 0)
381 		return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
382 	if (matches(*argv, "link") == 0)
383 		return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1);
384 	if (matches(*argv, "delete") == 0)
385 		return tc_qdisc_modify(RTM_DELQDISC, 0,  argc-1, argv+1);
386 #if 0
387 	if (matches(*argv, "get") == 0)
388 		return tc_qdisc_get(RTM_GETQDISC, 0,  argc-1, argv+1);
389 #endif
390 	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
391 	    || matches(*argv, "lst") == 0)
392 		return tc_qdisc_list(argc-1, argv+1);
393 	if (matches(*argv, "help") == 0) {
394 		usage();
395 		return 0;
396 	}
397 	fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv);
398 	return -1;
399 }
400