]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/tc.c
Merge branch 'master' into net-next
[mirror_iproute2.git] / tc / tc.c
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 #include "namespace.h"
33
34 int show_stats;
35 int show_details;
36 int show_raw;
37 int show_pretty;
38 int show_graph;
39 int timestamp;
40
41 int batch_mode;
42 int resolve_hosts;
43 int use_iec;
44 int force;
45 bool use_names;
46
47 static char *conf_file;
48
49 struct rtnl_handle rth;
50
51 static void *BODY; /* cached handle dlopen(NULL) */
52 static struct qdisc_util *qdisc_list;
53 static struct filter_util *filter_list;
54
55 static int print_noqopt(struct qdisc_util *qu, FILE *f,
56 struct rtattr *opt)
57 {
58 if (opt && RTA_PAYLOAD(opt))
59 fprintf(f, "[Unknown qdisc, optlen=%u] ",
60 (unsigned int) RTA_PAYLOAD(opt));
61 return 0;
62 }
63
64 static 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
73 static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle)
74 {
75 if (opt && RTA_PAYLOAD(opt))
76 fprintf(f, "fh %08x [Unknown filter, optlen=%u] ",
77 fhandle, (unsigned int) RTA_PAYLOAD(opt));
78 else if (fhandle)
79 fprintf(f, "fh %08x ", fhandle);
80 return 0;
81 }
82
83 static 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);
93
94 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
103 struct qdisc_util *get_qdisc_kind(const char *str)
104 {
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
113 snprintf(buf, sizeof(buf), "%s/q_%s.so", get_tc_lib(), str);
114 dlh = dlopen(buf, RTLD_LAZY);
115 if (!dlh) {
116 /* look in current binary, only open once */
117 dlh = BODY;
118 if (dlh == NULL) {
119 dlh = BODY = dlopen(NULL, RTLD_LAZY);
120 if (dlh == NULL)
121 goto noexist;
122 }
123 }
124
125 snprintf(buf, sizeof(buf), "%s_qdisc_util", str);
126 q = dlsym(dlh, buf);
127 if (q == NULL)
128 goto noexist;
129
130 reg:
131 q->next = qdisc_list;
132 qdisc_list = q;
133 return q;
134
135 noexist:
136 q = calloc(1, sizeof(*q));
137 if (q) {
138 q->id = strdup(str);
139 q->parse_qopt = parse_noqopt;
140 q->print_qopt = print_noqopt;
141 goto reg;
142 }
143 return q;
144 }
145
146
147 struct filter_util *get_filter_kind(const char *str)
148 {
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
157 snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
158 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
168 snprintf(buf, sizeof(buf), "%s_filter_util", str);
169 q = dlsym(dlh, buf);
170 if (q == NULL)
171 goto noexist;
172
173 reg:
174 q->next = filter_list;
175 filter_list = q;
176 return q;
177 noexist:
178 q = calloc(1, sizeof(*q));
179 if (q) {
180 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
188 static void usage(void)
189 {
190 fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
191 " tc [-force] -batch filename\n"
192 "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"
194 " -nm | -nam[es] | { -cf | -conf } path }\n");
195 }
196
197 static int do_cmd(int argc, char **argv)
198 {
199 if (matches(*argv, "qdisc") == 0)
200 return do_qdisc(argc-1, argv+1);
201 if (matches(*argv, "class") == 0)
202 return do_class(argc-1, argv+1);
203 if (matches(*argv, "filter") == 0)
204 return do_filter(argc-1, argv+1);
205 if (matches(*argv, "actions") == 0)
206 return do_action(argc-1, argv+1);
207 if (matches(*argv, "monitor") == 0)
208 return do_tcmonitor(argc-1, argv+1);
209 if (matches(*argv, "exec") == 0)
210 return do_exec(argc-1, argv+1);
211 if (matches(*argv, "help") == 0) {
212 usage();
213 return 0;
214 }
215
216 fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n",
217 *argv);
218 return -1;
219 }
220
221 static int batch(const char *name)
222 {
223 char *line = NULL;
224 size_t len = 0;
225 int ret = 0;
226
227 batch_mode = 1;
228 if (name && strcmp(name, "-") != 0) {
229 if (freopen(name, "r", stdin) == NULL) {
230 fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
231 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
243 cmdlineno = 0;
244 while (getcmdline(&line, &len, stdin) != -1) {
245 char *largv[100];
246 int largc;
247
248 largc = makeargs(line, largv, 100);
249 if (largc == 0)
250 continue; /* blank line */
251
252 if (do_cmd(largc, largv)) {
253 fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
254 ret = 1;
255 if (!force)
256 break;
257 }
258 }
259 if (line)
260 free(line);
261
262 rtnl_close(&rth);
263 return ret;
264 }
265
266
267 int main(int argc, char **argv)
268 {
269 int ret;
270 char *batch_file = NULL;
271
272 while (argc > 1) {
273 if (argv[1][0] != '-')
274 break;
275 if (matches(argv[1], "-stats") == 0 ||
276 matches(argv[1], "-statistics") == 0) {
277 ++show_stats;
278 } else if (matches(argv[1], "-details") == 0) {
279 ++show_details;
280 } else if (matches(argv[1], "-raw") == 0) {
281 ++show_raw;
282 } else if (matches(argv[1], "-pretty") == 0) {
283 ++show_pretty;
284 } else if (matches(argv[1], "-graph") == 0) {
285 show_graph = 1;
286 } else if (matches(argv[1], "-Version") == 0) {
287 printf("tc utility, iproute2-ss%s\n", SNAPSHOT);
288 return 0;
289 } else if (matches(argv[1], "-iec") == 0) {
290 ++use_iec;
291 } else if (matches(argv[1], "-help") == 0) {
292 usage();
293 return 0;
294 } else if (matches(argv[1], "-force") == 0) {
295 ++force;
296 } else if (matches(argv[1], "-batch") == 0) {
297 argc--; argv++;
298 if (argc <= 1)
299 usage();
300 batch_file = argv[1];
301 } else if (matches(argv[1], "-netns") == 0) {
302 NEXT_ARG();
303 if (netns_switch(argv[1]))
304 return -1;
305 } 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];
312 } else if (matches(argv[1], "-timestamp") == 0) {
313 timestamp++;
314 } else if (matches(argv[1], "-tshort") == 0) {
315 ++timestamp;
316 ++timestamp_short;
317 } else {
318 fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]);
319 return -1;
320 }
321 argc--; argv++;
322 }
323
324 if (batch_file)
325 return batch(batch_file);
326
327 if (argc <= 1) {
328 usage();
329 return 0;
330 }
331
332 tc_core_init();
333 if (rtnl_open(&rth, 0) < 0) {
334 fprintf(stderr, "Cannot open rtnetlink\n");
335 exit(1);
336 }
337
338 if (use_names && cls_names_init(conf_file)) {
339 ret = -1;
340 goto Exit;
341 }
342
343 ret = do_cmd(argc-1, argv+1);
344 Exit:
345 rtnl_close(&rth);
346
347 if (use_names)
348 cls_names_uninit();
349
350 return ret;
351 }