blob: d0ddb939d4737430104fc1efeafa27f9debbb0f6 [file] [log] [blame]
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +00001/*
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"
Vadim Kochan67e1d732014-12-24 23:04:11 +020032#include "namespace.h"
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000033
Stephen Hemminger32a121c2016-03-21 11:48:36 -070034int show_stats;
35int show_details;
36int show_raw;
37int show_pretty;
38int show_graph;
Eric Dumazet32a6fbe2015-09-23 16:40:04 -070039int timestamp;
Stephen Hemminger44dcfe82008-05-09 15:42:34 -070040
Stephen Hemminger32a121c2016-03-21 11:48:36 -070041int batch_mode;
42int resolve_hosts;
43int use_iec;
44int force;
45bool use_names;
Vadim Kochan4612d042015-03-03 18:41:18 +020046
47static char *conf_file;
48
osdl.net!shemminger79016602005-03-14 19:34:12 +000049struct rtnl_handle rth;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000050
Stephen Hemminger32a121c2016-03-21 11:48:36 -070051static void *BODY; /* cached handle dlopen(NULL) */
52static struct qdisc_util *qdisc_list;
53static struct filter_util *filter_list;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000054
Stephen Hemmingerae665a52006-12-05 10:10:22 -080055static int print_noqopt(struct qdisc_util *qu, FILE *f,
osdl.net!shemminger1798c9d2004-08-31 17:45:21 +000056 struct rtattr *opt)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000057{
58 if (opt && RTA_PAYLOAD(opt))
Stephen Hemmingerae665a52006-12-05 10:10:22 -080059 fprintf(f, "[Unknown qdisc, optlen=%u] ",
Stephen Hemminger32a121c2016-03-21 11:48:36 -070060 (unsigned int) RTA_PAYLOAD(opt));
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000061 return 0;
62}
63
64static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
65{
66 if (argc) {
67 fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
68 return -1;
69 }
70 return 0;
71}
72
73static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle)
74{
75 if (opt && RTA_PAYLOAD(opt))
Stephen Hemmingerae665a52006-12-05 10:10:22 -080076 fprintf(f, "fh %08x [Unknown filter, optlen=%u] ",
Stephen Hemminger32a121c2016-03-21 11:48:36 -070077 fhandle, (unsigned int) RTA_PAYLOAD(opt));
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000078 else if (fhandle)
79 fprintf(f, "fh %08x ", fhandle);
80 return 0;
81}
82
83static int parse_nofopt(struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n)
84{
85 __u32 handle;
86
87 if (argc) {
88 fprintf(stderr, "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
89 return -1;
90 }
91 if (fhandle) {
92 struct tcmsg *t = NLMSG_DATA(n);
Stephen Hemminger32a121c2016-03-21 11:48:36 -070093
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000094 if (get_u32(&handle, fhandle, 16)) {
95 fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle);
96 return -1;
97 }
98 t->tcm_handle = handle;
99 }
100 return 0;
101}
102
osdl.org!shemminger4094db72004-06-02 20:22:08 +0000103struct qdisc_util *get_qdisc_kind(const char *str)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000104{
105 void *dlh;
106 char buf[256];
107 struct qdisc_util *q;
108
109 for (q = qdisc_list; q; q = q->next)
110 if (strcmp(q->id, str) == 0)
111 return q;
112
Stephen Hemmingeraa27f882007-06-20 15:27:22 -0700113 snprintf(buf, sizeof(buf), "%s/q_%s.so", get_tc_lib(), str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000114 dlh = dlopen(buf, RTLD_LAZY);
osdl.net!shemmingerb7a45152004-07-02 17:47:53 +0000115 if (!dlh) {
116 /* look in current binary, only open once */
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000117 dlh = BODY;
118 if (dlh == NULL) {
119 dlh = BODY = dlopen(NULL, RTLD_LAZY);
120 if (dlh == NULL)
121 goto noexist;
122 }
123 }
124
net[shemminger]!kaber95812b52004-09-28 18:35:49 +0000125 snprintf(buf, sizeof(buf), "%s_qdisc_util", str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000126 q = dlsym(dlh, buf);
127 if (q == NULL)
128 goto noexist;
129
130reg:
131 q->next = qdisc_list;
132 qdisc_list = q;
133 return q;
134
135noexist:
136 q = malloc(sizeof(*q));
137 if (q) {
osdl.net!shemminger1798c9d2004-08-31 17:45:21 +0000138
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000139 memset(q, 0, sizeof(*q));
osdl.net!shemminger1798c9d2004-08-31 17:45:21 +0000140 q->id = strcpy(malloc(strlen(str)+1), str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000141 q->parse_qopt = parse_noqopt;
142 q->print_qopt = print_noqopt;
143 goto reg;
144 }
145 return q;
146}
147
148
osdl.org!shemminger4094db72004-06-02 20:22:08 +0000149struct filter_util *get_filter_kind(const char *str)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000150{
151 void *dlh;
152 char buf[256];
153 struct filter_util *q;
154
155 for (q = filter_list; q; q = q->next)
156 if (strcmp(q->id, str) == 0)
157 return q;
158
Stephen Hemmingeraa27f882007-06-20 15:27:22 -0700159 snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000160 dlh = dlopen(buf, RTLD_LAZY);
161 if (dlh == NULL) {
162 dlh = BODY;
163 if (dlh == NULL) {
164 dlh = BODY = dlopen(NULL, RTLD_LAZY);
165 if (dlh == NULL)
166 goto noexist;
167 }
168 }
169
net[shemminger]!kaber95812b52004-09-28 18:35:49 +0000170 snprintf(buf, sizeof(buf), "%s_filter_util", str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000171 q = dlsym(dlh, buf);
172 if (q == NULL)
173 goto noexist;
174
175reg:
176 q->next = filter_list;
177 filter_list = q;
178 return q;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000179noexist:
180 q = malloc(sizeof(*q));
181 if (q) {
182 memset(q, 0, sizeof(*q));
183 strncpy(q->id, str, 15);
184 q->parse_fopt = parse_nofopt;
185 q->print_fopt = print_nofopt;
186 goto reg;
187 }
188 return q;
189}
190
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000191static void usage(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000192{
193 fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
Petr Jediný10494d22008-08-07 16:45:33 +0200194 " tc [-force] -batch filename\n"
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700195 "where OBJECT := { qdisc | class | filter | action | monitor | exec }\n"
196 " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | -n[etns] name |\n"
Vadim Kochan4612d042015-03-03 18:41:18 +0200197 " -nm | -nam[es] | { -cf | -conf } path }\n");
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000198}
199
200static int do_cmd(int argc, char **argv)
201{
202 if (matches(*argv, "qdisc") == 0)
203 return do_qdisc(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000204 if (matches(*argv, "class") == 0)
205 return do_class(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000206 if (matches(*argv, "filter") == 0)
207 return do_filter(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000208 if (matches(*argv, "actions") == 0)
209 return do_action(argc-1, argv+1);
Jamal Hadi Salim5bec3482006-08-08 11:55:15 -0700210 if (matches(*argv, "monitor") == 0)
211 return do_tcmonitor(argc-1, argv+1);
Daniel Borkmann4bd62442015-04-16 21:20:06 +0200212 if (matches(*argv, "exec") == 0)
213 return do_exec(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000214 if (matches(*argv, "help") == 0) {
215 usage();
216 return 0;
217 }
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800218
219 fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n",
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000220 *argv);
osdl.net!shemminger044ebf32005-03-14 19:02:41 +0000221 return -1;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000222}
223
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000224static int batch(const char *name)
225{
226 char *line = NULL;
227 size_t len = 0;
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000228 int ret = 0;
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000229
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700230 batch_mode = 1;
shemmingerdd3e9082005-06-23 17:32:22 +0000231 if (name && strcmp(name, "-") != 0) {
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000232 if (freopen(name, "r", stdin) == NULL) {
Stephen Hemminger84d66882008-02-07 22:10:14 -0800233 fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000234 name, strerror(errno));
235 return -1;
236 }
237 }
238
239 tc_core_init();
240
241 if (rtnl_open(&rth, 0) < 0) {
242 fprintf(stderr, "Cannot open rtnetlink\n");
243 return -1;
244 }
245
shemminger351efcd2005-09-01 19:21:50 +0000246 cmdlineno = 0;
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000247 while (getcmdline(&line, &len, stdin) != -1) {
248 char *largv[100];
249 int largc;
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000250
251 largc = makeargs(line, largv, 100);
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000252 if (largc == 0)
253 continue; /* blank line */
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000254
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000255 if (do_cmd(largc, largv)) {
shemminger351efcd2005-09-01 19:21:50 +0000256 fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000257 ret = 1;
258 if (!force)
259 break;
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000260 }
261 }
shemminger8ed63ab2005-09-21 19:33:17 +0000262 if (line)
263 free(line);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000264
265 rtnl_close(&rth);
266 return ret;
267}
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000268
269
270int main(int argc, char **argv)
271{
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000272 int ret;
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700273 char *batch_file = NULL;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000274
275 while (argc > 1) {
276 if (argv[1][0] != '-')
277 break;
278 if (matches(argv[1], "-stats") == 0 ||
osdl.net!shemminger3ea2bf42004-06-28 20:42:59 +0000279 matches(argv[1], "-statistics") == 0) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000280 ++show_stats;
281 } else if (matches(argv[1], "-details") == 0) {
282 ++show_details;
283 } else if (matches(argv[1], "-raw") == 0) {
284 ++show_raw;
Stephen Hemminger44dcfe82008-05-09 15:42:34 -0700285 } else if (matches(argv[1], "-pretty") == 0) {
286 ++show_pretty;
Vadim Kochand954b342014-12-26 02:10:06 +0200287 } else if (matches(argv[1], "-graph") == 0) {
288 show_graph = 1;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000289 } else if (matches(argv[1], "-Version") == 0) {
290 printf("tc utility, iproute2-ss%s\n", SNAPSHOT);
osdl.net!shemminger044ebf32005-03-14 19:02:41 +0000291 return 0;
osdl.net!shemminger3ea2bf42004-06-28 20:42:59 +0000292 } else if (matches(argv[1], "-iec") == 0) {
293 ++use_iec;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000294 } else if (matches(argv[1], "-help") == 0) {
295 usage();
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000296 return 0;
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000297 } else if (matches(argv[1], "-force") == 0) {
298 ++force;
Vadim Kochan4612d042015-03-03 18:41:18 +0200299 } else if (matches(argv[1], "-batch") == 0) {
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000300 argc--; argv++;
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700301 if (argc <= 1)
302 usage();
303 batch_file = argv[1];
Vadim Kochan67e1d732014-12-24 23:04:11 +0200304 } else if (matches(argv[1], "-netns") == 0) {
305 NEXT_ARG();
306 if (netns_switch(argv[1]))
307 return -1;
Vadim Kochan4612d042015-03-03 18:41:18 +0200308 } else if (matches(argv[1], "-names") == 0 ||
309 matches(argv[1], "-nm") == 0) {
310 use_names = true;
311 } else if (matches(argv[1], "-cf") == 0 ||
312 matches(argv[1], "-conf") == 0) {
313 NEXT_ARG();
314 conf_file = argv[1];
Eric Dumazet32a6fbe2015-09-23 16:40:04 -0700315 } else if (matches(argv[1], "-timestamp") == 0) {
316 timestamp++;
317 } else if (matches(argv[1], "-tshort") == 0) {
318 ++timestamp;
319 ++timestamp_short;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000320 } else {
321 fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]);
osdl.net!shemminger044ebf32005-03-14 19:02:41 +0000322 return -1;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000323 }
324 argc--; argv++;
325 }
326
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700327 if (batch_file)
328 return batch(batch_file);
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000329
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000330 if (argc <= 1) {
331 usage();
332 return 0;
333 }
334
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000335 tc_core_init();
osdl.net!shemminger79016602005-03-14 19:34:12 +0000336 if (rtnl_open(&rth, 0) < 0) {
337 fprintf(stderr, "Cannot open rtnetlink\n");
338 exit(1);
339 }
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000340
Vadim Kochan4612d042015-03-03 18:41:18 +0200341 if (use_names && cls_names_init(conf_file)) {
342 ret = -1;
343 goto Exit;
344 }
345
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000346 ret = do_cmd(argc-1, argv+1);
Vadim Kochan4612d042015-03-03 18:41:18 +0200347Exit:
osdl.net!shemminger79016602005-03-14 19:34:12 +0000348 rtnl_close(&rth);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000349
Vadim Kochan4612d042015-03-03 18:41:18 +0200350 if (use_names)
351 cls_names_uninit();
352
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000353 return ret;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000354}