]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
1 | /* |
2 | * tc_filter.c "tc filter". | |
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 | */ | |
12 | ||
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
aba5acdf | 16 | #include <fcntl.h> |
aba5acdf SH |
17 | #include <sys/socket.h> |
18 | #include <netinet/in.h> | |
19 | #include <arpa/inet.h> | |
20 | #include <string.h> | |
21 | #include <linux/if_ether.h> | |
22 | ||
23 | #include "rt_names.h" | |
24 | #include "utils.h" | |
25 | #include "tc_util.h" | |
26 | #include "tc_common.h" | |
27 | ||
aba5acdf SH |
28 | static void usage(void) |
29 | { | |
ec2e005f | 30 | fprintf(stderr, |
0c7cef96 JP |
31 | "Usage: tc filter [ add | del | change | replace | show ] [ dev STRING ]\n" |
32 | " tc filter [ add | del | change | replace | show ] [ block BLOCK_INDEX ]\n" | |
33 | " tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n" | |
34 | " tc filter get block BLOCK_INDEX protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n" | |
732f0346 | 35 | " [ pref PRIO ] protocol PROTO [ chain CHAIN_INDEX ]\n" |
ec2e005f SH |
36 | " [ estimator INTERVAL TIME_CONSTANT ]\n" |
37 | " [ root | ingress | egress | parent CLASSID ]\n" | |
38 | " [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" | |
39 | "\n" | |
40 | " tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]\n" | |
0c7cef96 | 41 | " tc filter show [ block BLOCK_INDEX ]\n" |
ec2e005f SH |
42 | "Where:\n" |
43 | "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n" | |
44 | "FILTERID := ... format depends on classifier, see there\n" | |
45 | "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n"); | |
aba5acdf SH |
46 | } |
47 | ||
afcd0699 JP |
48 | static void chain_usage(void) |
49 | { | |
50 | fprintf(stderr, | |
51 | "Usage: tc chain [ add | del | get | show ] [ dev STRING ]\n" | |
52 | " tc chain [ add | del | get | show ] [ block BLOCK_INDEX ] ]\n"); | |
53 | } | |
54 | ||
485d0c60 CM |
55 | struct tc_filter_req { |
56 | struct nlmsghdr n; | |
57 | struct tcmsg t; | |
58 | char buf[MAX_MSG]; | |
59 | }; | |
60 | ||
e991c04d | 61 | static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv) |
aba5acdf | 62 | { |
e991c04d SH |
63 | struct { |
64 | struct nlmsghdr n; | |
65 | struct tcmsg t; | |
66 | char buf[MAX_MSG]; | |
67 | } req = { | |
68 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), | |
69 | .n.nlmsg_flags = NLM_F_REQUEST | flags, | |
70 | .n.nlmsg_type = cmd, | |
71 | .t.tcm_family = AF_UNSPEC, | |
72 | }; | |
aba5acdf | 73 | struct filter_util *q = NULL; |
e991c04d | 74 | __u32 prio = 0; |
485d0c60 | 75 | __u32 protocol = 0; |
e991c04d | 76 | int protocol_set = 0; |
39537003 | 77 | __u32 block_index = 0; |
485d0c60 | 78 | __u32 chain_index; |
e991c04d SH |
79 | int chain_index_set = 0; |
80 | char *fhandle = NULL; | |
81 | char d[IFNAMSIZ] = {}; | |
82 | char k[FILTER_NAMESZ] = {}; | |
83 | struct tc_estimator est = {}; | |
aba5acdf | 84 | |
e991c04d | 85 | if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE) |
05fb9184 | 86 | protocol = htons(ETH_P_ALL); |
ae761068 | 87 | |
aba5acdf SH |
88 | while (argc > 0) { |
89 | if (strcmp(*argv, "dev") == 0) { | |
90 | NEXT_ARG(); | |
91 | if (d[0]) | |
92 | duparg("dev", *argv); | |
39537003 IS |
93 | if (block_index) { |
94 | fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n"); | |
95 | return -1; | |
96 | } | |
aba5acdf | 97 | strncpy(d, *argv, sizeof(d)-1); |
39537003 IS |
98 | } else if (matches(*argv, "block") == 0) { |
99 | NEXT_ARG(); | |
100 | if (block_index) | |
101 | duparg("block", *argv); | |
102 | if (d[0]) { | |
103 | fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n"); | |
104 | return -1; | |
105 | } | |
106 | if (get_u32(&block_index, *argv, 0) || !block_index) | |
107 | invarg("invalid block index value", *argv); | |
aba5acdf | 108 | } else if (strcmp(*argv, "root") == 0) { |
e991c04d | 109 | if (req.t.tcm_parent) { |
ec2e005f SH |
110 | fprintf(stderr, |
111 | "Error: \"root\" is duplicate parent ID\n"); | |
024481bb | 112 | return -1; |
aba5acdf | 113 | } |
e991c04d | 114 | req.t.tcm_parent = TC_H_ROOT; |
8f9afdd5 | 115 | } else if (strcmp(*argv, "ingress") == 0) { |
e991c04d | 116 | if (req.t.tcm_parent) { |
ec2e005f SH |
117 | fprintf(stderr, |
118 | "Error: \"ingress\" is duplicate parent ID\n"); | |
8f9afdd5 DB |
119 | return -1; |
120 | } | |
e991c04d | 121 | req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, |
8f9afdd5 DB |
122 | TC_H_MIN_INGRESS); |
123 | } else if (strcmp(*argv, "egress") == 0) { | |
e991c04d | 124 | if (req.t.tcm_parent) { |
ec2e005f SH |
125 | fprintf(stderr, |
126 | "Error: \"egress\" is duplicate parent ID\n"); | |
8f9afdd5 DB |
127 | return -1; |
128 | } | |
e991c04d | 129 | req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, |
8f9afdd5 | 130 | TC_H_MIN_EGRESS); |
aba5acdf SH |
131 | } else if (strcmp(*argv, "parent") == 0) { |
132 | __u32 handle; | |
32a121cb | 133 | |
aba5acdf | 134 | NEXT_ARG(); |
e991c04d | 135 | if (req.t.tcm_parent) |
aba5acdf SH |
136 | duparg("parent", *argv); |
137 | if (get_tc_classid(&handle, *argv)) | |
f1675d61 | 138 | invarg("Invalid parent ID", *argv); |
e991c04d | 139 | req.t.tcm_parent = handle; |
aba5acdf SH |
140 | } else if (strcmp(*argv, "handle") == 0) { |
141 | NEXT_ARG(); | |
142 | if (fhandle) | |
143 | duparg("handle", *argv); | |
144 | fhandle = *argv; | |
145 | } else if (matches(*argv, "preference") == 0 || | |
146 | matches(*argv, "priority") == 0) { | |
147 | NEXT_ARG(); | |
148 | if (prio) | |
149 | duparg("priority", *argv); | |
424adc19 | 150 | if (get_u32(&prio, *argv, 0) || prio > 0xFFFF) |
f1675d61 | 151 | invarg("invalid priority value", *argv); |
aba5acdf SH |
152 | } else if (matches(*argv, "protocol") == 0) { |
153 | __u16 id; | |
32a121cb | 154 | |
aba5acdf | 155 | NEXT_ARG(); |
083a5f00 | 156 | if (protocol_set) |
aba5acdf SH |
157 | duparg("protocol", *argv); |
158 | if (ll_proto_a2n(&id, *argv)) | |
f1675d61 | 159 | invarg("invalid protocol", *argv); |
aba5acdf | 160 | protocol = id; |
083a5f00 | 161 | protocol_set = 1; |
732f0346 JP |
162 | } else if (matches(*argv, "chain") == 0) { |
163 | NEXT_ARG(); | |
164 | if (chain_index_set) | |
165 | duparg("chain", *argv); | |
166 | if (get_u32(&chain_index, *argv, 0)) | |
167 | invarg("invalid chain index value", *argv); | |
168 | chain_index_set = 1; | |
aba5acdf SH |
169 | } else if (matches(*argv, "estimator") == 0) { |
170 | if (parse_estimator(&argc, &argv, &est) < 0) | |
171 | return -1; | |
172 | } else if (matches(*argv, "help") == 0) { | |
173 | usage(); | |
3a99df70 | 174 | return 0; |
aba5acdf SH |
175 | } else { |
176 | strncpy(k, *argv, sizeof(k)-1); | |
177 | ||
178 | q = get_filter_kind(k); | |
179 | argc--; argv++; | |
180 | break; | |
181 | } | |
182 | ||
183 | argc--; argv++; | |
184 | } | |
185 | ||
e991c04d | 186 | req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); |
aba5acdf | 187 | |
732f0346 | 188 | if (chain_index_set) |
e991c04d | 189 | addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index); |
732f0346 | 190 | |
aba5acdf | 191 | if (k[0]) |
e991c04d | 192 | addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); |
aba5acdf | 193 | |
01ea76b1 JK |
194 | if (d[0]) { |
195 | ll_init_map(&rth); | |
196 | ||
e991c04d SH |
197 | req.t.tcm_ifindex = ll_name_to_index(d); |
198 | if (req.t.tcm_ifindex == 0) { | |
199 | fprintf(stderr, "Cannot find device \"%s\"\n", d); | |
200 | return 1; | |
201 | } | |
39537003 IS |
202 | } else if (block_index) { |
203 | req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; | |
204 | req.t.tcm_block_index = block_index; | |
01ea76b1 JK |
205 | } |
206 | ||
aba5acdf | 207 | if (q) { |
e991c04d | 208 | if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) |
024481bb | 209 | return 1; |
aba5acdf SH |
210 | } else { |
211 | if (fhandle) { | |
ec2e005f SH |
212 | fprintf(stderr, |
213 | "Must specify filter type when using \"handle\"\n"); | |
024481bb | 214 | return -1; |
aba5acdf SH |
215 | } |
216 | if (argc) { | |
217 | if (matches(*argv, "help") == 0) | |
218 | usage(); | |
ec2e005f SH |
219 | fprintf(stderr, |
220 | "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", | |
221 | *argv); | |
024481bb | 222 | return -1; |
aba5acdf SH |
223 | } |
224 | } | |
732f0346 | 225 | |
aba5acdf | 226 | if (est.ewma_log) |
e991c04d | 227 | addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); |
aba5acdf | 228 | |
e991c04d SH |
229 | if (rtnl_talk(&rth, &req.n, NULL) < 0) { |
230 | fprintf(stderr, "We have an error talking to the kernel\n"); | |
024481bb | 231 | return 2; |
2373fde9 | 232 | } |
aba5acdf | 233 | |
aba5acdf SH |
234 | return 0; |
235 | } | |
236 | ||
237 | static __u32 filter_parent; | |
238 | static int filter_ifindex; | |
239 | static __u32 filter_prio; | |
240 | static __u32 filter_protocol; | |
732f0346 JP |
241 | static __u32 filter_chain_index; |
242 | static int filter_chain_index_set; | |
0c7cef96 | 243 | static __u32 filter_block_index; |
32a121cb | 244 | __u16 f_proto; |
aba5acdf | 245 | |
cd554f2c | 246 | int print_filter(struct nlmsghdr *n, void *arg) |
aba5acdf | 247 | { |
32a121cb | 248 | FILE *fp = (FILE *)arg; |
aba5acdf SH |
249 | struct tcmsg *t = NLMSG_DATA(n); |
250 | int len = n->nlmsg_len; | |
32a121cb | 251 | struct rtattr *tb[TCA_MAX+1]; |
aba5acdf SH |
252 | struct filter_util *q; |
253 | char abuf[256]; | |
254 | ||
120f556d JHS |
255 | if (n->nlmsg_type != RTM_NEWTFILTER && |
256 | n->nlmsg_type != RTM_GETTFILTER && | |
afcd0699 JP |
257 | n->nlmsg_type != RTM_DELTFILTER && |
258 | n->nlmsg_type != RTM_NEWCHAIN && | |
259 | n->nlmsg_type != RTM_GETCHAIN && | |
260 | n->nlmsg_type != RTM_DELCHAIN) { | |
120f556d | 261 | fprintf(stderr, "Not a filter(cmd %d)\n", n->nlmsg_type); |
aba5acdf SH |
262 | return 0; |
263 | } | |
264 | len -= NLMSG_LENGTH(sizeof(*t)); | |
265 | if (len < 0) { | |
266 | fprintf(stderr, "Wrong len %d\n", len); | |
267 | return -1; | |
268 | } | |
269 | ||
aba5acdf SH |
270 | parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); |
271 | ||
afcd0699 JP |
272 | if (tb[TCA_KIND] == NULL && (n->nlmsg_type == RTM_NEWTFILTER || |
273 | n->nlmsg_type == RTM_GETTFILTER || | |
274 | n->nlmsg_type == RTM_DELTFILTER)) { | |
2373fde9 | 275 | fprintf(stderr, "print_filter: NULL kind\n"); |
aba5acdf SH |
276 | return -1; |
277 | } | |
278 | ||
249284ff JP |
279 | open_json_object(NULL); |
280 | ||
afcd0699 | 281 | if (n->nlmsg_type == RTM_DELTFILTER || n->nlmsg_type == RTM_DELCHAIN) |
249284ff | 282 | print_bool(PRINT_ANY, "deleted", "deleted ", true); |
aba5acdf | 283 | |
afcd0699 JP |
284 | if ((n->nlmsg_type == RTM_NEWTFILTER || |
285 | n->nlmsg_type == RTM_NEWCHAIN) && | |
98df0c81 RM |
286 | (n->nlmsg_flags & NLM_F_CREATE) && |
287 | !(n->nlmsg_flags & NLM_F_EXCL)) | |
249284ff | 288 | print_bool(PRINT_ANY, "replaced", "replaced ", true); |
98df0c81 | 289 | |
afcd0699 JP |
290 | if ((n->nlmsg_type == RTM_NEWTFILTER || |
291 | n->nlmsg_type == RTM_NEWCHAIN) && | |
98df0c81 RM |
292 | (n->nlmsg_flags & NLM_F_CREATE) && |
293 | (n->nlmsg_flags & NLM_F_EXCL)) | |
249284ff | 294 | print_bool(PRINT_ANY, "added", "added ", true); |
98df0c81 | 295 | |
afcd0699 JP |
296 | if (n->nlmsg_type == RTM_NEWTFILTER || |
297 | n->nlmsg_type == RTM_GETTFILTER || | |
298 | n->nlmsg_type == RTM_DELTFILTER) | |
299 | print_string(PRINT_FP, NULL, "filter ", NULL); | |
300 | else | |
301 | print_string(PRINT_FP, NULL, "chain ", NULL); | |
0c7cef96 JP |
302 | if (t->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { |
303 | if (!filter_block_index || | |
304 | filter_block_index != t->tcm_block_index) | |
305 | print_uint(PRINT_ANY, "block", "block %u ", | |
306 | t->tcm_block_index); | |
307 | } else { | |
308 | if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) | |
2d165c08 | 309 | print_devname(PRINT_ANY, t->tcm_ifindex); |
0c7cef96 JP |
310 | |
311 | if (!filter_parent || filter_parent != t->tcm_parent) { | |
312 | if (t->tcm_parent == TC_H_ROOT) | |
313 | print_bool(PRINT_ANY, "root", "root ", true); | |
314 | else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS)) | |
315 | print_bool(PRINT_ANY, "ingress", "ingress ", true); | |
316 | else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS)) | |
317 | print_bool(PRINT_ANY, "egress", "egress ", true); | |
318 | else { | |
319 | print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); | |
320 | print_string(PRINT_ANY, "parent", "parent %s ", abuf); | |
321 | } | |
aba5acdf SH |
322 | } |
323 | } | |
8f9afdd5 | 324 | |
afcd0699 JP |
325 | if (t->tcm_info && (n->nlmsg_type == RTM_NEWTFILTER || |
326 | n->nlmsg_type == RTM_DELTFILTER || | |
327 | n->nlmsg_type == RTM_GETTFILTER)) { | |
eefcbc72 | 328 | f_proto = TC_H_MIN(t->tcm_info); |
aba5acdf | 329 | __u32 prio = TC_H_MAJ(t->tcm_info)>>16; |
32a121cb | 330 | |
eefcbc72 | 331 | if (!filter_protocol || filter_protocol != f_proto) { |
332 | if (f_proto) { | |
aba5acdf | 333 | SPRINT_BUF(b1); |
24a5a48e | 334 | print_string(PRINT_ANY, "protocol", |
249284ff JP |
335 | "protocol %s ", |
336 | ll_proto_n2a(f_proto, b1, sizeof(b1))); | |
aba5acdf SH |
337 | } |
338 | } | |
339 | if (!filter_prio || filter_prio != prio) { | |
340 | if (prio) | |
249284ff | 341 | print_uint(PRINT_ANY, "pref", "pref %u ", prio); |
aba5acdf SH |
342 | } |
343 | } | |
afcd0699 JP |
344 | if (tb[TCA_KIND]) |
345 | print_string(PRINT_ANY, "kind", "%s ", rta_getattr_str(tb[TCA_KIND])); | |
732f0346 JP |
346 | |
347 | if (tb[TCA_CHAIN]) { | |
348 | __u32 chain_index = rta_getattr_u32(tb[TCA_CHAIN]); | |
349 | ||
350 | if (!filter_chain_index_set || | |
351 | filter_chain_index != chain_index) | |
249284ff JP |
352 | print_uint(PRINT_ANY, "chain", "chain %u ", |
353 | chain_index); | |
732f0346 JP |
354 | } |
355 | ||
afcd0699 JP |
356 | if (tb[TCA_KIND]) { |
357 | q = get_filter_kind(RTA_DATA(tb[TCA_KIND])); | |
358 | if (tb[TCA_OPTIONS]) { | |
359 | open_json_object("options"); | |
360 | if (q) | |
361 | q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle); | |
362 | else | |
d5ddb441 | 363 | fprintf(stderr, "cannot parse option parameters\n"); |
afcd0699 JP |
364 | close_json_object(); |
365 | } | |
aba5acdf | 366 | } |
7b0d424a | 367 | print_nl(); |
aba5acdf | 368 | |
e5879dc6 | 369 | if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) { |
370 | print_tcstats_attr(fp, tb, " ", NULL); | |
7b0d424a | 371 | print_nl(); |
aba5acdf | 372 | } |
de481780 | 373 | |
249284ff | 374 | close_json_object(); |
aba5acdf SH |
375 | fflush(fp); |
376 | return 0; | |
377 | } | |
378 | ||
120f556d JHS |
379 | static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv) |
380 | { | |
381 | struct { | |
382 | struct nlmsghdr n; | |
383 | struct tcmsg t; | |
384 | char buf[MAX_MSG]; | |
385 | } req = { | |
386 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), | |
387 | /* NLM_F_ECHO is for backward compatibility. old kernels never | |
388 | * respond without it and newer kernels will ignore it. | |
389 | * In old kernels there is a side effect: | |
390 | * In addition to a response to the GET you will receive an | |
391 | * event (if you do tc mon). | |
392 | */ | |
393 | .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO | flags, | |
394 | .n.nlmsg_type = cmd, | |
395 | .t.tcm_parent = TC_H_UNSPEC, | |
396 | .t.tcm_family = AF_UNSPEC, | |
397 | }; | |
86bf43c7 | 398 | struct nlmsghdr *answer; |
120f556d JHS |
399 | struct filter_util *q = NULL; |
400 | __u32 prio = 0; | |
401 | __u32 protocol = 0; | |
402 | int protocol_set = 0; | |
732f0346 JP |
403 | __u32 chain_index; |
404 | int chain_index_set = 0; | |
0c7cef96 | 405 | __u32 block_index = 0; |
120f556d JHS |
406 | __u32 parent_handle = 0; |
407 | char *fhandle = NULL; | |
b317557f SH |
408 | char d[IFNAMSIZ] = {}; |
409 | char k[FILTER_NAMESZ] = {}; | |
120f556d JHS |
410 | |
411 | while (argc > 0) { | |
412 | if (strcmp(*argv, "dev") == 0) { | |
413 | NEXT_ARG(); | |
414 | if (d[0]) | |
415 | duparg("dev", *argv); | |
0c7cef96 | 416 | if (block_index) { |
69df9bf9 | 417 | fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n"); |
0c7cef96 JP |
418 | return -1; |
419 | } | |
120f556d | 420 | strncpy(d, *argv, sizeof(d)-1); |
0c7cef96 JP |
421 | } else if (matches(*argv, "block") == 0) { |
422 | NEXT_ARG(); | |
423 | if (block_index) | |
424 | duparg("block", *argv); | |
425 | if (d[0]) { | |
69df9bf9 | 426 | fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n"); |
0c7cef96 JP |
427 | return -1; |
428 | } | |
429 | if (get_u32(&block_index, *argv, 0) || !block_index) | |
430 | invarg("invalid block index value", *argv); | |
120f556d JHS |
431 | } else if (strcmp(*argv, "root") == 0) { |
432 | if (req.t.tcm_parent) { | |
ec2e005f SH |
433 | fprintf(stderr, |
434 | "Error: \"root\" is duplicate parent ID\n"); | |
120f556d JHS |
435 | return -1; |
436 | } | |
437 | req.t.tcm_parent = TC_H_ROOT; | |
438 | } else if (strcmp(*argv, "ingress") == 0) { | |
439 | if (req.t.tcm_parent) { | |
ec2e005f SH |
440 | fprintf(stderr, |
441 | "Error: \"ingress\" is duplicate parent ID\n"); | |
120f556d JHS |
442 | return -1; |
443 | } | |
444 | req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, | |
445 | TC_H_MIN_INGRESS); | |
446 | } else if (strcmp(*argv, "egress") == 0) { | |
447 | if (req.t.tcm_parent) { | |
ec2e005f SH |
448 | fprintf(stderr, |
449 | "Error: \"egress\" is duplicate parent ID\n"); | |
120f556d JHS |
450 | return -1; |
451 | } | |
452 | req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT, | |
453 | TC_H_MIN_EGRESS); | |
454 | } else if (strcmp(*argv, "parent") == 0) { | |
455 | ||
456 | NEXT_ARG(); | |
457 | if (req.t.tcm_parent) | |
458 | duparg("parent", *argv); | |
459 | if (get_tc_classid(&parent_handle, *argv)) | |
460 | invarg("Invalid parent ID", *argv); | |
461 | req.t.tcm_parent = parent_handle; | |
462 | } else if (strcmp(*argv, "handle") == 0) { | |
463 | NEXT_ARG(); | |
464 | if (fhandle) | |
465 | duparg("handle", *argv); | |
466 | fhandle = *argv; | |
467 | } else if (matches(*argv, "preference") == 0 || | |
468 | matches(*argv, "priority") == 0) { | |
469 | NEXT_ARG(); | |
470 | if (prio) | |
471 | duparg("priority", *argv); | |
472 | if (get_u32(&prio, *argv, 0) || prio > 0xFFFF) | |
473 | invarg("invalid priority value", *argv); | |
474 | } else if (matches(*argv, "protocol") == 0) { | |
475 | __u16 id; | |
476 | ||
477 | NEXT_ARG(); | |
478 | if (protocol_set) | |
479 | duparg("protocol", *argv); | |
480 | if (ll_proto_a2n(&id, *argv)) | |
481 | invarg("invalid protocol", *argv); | |
482 | protocol = id; | |
483 | protocol_set = 1; | |
732f0346 JP |
484 | } else if (matches(*argv, "chain") == 0) { |
485 | NEXT_ARG(); | |
486 | if (chain_index_set) | |
487 | duparg("chain", *argv); | |
488 | if (get_u32(&chain_index, *argv, 0)) | |
489 | invarg("invalid chain index value", *argv); | |
490 | chain_index_set = 1; | |
120f556d JHS |
491 | } else if (matches(*argv, "help") == 0) { |
492 | usage(); | |
493 | return 0; | |
494 | } else { | |
75716932 PS |
495 | if (!**argv) |
496 | invarg("invalid filter name", *argv); | |
497 | ||
120f556d JHS |
498 | strncpy(k, *argv, sizeof(k)-1); |
499 | ||
500 | q = get_filter_kind(k); | |
501 | argc--; argv++; | |
502 | break; | |
503 | } | |
504 | ||
505 | argc--; argv++; | |
506 | } | |
507 | ||
afcd0699 JP |
508 | if (cmd == RTM_GETTFILTER) { |
509 | if (!protocol_set) { | |
510 | fprintf(stderr, "Must specify filter protocol\n"); | |
511 | return -1; | |
512 | } | |
120f556d | 513 | |
afcd0699 JP |
514 | if (!prio) { |
515 | fprintf(stderr, "Must specify filter priority\n"); | |
516 | return -1; | |
517 | } | |
120f556d | 518 | |
afcd0699 JP |
519 | req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); |
520 | } | |
120f556d | 521 | |
732f0346 JP |
522 | if (chain_index_set) |
523 | addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index); | |
524 | ||
120f556d JHS |
525 | if (req.t.tcm_parent == TC_H_UNSPEC) { |
526 | fprintf(stderr, "Must specify filter parent\n"); | |
527 | return -1; | |
528 | } | |
529 | ||
afcd0699 JP |
530 | if (cmd == RTM_GETTFILTER) { |
531 | if (k[0]) | |
532 | addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); | |
533 | else { | |
534 | fprintf(stderr, "Must specify filter type\n"); | |
535 | return -1; | |
536 | } | |
120f556d JHS |
537 | } |
538 | ||
01ea76b1 JK |
539 | if (d[0]) { |
540 | ll_init_map(&rth); | |
541 | ||
542 | req.t.tcm_ifindex = ll_name_to_index(d); | |
fe99adbc SP |
543 | if (!req.t.tcm_ifindex) |
544 | return -nodev(d); | |
01ea76b1 | 545 | filter_ifindex = req.t.tcm_ifindex; |
0c7cef96 JP |
546 | } else if (block_index) { |
547 | req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; | |
548 | req.t.tcm_block_index = block_index; | |
549 | filter_block_index = block_index; | |
01ea76b1 | 550 | } else { |
0c7cef96 | 551 | fprintf(stderr, "Must specify netdevice \"dev\" or block index \"block\"\n"); |
01ea76b1 JK |
552 | return -1; |
553 | } | |
554 | ||
afcd0699 JP |
555 | if (cmd == RTM_GETTFILTER && |
556 | q->parse_fopt(q, fhandle, argc, argv, &req.n)) | |
120f556d JHS |
557 | return 1; |
558 | ||
afcd0699 | 559 | if (!fhandle && cmd == RTM_GETTFILTER) { |
120f556d JHS |
560 | fprintf(stderr, "Must specify filter \"handle\"\n"); |
561 | return -1; | |
562 | } | |
563 | ||
564 | if (argc) { | |
565 | if (matches(*argv, "help") == 0) | |
566 | usage(); | |
ec2e005f SH |
567 | fprintf(stderr, |
568 | "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", | |
120f556d JHS |
569 | *argv); |
570 | return -1; | |
571 | } | |
572 | ||
86bf43c7 | 573 | if (rtnl_talk(&rth, &req.n, &answer) < 0) { |
120f556d JHS |
574 | fprintf(stderr, "We have an error talking to the kernel\n"); |
575 | return 2; | |
576 | } | |
577 | ||
249284ff | 578 | new_json_obj(json); |
cd554f2c | 579 | print_filter(answer, (void *)stdout); |
249284ff | 580 | delete_json_obj(); |
120f556d | 581 | |
86bf43c7 | 582 | free(answer); |
120f556d JHS |
583 | return 0; |
584 | } | |
585 | ||
afcd0699 | 586 | static int tc_filter_list(int cmd, int argc, char **argv) |
aba5acdf | 587 | { |
732f0346 JP |
588 | struct { |
589 | struct nlmsghdr n; | |
590 | struct tcmsg t; | |
591 | char buf[MAX_MSG]; | |
592 | } req = { | |
593 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), | |
afcd0699 | 594 | .n.nlmsg_type = cmd, |
732f0346 JP |
595 | .t.tcm_parent = TC_H_UNSPEC, |
596 | .t.tcm_family = AF_UNSPEC, | |
597 | }; | |
b317557f | 598 | char d[IFNAMSIZ] = {}; |
aba5acdf SH |
599 | __u32 prio = 0; |
600 | __u32 protocol = 0; | |
732f0346 | 601 | __u32 chain_index; |
0c7cef96 | 602 | __u32 block_index = 0; |
aba5acdf SH |
603 | char *fhandle = NULL; |
604 | ||
aba5acdf SH |
605 | while (argc > 0) { |
606 | if (strcmp(*argv, "dev") == 0) { | |
607 | NEXT_ARG(); | |
608 | if (d[0]) | |
609 | duparg("dev", *argv); | |
0c7cef96 JP |
610 | if (block_index) { |
611 | fprintf(stderr, "Error: \"dev\" cannot be used in the same time as \"block\"\n"); | |
612 | return -1; | |
613 | } | |
aba5acdf | 614 | strncpy(d, *argv, sizeof(d)-1); |
0c7cef96 JP |
615 | } else if (matches(*argv, "block") == 0) { |
616 | NEXT_ARG(); | |
617 | if (block_index) | |
618 | duparg("block", *argv); | |
619 | if (d[0]) { | |
620 | fprintf(stderr, "Error: \"block\" cannot be used in the same time as \"dev\"\n"); | |
621 | return -1; | |
622 | } | |
623 | if (get_u32(&block_index, *argv, 0) || !block_index) | |
624 | invarg("invalid block index value", *argv); | |
aba5acdf | 625 | } else if (strcmp(*argv, "root") == 0) { |
732f0346 | 626 | if (req.t.tcm_parent) { |
ec2e005f SH |
627 | fprintf(stderr, |
628 | "Error: \"root\" is duplicate parent ID\n"); | |
024481bb | 629 | return -1; |
aba5acdf | 630 | } |
732f0346 | 631 | filter_parent = req.t.tcm_parent = TC_H_ROOT; |
8f9afdd5 | 632 | } else if (strcmp(*argv, "ingress") == 0) { |
732f0346 | 633 | if (req.t.tcm_parent) { |
ec2e005f SH |
634 | fprintf(stderr, |
635 | "Error: \"ingress\" is duplicate parent ID\n"); | |
8f9afdd5 DB |
636 | return -1; |
637 | } | |
638 | filter_parent = TC_H_MAKE(TC_H_CLSACT, | |
639 | TC_H_MIN_INGRESS); | |
732f0346 | 640 | req.t.tcm_parent = filter_parent; |
8f9afdd5 | 641 | } else if (strcmp(*argv, "egress") == 0) { |
732f0346 | 642 | if (req.t.tcm_parent) { |
ec2e005f SH |
643 | fprintf(stderr, |
644 | "Error: \"egress\" is duplicate parent ID\n"); | |
8f9afdd5 DB |
645 | return -1; |
646 | } | |
647 | filter_parent = TC_H_MAKE(TC_H_CLSACT, | |
648 | TC_H_MIN_EGRESS); | |
732f0346 | 649 | req.t.tcm_parent = filter_parent; |
aba5acdf SH |
650 | } else if (strcmp(*argv, "parent") == 0) { |
651 | __u32 handle; | |
32a121cb | 652 | |
aba5acdf | 653 | NEXT_ARG(); |
732f0346 | 654 | if (req.t.tcm_parent) |
aba5acdf SH |
655 | duparg("parent", *argv); |
656 | if (get_tc_classid(&handle, *argv)) | |
f1675d61 | 657 | invarg("invalid parent ID", *argv); |
732f0346 | 658 | filter_parent = req.t.tcm_parent = handle; |
aba5acdf SH |
659 | } else if (strcmp(*argv, "handle") == 0) { |
660 | NEXT_ARG(); | |
661 | if (fhandle) | |
662 | duparg("handle", *argv); | |
663 | fhandle = *argv; | |
664 | } else if (matches(*argv, "preference") == 0 || | |
665 | matches(*argv, "priority") == 0) { | |
666 | NEXT_ARG(); | |
667 | if (prio) | |
668 | duparg("priority", *argv); | |
669 | if (get_u32(&prio, *argv, 0)) | |
f1675d61 | 670 | invarg("invalid preference", *argv); |
aba5acdf SH |
671 | filter_prio = prio; |
672 | } else if (matches(*argv, "protocol") == 0) { | |
673 | __u16 res; | |
32a121cb | 674 | |
aba5acdf SH |
675 | NEXT_ARG(); |
676 | if (protocol) | |
677 | duparg("protocol", *argv); | |
678 | if (ll_proto_a2n(&res, *argv)) | |
f1675d61 | 679 | invarg("invalid protocol", *argv); |
aba5acdf SH |
680 | protocol = res; |
681 | filter_protocol = protocol; | |
732f0346 JP |
682 | } else if (matches(*argv, "chain") == 0) { |
683 | NEXT_ARG(); | |
684 | if (filter_chain_index_set) | |
685 | duparg("chain", *argv); | |
686 | if (get_u32(&chain_index, *argv, 0)) | |
687 | invarg("invalid chain index value", *argv); | |
688 | filter_chain_index_set = 1; | |
689 | filter_chain_index = chain_index; | |
aba5acdf SH |
690 | } else if (matches(*argv, "help") == 0) { |
691 | usage(); | |
692 | } else { | |
ec2e005f SH |
693 | fprintf(stderr, |
694 | " What is \"%s\"? Try \"tc filter help\"\n", | |
695 | *argv); | |
024481bb | 696 | return -1; |
aba5acdf SH |
697 | } |
698 | ||
699 | argc--; argv++; | |
700 | } | |
701 | ||
732f0346 | 702 | req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); |
aba5acdf | 703 | |
3d0b7439 | 704 | ll_init_map(&rth); |
aba5acdf SH |
705 | |
706 | if (d[0]) { | |
732f0346 | 707 | req.t.tcm_ifindex = ll_name_to_index(d); |
fe99adbc SP |
708 | if (!req.t.tcm_ifindex) |
709 | return -nodev(d); | |
732f0346 | 710 | filter_ifindex = req.t.tcm_ifindex; |
0c7cef96 JP |
711 | } else if (block_index) { |
712 | if (!tc_qdisc_block_exists(block_index)) { | |
713 | fprintf(stderr, "Cannot find block \"%u\"\n", block_index); | |
714 | return 1; | |
715 | } | |
716 | req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; | |
717 | req.t.tcm_block_index = block_index; | |
718 | filter_block_index = block_index; | |
aba5acdf SH |
719 | } |
720 | ||
732f0346 JP |
721 | if (filter_chain_index_set) |
722 | addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index); | |
723 | ||
724 | if (rtnl_dump_request_n(&rth, &req.n) < 0) { | |
aba5acdf | 725 | perror("Cannot send dump request"); |
024481bb | 726 | return 1; |
aba5acdf SH |
727 | } |
728 | ||
249284ff | 729 | new_json_obj(json); |
3d0b7439 | 730 | if (rtnl_dump_filter(&rth, print_filter, stdout) < 0) { |
aba5acdf | 731 | fprintf(stderr, "Dump terminated\n"); |
024481bb | 732 | return 1; |
aba5acdf | 733 | } |
249284ff | 734 | delete_json_obj(); |
aba5acdf | 735 | |
aba5acdf SH |
736 | return 0; |
737 | } | |
738 | ||
e991c04d | 739 | int do_filter(int argc, char **argv) |
aba5acdf SH |
740 | { |
741 | if (argc < 1) | |
afcd0699 | 742 | return tc_filter_list(RTM_GETTFILTER, 0, NULL); |
aba5acdf | 743 | if (matches(*argv, "add") == 0) |
120f556d | 744 | return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, |
e991c04d | 745 | argc-1, argv+1); |
aba5acdf | 746 | if (matches(*argv, "change") == 0) |
e991c04d | 747 | return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1); |
aba5acdf | 748 | if (matches(*argv, "replace") == 0) |
120f556d | 749 | return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, |
e991c04d | 750 | argv+1); |
aba5acdf | 751 | if (matches(*argv, "delete") == 0) |
e991c04d | 752 | return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1); |
aba5acdf SH |
753 | if (matches(*argv, "get") == 0) |
754 | return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1); | |
aba5acdf SH |
755 | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 |
756 | || matches(*argv, "lst") == 0) | |
afcd0699 | 757 | return tc_filter_list(RTM_GETTFILTER, argc-1, argv+1); |
e5d179d8 | 758 | if (matches(*argv, "help") == 0) { |
aba5acdf | 759 | usage(); |
e5d179d8 | 760 | return 0; |
32a121cb | 761 | } |
120f556d JHS |
762 | fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", |
763 | *argv); | |
024481bb | 764 | return -1; |
aba5acdf | 765 | } |
afcd0699 | 766 | |
e991c04d | 767 | int do_chain(int argc, char **argv) |
afcd0699 JP |
768 | { |
769 | if (argc < 1) | |
770 | return tc_filter_list(RTM_GETCHAIN, 0, NULL); | |
771 | if (matches(*argv, "add") == 0) { | |
772 | return tc_filter_modify(RTM_NEWCHAIN, NLM_F_EXCL | NLM_F_CREATE, | |
e991c04d | 773 | argc - 1, argv + 1); |
afcd0699 JP |
774 | } else if (matches(*argv, "delete") == 0) { |
775 | return tc_filter_modify(RTM_DELCHAIN, 0, | |
e991c04d | 776 | argc - 1, argv + 1); |
afcd0699 JP |
777 | } else if (matches(*argv, "get") == 0) { |
778 | return tc_filter_get(RTM_GETCHAIN, 0, | |
779 | argc - 1, argv + 1); | |
780 | } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 || | |
781 | matches(*argv, "lst") == 0) { | |
782 | return tc_filter_list(RTM_GETCHAIN, argc - 1, argv + 1); | |
783 | } else if (matches(*argv, "help") == 0) { | |
784 | chain_usage(); | |
785 | return 0; | |
786 | } | |
787 | fprintf(stderr, "Command \"%s\" is unknown, try \"tc chain help\".\n", | |
788 | *argv); | |
789 | return -1; | |
790 | } |