]>
Commit | Line | Data |
---|---|---|
68d5ba54 SH |
1 | /* |
2 | * m_action.c Action Management | |
3 | * | |
4 | * This program is free software; you can distribute 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 | * | |
ae665a52 SH |
9 | * Authors: J Hadi Salim (hadi@cyberus.ca) |
10 | * | |
68d5ba54 SH |
11 | * TODO: |
12 | * - parse to be passed a filedescriptor for logging purposes | |
13 | * | |
ed485691 | 14 | */ |
68d5ba54 SH |
15 | |
16 | #include <stdio.h> | |
17 | #include <stdlib.h> | |
ed485691 | 18 | #include <stdbool.h> |
68d5ba54 | 19 | #include <unistd.h> |
68d5ba54 SH |
20 | #include <fcntl.h> |
21 | #include <sys/socket.h> | |
22 | #include <netinet/in.h> | |
23 | #include <arpa/inet.h> | |
24 | #include <string.h> | |
25 | #include <dlfcn.h> | |
26 | ||
27 | #include "utils.h" | |
c1027a75 | 28 | #include "tc_common.h" |
68d5ba54 SH |
29 | #include "tc_util.h" |
30 | ||
32a121cb | 31 | static struct action_util *action_list; |
68d5ba54 | 32 | #ifdef CONFIG_GACT |
9e96e715 | 33 | static int gact_ld; /* f*ckin backward compatibility */ |
68d5ba54 | 34 | #endif |
9e96e715 | 35 | static int tab_flush; |
68d5ba54 | 36 | |
d1f28cf1 | 37 | static void act_usage(void) |
68d5ba54 | 38 | { |
f1e4f042 | 39 | /*XXX: In the near future add a action->print_help to improve |
40 | * usability | |
41 | * This would mean new tc will not be backward compatible | |
42 | * with any action .so from the old days. But if someone really | |
43 | * does that, they would know how to fix this .. | |
44 | * | |
ed485691 | 45 | */ |
32a121cb | 46 | fprintf(stderr, "usage: tc actions <ACTSPECOP>*\n"); |
f1e4f042 | 47 | fprintf(stderr, |
48 | "Where: \tACTSPECOP := ACR | GD | FL\n" | |
32a121cb | 49 | "\tACR := add | change | replace <ACTSPEC>*\n" |
302d3fb7 SH |
50 | "\tGD := get | delete | <ACTISPEC>*\n" |
51 | "\tFL := ls | list | flush | <ACTNAMESPEC>\n" | |
52 | "\tACTNAMESPEC := action <ACTNAME>\n" | |
53 | "\tACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n" | |
54 | "\tACTSPEC := action <ACTDETAIL> [INDEXSPEC]\n" | |
55 | "\tINDEXSPEC := index <32 bit indexvalue>\n" | |
56 | "\tACTDETAIL := <ACTNAME> <ACTPARAMS>\n" | |
4bd62446 | 57 | "\t\tExample ACTNAME is gact, mirred, bpf, etc\n" |
302d3fb7 SH |
58 | "\t\tEach action has its own parameters (ACTPARAMS)\n" |
59 | "\n"); | |
60 | ||
ebf32083 | 61 | exit(-1); |
68d5ba54 SH |
62 | } |
63 | ||
64 | static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt) | |
65 | { | |
66 | if (opt && RTA_PAYLOAD(opt)) | |
ae665a52 | 67 | fprintf(f, "[Unknown action, optlen=%u] ", |
32a121cb | 68 | (unsigned int) RTA_PAYLOAD(opt)); |
68d5ba54 SH |
69 | return 0; |
70 | } | |
71 | ||
ed485691 SH |
72 | static int parse_noaopt(struct action_util *au, int *argc_p, |
73 | char ***argv_p, int code, struct nlmsghdr *n) | |
68d5ba54 SH |
74 | { |
75 | int argc = *argc_p; | |
76 | char **argv = *argv_p; | |
77 | ||
ed485691 SH |
78 | if (argc) |
79 | fprintf(stderr, | |
80 | "Unknown action \"%s\", hence option \"%s\" is unparsable\n", | |
81 | au->id, *argv); | |
82 | else | |
68d5ba54 | 83 | fprintf(stderr, "Unknown action \"%s\"\n", au->id); |
ed485691 | 84 | |
68d5ba54 SH |
85 | return -1; |
86 | } | |
87 | ||
d1f28cf1 | 88 | static struct action_util *get_action_kind(char *str) |
68d5ba54 | 89 | { |
cda4026c | 90 | static void *aBODY; |
68d5ba54 SH |
91 | void *dlh; |
92 | char buf[256]; | |
93 | struct action_util *a; | |
94 | #ifdef CONFIG_GACT | |
95 | int looked4gact = 0; | |
96 | restart_s: | |
97 | #endif | |
98 | for (a = action_list; a; a = a->next) { | |
99 | if (strcmp(a->id, str) == 0) | |
100 | return a; | |
101 | } | |
102 | ||
ab814d63 | 103 | snprintf(buf, sizeof(buf), "%s/m_%s.so", get_tc_lib(), str); |
02833d1b | 104 | dlh = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL); |
68d5ba54 SH |
105 | if (dlh == NULL) { |
106 | dlh = aBODY; | |
107 | if (dlh == NULL) { | |
108 | dlh = aBODY = dlopen(NULL, RTLD_LAZY); | |
109 | if (dlh == NULL) | |
110 | goto noexist; | |
111 | } | |
112 | } | |
113 | ||
95812b56 | 114 | snprintf(buf, sizeof(buf), "%s_action_util", str); |
68d5ba54 SH |
115 | a = dlsym(dlh, buf); |
116 | if (a == NULL) | |
117 | goto noexist; | |
118 | ||
119 | reg: | |
120 | a->next = action_list; | |
121 | action_list = a; | |
122 | return a; | |
123 | ||
124 | noexist: | |
125 | #ifdef CONFIG_GACT | |
126 | if (!looked4gact) { | |
127 | looked4gact = 1; | |
32a121cb | 128 | strcpy(str, "gact"); |
68d5ba54 SH |
129 | goto restart_s; |
130 | } | |
131 | #endif | |
f89bb021 | 132 | a = calloc(1, sizeof(*a)); |
68d5ba54 | 133 | if (a) { |
68d5ba54 SH |
134 | strncpy(a->id, "noact", 15); |
135 | a->parse_aopt = parse_noaopt; | |
136 | a->print_aopt = print_noaopt; | |
137 | goto reg; | |
138 | } | |
139 | return a; | |
140 | } | |
141 | ||
ed485691 | 142 | static bool |
ae665a52 | 143 | new_cmd(char **argv) |
68d5ba54 | 144 | { |
ed485691 | 145 | return (matches(*argv, "change") == 0) || |
e9e9365b SH |
146 | (matches(*argv, "replace") == 0) || |
147 | (matches(*argv, "delete") == 0) || | |
148 | (matches(*argv, "get") == 0) || | |
ed485691 | 149 | (matches(*argv, "add") == 0); |
68d5ba54 SH |
150 | } |
151 | ||
fd8b3d2c | 152 | int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) |
68d5ba54 SH |
153 | { |
154 | int argc = *argc_p; | |
155 | char **argv = *argv_p; | |
156 | struct rtattr *tail, *tail2; | |
b317557f | 157 | char k[FILTER_NAMESZ]; |
fd8b3d2c | 158 | int act_ck_len = 0; |
68d5ba54 SH |
159 | int ok = 0; |
160 | int eap = 0; /* expect action parameters */ | |
161 | ||
162 | int ret = 0; | |
163 | int prio = 0; | |
fd8b3d2c | 164 | unsigned char act_ck[TC_COOKIE_MAX_SIZE]; |
68d5ba54 SH |
165 | |
166 | if (argc <= 0) | |
167 | return -1; | |
168 | ||
c14f9d92 | 169 | tail2 = addattr_nest(n, MAX_MSG, tca_id); |
68d5ba54 SH |
170 | |
171 | while (argc > 0) { | |
172 | ||
32a121cb | 173 | memset(k, 0, sizeof(k)); |
68d5ba54 | 174 | |
32a121cb | 175 | if (strcmp(*argv, "action") == 0) { |
68d5ba54 SH |
176 | argc--; |
177 | argv++; | |
178 | eap = 1; | |
179 | #ifdef CONFIG_GACT | |
ed485691 | 180 | if (!gact_ld) |
68d5ba54 | 181 | get_action_kind("gact"); |
68d5ba54 SH |
182 | #endif |
183 | continue; | |
9282d08d JHS |
184 | } else if (strcmp(*argv, "flowid") == 0) { |
185 | break; | |
186 | } else if (strcmp(*argv, "classid") == 0) { | |
187 | break; | |
68d5ba54 SH |
188 | } else if (strcmp(*argv, "help") == 0) { |
189 | return -1; | |
190 | } else if (new_cmd(argv)) { | |
191 | goto done0; | |
192 | } else { | |
193 | struct action_util *a = NULL; | |
32a121cb | 194 | |
6f7df6b2 PS |
195 | if (!action_a2n(*argv, NULL, false)) |
196 | strncpy(k, "gact", sizeof(k) - 1); | |
197 | else | |
198 | strncpy(k, *argv, sizeof(k) - 1); | |
68d5ba54 | 199 | eap = 0; |
32a121cb | 200 | if (argc > 0) { |
68d5ba54 SH |
201 | a = get_action_kind(k); |
202 | } else { | |
203 | done0: | |
204 | if (ok) | |
205 | break; | |
206 | else | |
207 | goto done; | |
208 | } | |
209 | ||
ed485691 | 210 | if (a == NULL) |
68d5ba54 | 211 | goto bad_val; |
ed485691 | 212 | |
68d5ba54 | 213 | |
c14f9d92 | 214 | tail = addattr_nest(n, MAX_MSG, ++prio); |
68d5ba54 SH |
215 | addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); |
216 | ||
fd8b3d2c JHS |
217 | ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS, |
218 | n); | |
68d5ba54 SH |
219 | |
220 | if (ret < 0) { | |
32a121cb | 221 | fprintf(stderr, "bad action parsing\n"); |
68d5ba54 SH |
222 | goto bad_val; |
223 | } | |
fd8b3d2c JHS |
224 | |
225 | if (*argv && strcmp(*argv, "cookie") == 0) { | |
226 | size_t slen; | |
227 | ||
228 | NEXT_ARG(); | |
229 | slen = strlen(*argv); | |
230 | if (slen > TC_COOKIE_MAX_SIZE * 2) { | |
231 | char cookie_err_m[128]; | |
232 | ||
233 | snprintf(cookie_err_m, 128, | |
234 | "%zd Max allowed size %d", | |
235 | slen, TC_COOKIE_MAX_SIZE*2); | |
236 | invarg(cookie_err_m, *argv); | |
237 | } | |
238 | ||
239 | if (hex2mem(*argv, act_ck, slen / 2) < 0) | |
240 | invarg("cookie must be a hex string\n", | |
241 | *argv); | |
242 | ||
b75e0f6f | 243 | act_ck_len = slen / 2; |
fd8b3d2c JHS |
244 | argc--; |
245 | argv++; | |
246 | } | |
247 | ||
248 | if (act_ck_len) | |
249 | addattr_l(n, MAX_MSG, TCA_ACT_COOKIE, | |
250 | &act_ck, act_ck_len); | |
251 | ||
c14f9d92 | 252 | addattr_nest_end(n, tail); |
68d5ba54 SH |
253 | ok++; |
254 | } | |
68d5ba54 SH |
255 | } |
256 | ||
257 | if (eap > 0) { | |
32a121cb | 258 | fprintf(stderr, "bad action empty %d\n", eap); |
68d5ba54 SH |
259 | goto bad_val; |
260 | } | |
261 | ||
c14f9d92 | 262 | addattr_nest_end(n, tail2); |
68d5ba54 SH |
263 | |
264 | done: | |
265 | *argc_p = argc; | |
266 | *argv_p = argv; | |
267 | return 0; | |
268 | bad_val: | |
ae665a52 | 269 | /* no need to undo things, returning from here should |
ed485691 SH |
270 | * cause enough pain |
271 | */ | |
32a121cb | 272 | fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv); |
68d5ba54 SH |
273 | return -1; |
274 | } | |
275 | ||
fd8b3d2c | 276 | static int tc_print_one_action(FILE *f, struct rtattr *arg) |
68d5ba54 SH |
277 | { |
278 | ||
954de6c7 | 279 | struct rtattr *tb[TCA_ACT_MAX + 1]; |
68d5ba54 SH |
280 | int err = 0; |
281 | struct action_util *a = NULL; | |
282 | ||
283 | if (arg == NULL) | |
284 | return -1; | |
285 | ||
954de6c7 JHS |
286 | parse_rtattr_nested(tb, TCA_ACT_MAX, arg); |
287 | ||
288 | if (tb[TCA_ACT_KIND] == NULL) { | |
68d5ba54 SH |
289 | fprintf(stderr, "NULL Action!\n"); |
290 | return -1; | |
291 | } | |
292 | ||
293 | ||
954de6c7 | 294 | a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND])); |
32a121cb | 295 | if (a == NULL) |
68d5ba54 SH |
296 | return err; |
297 | ||
954de6c7 | 298 | err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]); |
68d5ba54 | 299 | |
32a121cb | 300 | if (err < 0) |
68d5ba54 SH |
301 | return err; |
302 | ||
954de6c7 | 303 | if (show_stats && tb[TCA_ACT_STATS]) { |
c2f60f5c RM |
304 | print_string(PRINT_FP, NULL, "\tAction statistics:", NULL); |
305 | print_string(PRINT_FP, NULL, "%s", _SL_); | |
2704bd62 | 306 | open_json_object("stats"); |
e5879dc6 | 307 | print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL); |
2704bd62 | 308 | close_json_object(); |
c2f60f5c | 309 | print_string(PRINT_FP, NULL, "%s", _SL_); |
68d5ba54 | 310 | } |
abff45b8 JP |
311 | if (tb[TCA_ACT_COOKIE]) { |
312 | int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]); | |
313 | char b1[strsz * 2 + 1]; | |
314 | ||
c2f60f5c | 315 | print_string(PRINT_ANY, "cookie", "\tcookie %s", |
2704bd62 JP |
316 | hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]), |
317 | strsz, b1, sizeof(b1))); | |
c2f60f5c | 318 | print_string(PRINT_FP, NULL, "%s", _SL_); |
abff45b8 | 319 | } |
68d5ba54 SH |
320 | |
321 | return 0; | |
322 | } | |
323 | ||
954de6c7 JHS |
324 | static int |
325 | tc_print_action_flush(FILE *f, const struct rtattr *arg) | |
326 | { | |
327 | ||
328 | struct rtattr *tb[TCA_MAX + 1]; | |
329 | int err = 0; | |
330 | struct action_util *a = NULL; | |
331 | __u32 *delete_count = 0; | |
332 | ||
333 | parse_rtattr_nested(tb, TCA_MAX, arg); | |
334 | ||
335 | if (tb[TCA_KIND] == NULL) { | |
336 | fprintf(stderr, "NULL Action!\n"); | |
337 | return -1; | |
338 | } | |
339 | ||
340 | a = get_action_kind(RTA_DATA(tb[TCA_KIND])); | |
32a121cb | 341 | if (a == NULL) |
954de6c7 JHS |
342 | return err; |
343 | ||
344 | delete_count = RTA_DATA(tb[TCA_FCNT]); | |
32a121cb | 345 | fprintf(f, " %s (%d entries)\n", a->id, *delete_count); |
954de6c7 JHS |
346 | tab_flush = 0; |
347 | return 0; | |
348 | } | |
349 | ||
68d5ba54 | 350 | int |
9e713525 | 351 | tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts) |
68d5ba54 SH |
352 | { |
353 | ||
354 | int i; | |
68d5ba54 SH |
355 | |
356 | if (arg == NULL) | |
357 | return 0; | |
358 | ||
9e713525 JHS |
359 | if (!tot_acts) |
360 | tot_acts = TCA_ACT_MAX_PRIO; | |
361 | ||
362 | struct rtattr *tb[tot_acts + 1]; | |
363 | ||
364 | parse_rtattr_nested(tb, tot_acts, arg); | |
68d5ba54 | 365 | |
954de6c7 JHS |
366 | if (tab_flush && NULL != tb[0] && NULL == tb[1]) |
367 | return tc_print_action_flush(f, tb[0]); | |
68d5ba54 | 368 | |
2704bd62 | 369 | open_json_array(PRINT_JSON, "actions"); |
2fb854d0 | 370 | for (i = 0; i <= tot_acts; i++) { |
68d5ba54 | 371 | if (tb[i]) { |
2704bd62 | 372 | open_json_object(NULL); |
c2f60f5c | 373 | print_string(PRINT_FP, NULL, "%s", _SL_); |
2704bd62 | 374 | print_uint(PRINT_ANY, "order", |
c2f60f5c | 375 | "\taction order %u: ", i); |
32a121cb | 376 | if (tc_print_one_action(f, tb[i]) < 0) { |
2704bd62 JP |
377 | print_string(PRINT_FP, NULL, |
378 | "Error printing action\n", NULL); | |
68d5ba54 | 379 | } |
2704bd62 | 380 | close_json_object(); |
68d5ba54 SH |
381 | } |
382 | ||
383 | } | |
1876ab07 | 384 | close_json_array(PRINT_JSON, NULL); |
68d5ba54 | 385 | |
68d5ba54 SH |
386 | return 0; |
387 | } | |
388 | ||
cd554f2c | 389 | int print_action(struct nlmsghdr *n, void *arg) |
68d5ba54 | 390 | { |
32a121cb | 391 | FILE *fp = (FILE *)arg; |
68d5ba54 SH |
392 | struct tcamsg *t = NLMSG_DATA(n); |
393 | int len = n->nlmsg_len; | |
9e713525 JHS |
394 | __u32 *tot_acts = NULL; |
395 | struct rtattr *tb[TCA_ROOT_MAX+1]; | |
68d5ba54 SH |
396 | |
397 | len -= NLMSG_LENGTH(sizeof(*t)); | |
398 | ||
399 | if (len < 0) { | |
400 | fprintf(stderr, "Wrong len %d\n", len); | |
401 | return -1; | |
402 | } | |
403 | ||
9e713525 JHS |
404 | parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len); |
405 | ||
406 | if (tb[TCA_ROOT_COUNT]) | |
407 | tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]); | |
68d5ba54 | 408 | |
9fd3f0b2 RM |
409 | open_json_object(NULL); |
410 | print_uint(PRINT_ANY, "total acts", "total acts %u", | |
411 | tot_acts ? *tot_acts : 0); | |
c2f60f5c | 412 | print_string(PRINT_FP, NULL, "%s", _SL_); |
9fd3f0b2 | 413 | close_json_object(); |
32a121cb | 414 | if (tb[TCA_ACT_TAB] == NULL) { |
68d5ba54 | 415 | if (n->nlmsg_type != RTM_GETACTION) |
5bec3484 | 416 | fprintf(stderr, "print_action: NULL kind\n"); |
68d5ba54 | 417 | return -1; |
ae665a52 | 418 | } |
68d5ba54 SH |
419 | |
420 | if (n->nlmsg_type == RTM_DELACTION) { | |
421 | if (n->nlmsg_flags & NLM_F_ROOT) { | |
ae665a52 | 422 | fprintf(fp, "Flushed table "); |
68d5ba54 SH |
423 | tab_flush = 1; |
424 | } else { | |
31951c47 | 425 | fprintf(fp, "Deleted action "); |
68d5ba54 SH |
426 | } |
427 | } | |
428 | ||
31951c47 RM |
429 | if (n->nlmsg_type == RTM_NEWACTION) { |
430 | if ((n->nlmsg_flags & NLM_F_CREATE) && | |
431 | !(n->nlmsg_flags & NLM_F_REPLACE)) { | |
432 | fprintf(fp, "Added action "); | |
433 | } else if (n->nlmsg_flags & NLM_F_REPLACE) { | |
434 | fprintf(fp, "Replaced action "); | |
435 | } | |
436 | } | |
9e713525 | 437 | |
0927bf83 | 438 | open_json_object(NULL); |
9e713525 | 439 | tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0); |
0927bf83 | 440 | close_json_object(); |
68d5ba54 SH |
441 | |
442 | return 0; | |
443 | } | |
444 | ||
ed485691 SH |
445 | static int tc_action_gd(int cmd, unsigned int flags, |
446 | int *argc_p, char ***argv_p) | |
68d5ba54 | 447 | { |
b317557f | 448 | char k[FILTER_NAMESZ]; |
68d5ba54 SH |
449 | struct action_util *a = NULL; |
450 | int argc = *argc_p; | |
451 | char **argv = *argv_p; | |
452 | int prio = 0; | |
453 | int ret = 0; | |
9e713525 | 454 | __u32 i = 0; |
68d5ba54 SH |
455 | struct rtattr *tail; |
456 | struct rtattr *tail2; | |
457 | struct nlmsghdr *ans = NULL; | |
458 | ||
459 | struct { | |
460 | struct nlmsghdr n; | |
461 | struct tcamsg t; | |
462 | char buf[MAX_MSG]; | |
a0a73b29 | 463 | } req = { |
d892aaf7 PS |
464 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)), |
465 | .n.nlmsg_flags = NLM_F_REQUEST | flags, | |
466 | .n.nlmsg_type = cmd, | |
a0a73b29 | 467 | .t.tca_family = AF_UNSPEC, |
a0a73b29 | 468 | }; |
68d5ba54 | 469 | |
32a121cb SH |
470 | argc -= 1; |
471 | argv += 1; | |
68d5ba54 SH |
472 | |
473 | ||
c14f9d92 | 474 | tail = addattr_nest(&req.n, MAX_MSG, TCA_ACT_TAB); |
68d5ba54 SH |
475 | |
476 | while (argc > 0) { | |
32a121cb | 477 | if (strcmp(*argv, "action") == 0) { |
68d5ba54 SH |
478 | argc--; |
479 | argv++; | |
480 | continue; | |
481 | } else if (strcmp(*argv, "help") == 0) { | |
482 | return -1; | |
483 | } | |
484 | ||
32a121cb | 485 | strncpy(k, *argv, sizeof(k) - 1); |
68d5ba54 | 486 | a = get_action_kind(k); |
32a121cb SH |
487 | if (a == NULL) { |
488 | fprintf(stderr, "Error: non existent action: %s\n", k); | |
68d5ba54 SH |
489 | ret = -1; |
490 | goto bad_val; | |
491 | } | |
492 | if (strcmp(a->id, k) != 0) { | |
32a121cb | 493 | fprintf(stderr, "Error: non existent action: %s\n", k); |
68d5ba54 SH |
494 | ret = -1; |
495 | goto bad_val; | |
496 | } | |
497 | ||
32a121cb SH |
498 | argc -= 1; |
499 | argv += 1; | |
68d5ba54 | 500 | if (argc <= 0) { |
ed485691 SH |
501 | fprintf(stderr, |
502 | "Error: no index specified action: %s\n", k); | |
68d5ba54 SH |
503 | ret = -1; |
504 | goto bad_val; | |
505 | } | |
506 | ||
507 | if (matches(*argv, "index") == 0) { | |
508 | NEXT_ARG(); | |
509 | if (get_u32(&i, *argv, 10)) { | |
510 | fprintf(stderr, "Illegal \"index\"\n"); | |
511 | ret = -1; | |
512 | goto bad_val; | |
513 | } | |
32a121cb SH |
514 | argc -= 1; |
515 | argv += 1; | |
68d5ba54 | 516 | } else { |
ed485691 SH |
517 | fprintf(stderr, |
518 | "Error: no index specified action: %s\n", k); | |
68d5ba54 SH |
519 | ret = -1; |
520 | goto bad_val; | |
521 | } | |
522 | ||
c14f9d92 | 523 | tail2 = addattr_nest(&req.n, MAX_MSG, ++prio); |
4a86fe19 | 524 | addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); |
9e713525 JHS |
525 | if (i > 0) |
526 | addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); | |
c14f9d92 | 527 | addattr_nest_end(&req.n, tail2); |
68d5ba54 SH |
528 | |
529 | } | |
530 | ||
c14f9d92 | 531 | addattr_nest_end(&req.n, tail); |
68d5ba54 | 532 | |
68d5ba54 | 533 | req.n.nlmsg_seq = rth.dump = ++rth.seq; |
c1027a75 | 534 | |
83cf5bc7 | 535 | if (rtnl_talk(&rth, &req.n, cmd == RTM_DELACTION ? NULL : &ans) < 0) { |
68d5ba54 | 536 | fprintf(stderr, "We have an error talking to the kernel\n"); |
52f337c1 | 537 | return 1; |
68d5ba54 SH |
538 | } |
539 | ||
9fd3f0b2 RM |
540 | if (cmd == RTM_GETACTION) { |
541 | new_json_obj(json); | |
cd554f2c | 542 | ret = print_action(ans, stdout); |
9fd3f0b2 RM |
543 | if (ret < 0) { |
544 | fprintf(stderr, "Dump terminated\n"); | |
545 | free(ans); | |
546 | delete_json_obj(); | |
547 | return 1; | |
548 | } | |
549 | delete_json_obj(); | |
68d5ba54 | 550 | } |
86bf43c7 | 551 | free(ans); |
68d5ba54 SH |
552 | |
553 | *argc_p = argc; | |
554 | *argv_p = argv; | |
68d5ba54 SH |
555 | bad_val: |
556 | return ret; | |
557 | } | |
558 | ||
485d0c60 CM |
559 | struct tc_action_req { |
560 | struct nlmsghdr n; | |
561 | struct tcamsg t; | |
562 | char buf[MAX_MSG]; | |
563 | }; | |
564 | ||
ed485691 | 565 | static int tc_action_modify(int cmd, unsigned int flags, |
485d0c60 CM |
566 | int *argc_p, char ***argv_p, |
567 | void *buf, size_t buflen) | |
68d5ba54 | 568 | { |
485d0c60 | 569 | struct tc_action_req *req, action_req; |
68d5ba54 | 570 | char **argv = *argv_p; |
485d0c60 CM |
571 | struct rtattr *tail; |
572 | int argc = *argc_p; | |
573 | struct iovec iov; | |
68d5ba54 | 574 | int ret = 0; |
485d0c60 CM |
575 | |
576 | if (buf) { | |
577 | req = buf; | |
578 | if (buflen < sizeof (struct tc_action_req)) { | |
579 | fprintf(stderr, "buffer is too small: %zu\n", buflen); | |
580 | return -1; | |
581 | } | |
582 | } else { | |
583 | memset(&action_req, 0, sizeof (struct tc_action_req)); | |
584 | req = &action_req; | |
585 | } | |
586 | ||
587 | req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); | |
588 | req->n.nlmsg_flags = NLM_F_REQUEST | flags; | |
589 | req->n.nlmsg_type = cmd; | |
590 | req->t.tca_family = AF_UNSPEC; | |
591 | tail = NLMSG_TAIL(&req->n); | |
68d5ba54 | 592 | |
32a121cb SH |
593 | argc -= 1; |
594 | argv += 1; | |
485d0c60 | 595 | if (parse_action(&argc, &argv, TCA_ACT_TAB, &req->n)) { |
68d5ba54 SH |
596 | fprintf(stderr, "Illegal \"action\"\n"); |
597 | return -1; | |
598 | } | |
485d0c60 CM |
599 | tail->rta_len = (void *) NLMSG_TAIL(&req->n) - (void *) tail; |
600 | ||
601 | *argc_p = argc; | |
602 | *argv_p = argv; | |
603 | ||
604 | if (buf) | |
605 | return 0; | |
68d5ba54 | 606 | |
485d0c60 CM |
607 | iov.iov_base = &req->n; |
608 | iov.iov_len = req->n.nlmsg_len; | |
609 | if (rtnl_talk_iov(&rth, &iov, 1, NULL) < 0) { | |
68d5ba54 SH |
610 | fprintf(stderr, "We have an error talking to the kernel\n"); |
611 | ret = -1; | |
612 | } | |
613 | ||
68d5ba54 SH |
614 | return ret; |
615 | } | |
616 | ||
9e713525 | 617 | static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event) |
68d5ba54 | 618 | { |
9e713525 | 619 | struct rtattr *tail, *tail2, *tail3, *tail4; |
68d5ba54 | 620 | int ret = 0, prio = 0, msg_size = 0; |
68d5ba54 | 621 | struct action_util *a = NULL; |
9e713525 JHS |
622 | struct nla_bitfield32 flag_select = { 0 }; |
623 | char **argv = *argv_p; | |
624 | __u32 msec_since = 0; | |
625 | int argc = *argc_p; | |
b317557f | 626 | char k[FILTER_NAMESZ]; |
68d5ba54 SH |
627 | struct { |
628 | struct nlmsghdr n; | |
629 | struct tcamsg t; | |
630 | char buf[MAX_MSG]; | |
a0a73b29 PS |
631 | } req = { |
632 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)), | |
633 | .t.tca_family = AF_UNSPEC, | |
a0a73b29 | 634 | }; |
68d5ba54 | 635 | |
c14f9d92 | 636 | tail = addattr_nest(&req.n, MAX_MSG, TCA_ACT_TAB); |
4a86fe19 | 637 | tail2 = NLMSG_TAIL(&req.n); |
68d5ba54 | 638 | |
32a121cb | 639 | strncpy(k, *argv, sizeof(k) - 1); |
68d5ba54 | 640 | #ifdef CONFIG_GACT |
ed485691 | 641 | if (!gact_ld) |
68d5ba54 | 642 | get_action_kind("gact"); |
ed485691 | 643 | |
68d5ba54 SH |
644 | #endif |
645 | a = get_action_kind(k); | |
32a121cb SH |
646 | if (a == NULL) { |
647 | fprintf(stderr, "bad action %s\n", k); | |
68d5ba54 SH |
648 | goto bad_val; |
649 | } | |
650 | if (strcmp(a->id, k) != 0) { | |
32a121cb | 651 | fprintf(stderr, "bad action %s\n", k); |
68d5ba54 SH |
652 | goto bad_val; |
653 | } | |
32a121cb | 654 | strncpy(k, *argv, sizeof(k) - 1); |
68d5ba54 | 655 | |
9e713525 JHS |
656 | argc -= 1; |
657 | argv += 1; | |
658 | ||
659 | if (argc && (strcmp(*argv, "since") == 0)) { | |
660 | NEXT_ARG(); | |
661 | if (get_u32(&msec_since, *argv, 0)) | |
662 | invarg("dump time \"since\" is invalid", *argv); | |
663 | } | |
664 | ||
68d5ba54 SH |
665 | addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); |
666 | addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); | |
4a86fe19 | 667 | tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; |
c14f9d92 | 668 | addattr_nest_end(&req.n, tail); |
68d5ba54 | 669 | |
9e713525 JHS |
670 | tail3 = NLMSG_TAIL(&req.n); |
671 | flag_select.value |= TCA_FLAG_LARGE_DUMP_ON; | |
672 | flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON; | |
673 | addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select, | |
674 | sizeof(struct nla_bitfield32)); | |
675 | tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3; | |
676 | if (msec_since) { | |
677 | tail4 = NLMSG_TAIL(&req.n); | |
678 | addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since); | |
679 | tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4; | |
680 | } | |
ed485691 SH |
681 | msg_size = NLMSG_ALIGN(req.n.nlmsg_len) |
682 | - NLMSG_ALIGN(sizeof(struct nlmsghdr)); | |
68d5ba54 | 683 | |
ae665a52 | 684 | if (event == RTM_GETACTION) { |
ed485691 SH |
685 | if (rtnl_dump_request(&rth, event, |
686 | (void *)&req.t, msg_size) < 0) { | |
68d5ba54 | 687 | perror("Cannot send dump request"); |
52f337c1 | 688 | return 1; |
68d5ba54 | 689 | } |
9fd3f0b2 | 690 | new_json_obj(json); |
cd70f3f5 | 691 | ret = rtnl_dump_filter(&rth, print_action, stdout); |
9fd3f0b2 | 692 | delete_json_obj(); |
68d5ba54 SH |
693 | } |
694 | ||
ae665a52 | 695 | if (event == RTM_DELACTION) { |
68d5ba54 SH |
696 | req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); |
697 | req.n.nlmsg_type = RTM_DELACTION; | |
698 | req.n.nlmsg_flags |= NLM_F_ROOT; | |
699 | req.n.nlmsg_flags |= NLM_F_REQUEST; | |
86bf43c7 | 700 | if (rtnl_talk(&rth, &req.n, NULL) < 0) { |
68d5ba54 | 701 | fprintf(stderr, "We have an error flushing\n"); |
52f337c1 | 702 | return 1; |
68d5ba54 SH |
703 | } |
704 | ||
705 | } | |
706 | ||
707 | bad_val: | |
708 | ||
9e713525 JHS |
709 | *argc_p = argc; |
710 | *argv_p = argv; | |
68d5ba54 SH |
711 | return ret; |
712 | } | |
713 | ||
485d0c60 | 714 | int do_action(int argc, char **argv, void *buf, size_t buflen) |
68d5ba54 SH |
715 | { |
716 | ||
717 | int ret = 0; | |
718 | ||
719 | while (argc > 0) { | |
720 | ||
721 | if (matches(*argv, "add") == 0) { | |
ed485691 SH |
722 | ret = tc_action_modify(RTM_NEWACTION, |
723 | NLM_F_EXCL | NLM_F_CREATE, | |
485d0c60 | 724 | &argc, &argv, buf, buflen); |
68d5ba54 SH |
725 | } else if (matches(*argv, "change") == 0 || |
726 | matches(*argv, "replace") == 0) { | |
ed485691 SH |
727 | ret = tc_action_modify(RTM_NEWACTION, |
728 | NLM_F_CREATE | NLM_F_REPLACE, | |
485d0c60 | 729 | &argc, &argv, buf, buflen); |
68d5ba54 | 730 | } else if (matches(*argv, "delete") == 0) { |
32a121cb SH |
731 | argc -= 1; |
732 | argv += 1; | |
68d5ba54 SH |
733 | ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv); |
734 | } else if (matches(*argv, "get") == 0) { | |
32a121cb SH |
735 | argc -= 1; |
736 | argv += 1; | |
68d5ba54 | 737 | ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv); |
ed485691 SH |
738 | } else if (matches(*argv, "list") == 0 || |
739 | matches(*argv, "show") == 0 || | |
740 | matches(*argv, "lst") == 0) { | |
68d5ba54 SH |
741 | if (argc <= 2) { |
742 | act_usage(); | |
743 | return -1; | |
744 | } | |
9e713525 JHS |
745 | |
746 | argc -= 2; | |
747 | argv += 2; | |
748 | return tc_act_list_or_flush(&argc, &argv, | |
749 | RTM_GETACTION); | |
68d5ba54 SH |
750 | } else if (matches(*argv, "flush") == 0) { |
751 | if (argc <= 2) { | |
752 | act_usage(); | |
753 | return -1; | |
754 | } | |
9e713525 JHS |
755 | |
756 | argc -= 2; | |
757 | argv += 2; | |
758 | return tc_act_list_or_flush(&argc, &argv, | |
759 | RTM_DELACTION); | |
68d5ba54 SH |
760 | } else if (matches(*argv, "help") == 0) { |
761 | act_usage(); | |
762 | return -1; | |
763 | } else { | |
ed485691 SH |
764 | fprintf(stderr, |
765 | "Command \"%s\" is unknown, try \"tc actions help\".\n", | |
766 | *argv); | |
68d5ba54 SH |
767 | return -1; |
768 | } | |
6fcf36c9 WC |
769 | |
770 | if (ret < 0) | |
771 | return -1; | |
68d5ba54 SH |
772 | } |
773 | ||
774 | return 0; | |
775 | } |