blob: 8e64a82b4271cf1800c2feabb24a28c31b765382 [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:
Phil Sutterf89bb022016-07-18 16:48:43 +0200136 q = calloc(1, sizeof(*q));
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000137 if (q) {
Phil Sutterf89bb022016-07-18 16:48:43 +0200138 q->id = strdup(str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000139 q->parse_qopt = parse_noqopt;
140 q->print_qopt = print_noqopt;
141 goto reg;
142 }
143 return q;
144}
145
146
osdl.org!shemminger4094db72004-06-02 20:22:08 +0000147struct filter_util *get_filter_kind(const char *str)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000148{
149 void *dlh;
150 char buf[256];
151 struct filter_util *q;
152
153 for (q = filter_list; q; q = q->next)
154 if (strcmp(q->id, str) == 0)
155 return q;
156
Stephen Hemmingeraa27f882007-06-20 15:27:22 -0700157 snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000158 dlh = dlopen(buf, RTLD_LAZY);
159 if (dlh == NULL) {
160 dlh = BODY;
161 if (dlh == NULL) {
162 dlh = BODY = dlopen(NULL, RTLD_LAZY);
163 if (dlh == NULL)
164 goto noexist;
165 }
166 }
167
net[shemminger]!kaber95812b52004-09-28 18:35:49 +0000168 snprintf(buf, sizeof(buf), "%s_filter_util", str);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000169 q = dlsym(dlh, buf);
170 if (q == NULL)
171 goto noexist;
172
173reg:
174 q->next = filter_list;
175 filter_list = q;
176 return q;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000177noexist:
Phil Sutterf89bb022016-07-18 16:48:43 +0200178 q = calloc(1, sizeof(*q));
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000179 if (q) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000180 strncpy(q->id, str, 15);
181 q->parse_fopt = parse_nofopt;
182 q->print_fopt = print_nofopt;
183 goto reg;
184 }
185 return q;
186}
187
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000188static void usage(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000189{
190 fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
Petr Jediný10494d22008-08-07 16:45:33 +0200191 " tc [-force] -batch filename\n"
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700192 "where OBJECT := { qdisc | class | filter | action | monitor | exec }\n"
193 " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | -n[etns] name |\n"
Vadim Kochan4612d042015-03-03 18:41:18 +0200194 " -nm | -nam[es] | { -cf | -conf } path }\n");
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000195}
196
197static int do_cmd(int argc, char **argv)
198{
199 if (matches(*argv, "qdisc") == 0)
200 return do_qdisc(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000201 if (matches(*argv, "class") == 0)
202 return do_class(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000203 if (matches(*argv, "filter") == 0)
204 return do_filter(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000205 if (matches(*argv, "actions") == 0)
206 return do_action(argc-1, argv+1);
Jamal Hadi Salim5bec3482006-08-08 11:55:15 -0700207 if (matches(*argv, "monitor") == 0)
208 return do_tcmonitor(argc-1, argv+1);
Daniel Borkmann4bd62442015-04-16 21:20:06 +0200209 if (matches(*argv, "exec") == 0)
210 return do_exec(argc-1, argv+1);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000211 if (matches(*argv, "help") == 0) {
212 usage();
213 return 0;
214 }
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800215
216 fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n",
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000217 *argv);
osdl.net!shemminger044ebf32005-03-14 19:02:41 +0000218 return -1;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000219}
220
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000221static int batch(const char *name)
222{
223 char *line = NULL;
224 size_t len = 0;
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000225 int ret = 0;
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000226
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700227 batch_mode = 1;
shemmingerdd3e9082005-06-23 17:32:22 +0000228 if (name && strcmp(name, "-") != 0) {
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000229 if (freopen(name, "r", stdin) == NULL) {
Stephen Hemminger84d66882008-02-07 22:10:14 -0800230 fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000231 name, strerror(errno));
232 return -1;
233 }
234 }
235
236 tc_core_init();
237
238 if (rtnl_open(&rth, 0) < 0) {
239 fprintf(stderr, "Cannot open rtnetlink\n");
240 return -1;
241 }
242
shemminger351efcd2005-09-01 19:21:50 +0000243 cmdlineno = 0;
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000244 while (getcmdline(&line, &len, stdin) != -1) {
245 char *largv[100];
246 int largc;
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000247
248 largc = makeargs(line, largv, 100);
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000249 if (largc == 0)
250 continue; /* blank line */
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000251
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000252 if (do_cmd(largc, largv)) {
shemminger351efcd2005-09-01 19:21:50 +0000253 fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000254 ret = 1;
255 if (!force)
256 break;
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000257 }
258 }
shemminger8ed63ab2005-09-21 19:33:17 +0000259 if (line)
260 free(line);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000261
262 rtnl_close(&rth);
263 return ret;
264}
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000265
266
267int main(int argc, char **argv)
268{
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000269 int ret;
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700270 char *batch_file = NULL;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000271
272 while (argc > 1) {
273 if (argv[1][0] != '-')
274 break;
275 if (matches(argv[1], "-stats") == 0 ||
osdl.net!shemminger3ea2bf42004-06-28 20:42:59 +0000276 matches(argv[1], "-statistics") == 0) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000277 ++show_stats;
278 } else if (matches(argv[1], "-details") == 0) {
279 ++show_details;
280 } else if (matches(argv[1], "-raw") == 0) {
281 ++show_raw;
Stephen Hemminger44dcfe82008-05-09 15:42:34 -0700282 } else if (matches(argv[1], "-pretty") == 0) {
283 ++show_pretty;
Vadim Kochand954b342014-12-26 02:10:06 +0200284 } else if (matches(argv[1], "-graph") == 0) {
285 show_graph = 1;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000286 } else if (matches(argv[1], "-Version") == 0) {
287 printf("tc utility, iproute2-ss%s\n", SNAPSHOT);
osdl.net!shemminger044ebf32005-03-14 19:02:41 +0000288 return 0;
osdl.net!shemminger3ea2bf42004-06-28 20:42:59 +0000289 } else if (matches(argv[1], "-iec") == 0) {
290 ++use_iec;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000291 } else if (matches(argv[1], "-help") == 0) {
292 usage();
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000293 return 0;
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000294 } else if (matches(argv[1], "-force") == 0) {
295 ++force;
Vadim Kochan4612d042015-03-03 18:41:18 +0200296 } else if (matches(argv[1], "-batch") == 0) {
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000297 argc--; argv++;
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700298 if (argc <= 1)
299 usage();
300 batch_file = argv[1];
Vadim Kochan67e1d732014-12-24 23:04:11 +0200301 } else if (matches(argv[1], "-netns") == 0) {
302 NEXT_ARG();
303 if (netns_switch(argv[1]))
304 return -1;
Vadim Kochan4612d042015-03-03 18:41:18 +0200305 } else if (matches(argv[1], "-names") == 0 ||
306 matches(argv[1], "-nm") == 0) {
307 use_names = true;
308 } else if (matches(argv[1], "-cf") == 0 ||
309 matches(argv[1], "-conf") == 0) {
310 NEXT_ARG();
311 conf_file = argv[1];
Eric Dumazet32a6fbe2015-09-23 16:40:04 -0700312 } else if (matches(argv[1], "-timestamp") == 0) {
313 timestamp++;
314 } else if (matches(argv[1], "-tshort") == 0) {
315 ++timestamp;
316 ++timestamp_short;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000317 } else {
318 fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]);
osdl.net!shemminger044ebf32005-03-14 19:02:41 +0000319 return -1;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000320 }
321 argc--; argv++;
322 }
323
Stephen Hemmingera3aa47a2013-07-16 10:04:05 -0700324 if (batch_file)
325 return batch(batch_file);
osdl.net!shemminger08856f02005-03-18 19:40:55 +0000326
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000327 if (argc <= 1) {
328 usage();
329 return 0;
330 }
331
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000332 tc_core_init();
osdl.net!shemminger79016602005-03-14 19:34:12 +0000333 if (rtnl_open(&rth, 0) < 0) {
334 fprintf(stderr, "Cannot open rtnetlink\n");
335 exit(1);
336 }
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000337
Vadim Kochan4612d042015-03-03 18:41:18 +0200338 if (use_names && cls_names_init(conf_file)) {
339 ret = -1;
340 goto Exit;
341 }
342
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000343 ret = do_cmd(argc-1, argv+1);
Vadim Kochan4612d042015-03-03 18:41:18 +0200344Exit:
osdl.net!shemminger79016602005-03-14 19:34:12 +0000345 rtnl_close(&rth);
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000346
Vadim Kochan4612d042015-03-03 18:41:18 +0200347 if (use_names)
348 cls_names_uninit();
349
site!shemmingerc2f3a0f2005-03-14 22:19:16 +0000350 return ret;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000351}