]>
Commit | Line | Data |
---|---|---|
7e7c7372 | 1 | /* |
ae665a52 | 2 | * m_pedit.c generic packet editor actions module |
7e7c7372 | 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 | * | |
11 | * TODO: | |
3d0b7439 SH |
12 | * 1) Big endian broken in some spots |
13 | * 2) A lot of this stuff was added on the fly; get a big double-double | |
14 | * and clean it up at some point. | |
ae665a52 | 15 | * |
7e7c7372 | 16 | */ |
17 | ||
18 | #include <stdio.h> | |
19 | #include <stdlib.h> | |
20 | #include <unistd.h> | |
7e7c7372 | 21 | #include <fcntl.h> |
22 | #include <sys/socket.h> | |
23 | #include <netinet/in.h> | |
24 | #include <arpa/inet.h> | |
25 | #include <string.h> | |
26 | #include <dlfcn.h> | |
27 | #include "utils.h" | |
28 | #include "tc_util.h" | |
29 | #include "m_pedit.h" | |
3cd5149e | 30 | #include "rt_names.h" |
7e7c7372 | 31 | |
7e7c7372 | 32 | static struct m_pedit_util *pedit_list; |
1c9af050 | 33 | static int pedit_debug; |
7e7c7372 | 34 | |
d8694a30 | 35 | static void explain(void) |
7e7c7372 | 36 | { |
7e7c7372 | 37 | fprintf(stderr, |
8589eb4e | 38 | "Usage: ... pedit munge [ex] <MUNGE> [CONTROL]\n" |
ae665a52 | 39 | "Where: MUNGE := <RAW>|<LAYERED>\n" |
8b625177 SH |
40 | "\t<RAW>:= <OFFSETC>[ATC]<CMD>\n \t\tOFFSETC:= offset <offval> <u8|u16|u32>\n" |
41 | "\t\tATC:= at <atval> offmask <maskval> shift <shiftval>\n" | |
42 | "\t\tNOTE: offval is byte offset, must be multiple of 4\n" | |
51536ebb | 43 | "\t\tNOTE: maskval is a 32 bit hex number\n \t\tNOTE: shiftval is a shift value\n" |
c05ddaf9 | 44 | "\t\tCMD:= clear | invert | set <setval>| add <addval> | retain\n" |
8b625177 | 45 | "\t<LAYERED>:= ip <ipdata> | ip6 <ip6data>\n" |
32a121cb | 46 | " \t\t| udp <udpdata> | tcp <tcpdata> | icmp <icmpdata>\n" |
d19f72f7 JP |
47 | "\tCONTROL:= reclassify | pipe | drop | continue | pass |\n" |
48 | "\t goto chain <CHAIN_INDEX>\n" | |
7c71a40c | 49 | "\tNOTE: if 'ex' is set, extended functionality will be supported (kernel >= 4.11)\n" |
ebf32083 | 50 | "For Example usage look at the examples directory\n"); |
7e7c7372 | 51 | |
52 | } | |
53 | ||
d8694a30 | 54 | static void usage(void) |
ebf32083 JHS |
55 | { |
56 | explain(); | |
57 | exit(-1); | |
58 | } | |
7e7c7372 | 59 | |
d8694a30 | 60 | static int pedit_parse_nopopt(int *argc_p, char ***argv_p, |
7c71a40c AV |
61 | struct m_pedit_sel *sel, |
62 | struct m_pedit_key *tkey) | |
7e7c7372 | 63 | { |
64 | int argc = *argc_p; | |
65 | char **argv = *argv_p; | |
66 | ||
67 | if (argc) { | |
d8694a30 JHS |
68 | fprintf(stderr, |
69 | "Unknown action hence option \"%s\" is unparsable\n", | |
70 | *argv); | |
71 | return -1; | |
7e7c7372 | 72 | } |
73 | ||
74 | return 0; | |
75 | ||
76 | } | |
77 | ||
d1f28cf1 | 78 | static struct m_pedit_util *get_pedit_kind(const char *str) |
7e7c7372 | 79 | { |
fe102f61 | 80 | static void *pBODY; |
7e7c7372 | 81 | void *dlh; |
82 | char buf[256]; | |
d8694a30 | 83 | struct m_pedit_util *p; |
7e7c7372 | 84 | |
85 | for (p = pedit_list; p; p = p->next) { | |
86 | if (strcmp(p->id, str) == 0) | |
87 | return p; | |
88 | } | |
89 | ||
90 | snprintf(buf, sizeof(buf), "p_%s.so", str); | |
91 | dlh = dlopen(buf, RTLD_LAZY); | |
92 | if (dlh == NULL) { | |
93 | dlh = pBODY; | |
94 | if (dlh == NULL) { | |
95 | dlh = pBODY = dlopen(NULL, RTLD_LAZY); | |
96 | if (dlh == NULL) | |
97 | goto noexist; | |
98 | } | |
99 | } | |
100 | ||
101 | snprintf(buf, sizeof(buf), "p_pedit_%s", str); | |
102 | p = dlsym(dlh, buf); | |
103 | if (p == NULL) | |
104 | goto noexist; | |
105 | ||
106 | reg: | |
107 | p->next = pedit_list; | |
108 | pedit_list = p; | |
109 | return p; | |
110 | ||
111 | noexist: | |
f89bb021 | 112 | p = calloc(1, sizeof(*p)); |
7e7c7372 | 113 | if (p) { |
95744efa | 114 | strlcpy(p->id, str, sizeof(p->id)); |
7e7c7372 | 115 | p->parse_peopt = pedit_parse_nopopt; |
116 | goto reg; | |
117 | } | |
118 | return p; | |
119 | } | |
120 | ||
7527b221 | 121 | static int pack_key(struct m_pedit_sel *_sel, struct m_pedit_key *tkey) |
7e7c7372 | 122 | { |
7c71a40c AV |
123 | struct tc_pedit_sel *sel = &_sel->sel; |
124 | struct m_pedit_key_ex *keys_ex = _sel->keys_ex; | |
7e7c7372 | 125 | int hwm = sel->nkeys; |
126 | ||
127 | if (hwm >= MAX_OFFS) | |
128 | return -1; | |
129 | ||
130 | if (tkey->off % 4) { | |
131 | fprintf(stderr, "offsets MUST be in 32 bit boundaries\n"); | |
132 | return -1; | |
133 | } | |
134 | ||
135 | sel->keys[hwm].val = tkey->val; | |
136 | sel->keys[hwm].mask = tkey->mask; | |
137 | sel->keys[hwm].off = tkey->off; | |
138 | sel->keys[hwm].at = tkey->at; | |
139 | sel->keys[hwm].offmask = tkey->offmask; | |
140 | sel->keys[hwm].shift = tkey->shift; | |
7c71a40c AV |
141 | |
142 | if (_sel->extended) { | |
143 | keys_ex[hwm].htype = tkey->htype; | |
144 | keys_ex[hwm].cmd = tkey->cmd; | |
145 | } else { | |
146 | if (tkey->htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK || | |
147 | tkey->cmd != TCA_PEDIT_KEY_EX_CMD_SET) { | |
1e600da0 | 148 | fprintf(stderr, |
290cdc05 | 149 | "Munge parameters not supported. Use 'pedit ex munge ...'.\n"); |
7c71a40c AV |
150 | return -1; |
151 | } | |
152 | } | |
153 | ||
7e7c7372 | 154 | sel->nkeys++; |
155 | return 0; | |
156 | } | |
157 | ||
7527b221 SH |
158 | static int pack_key32(__u32 retain, struct m_pedit_sel *sel, |
159 | struct m_pedit_key *tkey) | |
7e7c7372 | 160 | { |
161 | if (tkey->off > (tkey->off & ~3)) { | |
162 | fprintf(stderr, | |
163 | "pack_key32: 32 bit offsets must begin in 32bit boundaries\n"); | |
164 | return -1; | |
165 | } | |
166 | ||
167 | tkey->val = htonl(tkey->val & retain); | |
168 | tkey->mask = htonl(tkey->mask | ~retain); | |
32a121cb | 169 | return pack_key(sel, tkey); |
7e7c7372 | 170 | } |
171 | ||
7527b221 SH |
172 | static int pack_key16(__u32 retain, struct m_pedit_sel *sel, |
173 | struct m_pedit_key *tkey) | |
7e7c7372 | 174 | { |
0bbca042 | 175 | int ind, stride; |
d8694a30 | 176 | __u32 m[4] = { 0x0000FFFF, 0xFF0000FF, 0xFFFF0000 }; |
7e7c7372 | 177 | |
7e7c7372 | 178 | if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) { |
179 | fprintf(stderr, "pack_key16 bad value\n"); | |
180 | return -1; | |
181 | } | |
182 | ||
183 | ind = tkey->off & 3; | |
184 | ||
0bbca042 | 185 | if (ind == 3) { |
32a121cb | 186 | fprintf(stderr, "pack_key16 bad index value %d\n", ind); |
7e7c7372 | 187 | return -1; |
188 | } | |
189 | ||
77bed404 PS |
190 | stride = 8 * (2 - ind); |
191 | tkey->val = htonl((tkey->val & retain) << stride); | |
192 | tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]); | |
7e7c7372 | 193 | |
194 | tkey->off &= ~3; | |
195 | ||
196 | if (pedit_debug) | |
d8694a30 JHS |
197 | printf("pack_key16: Final val %08x mask %08x\n", |
198 | tkey->val, tkey->mask); | |
32a121cb | 199 | return pack_key(sel, tkey); |
7e7c7372 | 200 | } |
201 | ||
7527b221 SH |
202 | static int pack_key8(__u32 retain, struct m_pedit_sel *sel, |
203 | struct m_pedit_key *tkey) | |
7e7c7372 | 204 | { |
0bbca042 | 205 | int ind, stride; |
d8694a30 | 206 | __u32 m[4] = { 0x00FFFFFF, 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00 }; |
7e7c7372 | 207 | |
7e7c7372 | 208 | if (tkey->val > 0xFF || tkey->mask > 0xFF) { |
d8694a30 JHS |
209 | fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", |
210 | tkey->val, tkey->mask); | |
7e7c7372 | 211 | return -1; |
212 | } | |
213 | ||
214 | ind = tkey->off & 3; | |
0bbca042 | 215 | |
77bed404 PS |
216 | stride = 8 * (3 - ind); |
217 | tkey->val = htonl((tkey->val & retain) << stride); | |
218 | tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]); | |
0bbca042 | 219 | |
7e7c7372 | 220 | tkey->off &= ~3; |
ae665a52 | 221 | |
7e7c7372 | 222 | if (pedit_debug) |
d8694a30 JHS |
223 | printf("pack_key8: Final word off %d val %08x mask %08x\n", |
224 | tkey->off, tkey->val, tkey->mask); | |
32a121cb | 225 | return pack_key(sel, tkey); |
7e7c7372 | 226 | } |
227 | ||
3cd5149e AV |
228 | static int pack_mac(struct m_pedit_sel *sel, struct m_pedit_key *tkey, |
229 | __u8 *mac) | |
230 | { | |
231 | int ret = 0; | |
232 | ||
233 | if (!(tkey->off & 0x3)) { | |
234 | tkey->mask = 0; | |
235 | tkey->val = ntohl(*((__u32 *)mac)); | |
236 | ret |= pack_key32(~0, sel, tkey); | |
237 | ||
238 | tkey->off += 4; | |
239 | tkey->mask = 0; | |
240 | tkey->val = ntohs(*((__u16 *)&mac[4])); | |
241 | ret |= pack_key16(~0, sel, tkey); | |
242 | } else if (!(tkey->off & 0x1)) { | |
243 | tkey->mask = 0; | |
244 | tkey->val = ntohs(*((__u16 *)mac)); | |
245 | ret |= pack_key16(~0, sel, tkey); | |
246 | ||
247 | tkey->off += 4; | |
248 | tkey->mask = 0; | |
249 | tkey->val = ntohl(*((__u32 *)(mac + 2))); | |
250 | ret |= pack_key32(~0, sel, tkey); | |
251 | } else { | |
252 | fprintf(stderr, | |
253 | "pack_mac: mac offsets must begin in 32bit or 16bit boundaries\n"); | |
254 | return -1; | |
255 | } | |
256 | ||
257 | return ret; | |
258 | } | |
259 | ||
f3e1b244 AV |
260 | static int pack_ipv6(struct m_pedit_sel *sel, struct m_pedit_key *tkey, |
261 | __u32 *ipv6) | |
262 | { | |
263 | int ret = 0; | |
264 | int i; | |
265 | ||
266 | if (tkey->off & 0x3) { | |
267 | fprintf(stderr, | |
268 | "pack_ipv6: IPv6 offsets must begin in 32bit boundaries\n"); | |
269 | return -1; | |
270 | } | |
271 | ||
272 | for (i = 0; i < 4; i++) { | |
273 | tkey->mask = 0; | |
274 | tkey->val = ntohl(ipv6[i]); | |
275 | ||
276 | ret = pack_key32(~0, sel, tkey); | |
277 | if (ret) | |
278 | return ret; | |
279 | ||
280 | tkey->off += 4; | |
281 | } | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
7527b221 | 286 | static int parse_val(int *argc_p, char ***argv_p, __u32 *val, int type) |
7e7c7372 | 287 | { |
288 | int argc = *argc_p; | |
289 | char **argv = *argv_p; | |
290 | ||
291 | if (argc <= 0) | |
292 | return -1; | |
293 | ||
32a121cb | 294 | if (type == TINT) |
d8694a30 | 295 | return get_integer((int *)val, *argv, 0); |
f332d169 | 296 | |
32a121cb | 297 | if (type == TU32) |
7e7c7372 | 298 | return get_u32(val, *argv, 0); |
f332d169 | 299 | |
32a121cb | 300 | if (type == TIPV4) { |
7e7c7372 | 301 | inet_prefix addr; |
32a121cb | 302 | |
8b625177 | 303 | if (get_prefix_1(&addr, *argv, AF_INET)) |
7e7c7372 | 304 | return -1; |
8b625177 | 305 | |
32a121cb | 306 | *val = addr.data[0]; |
7e7c7372 | 307 | return 0; |
308 | } | |
8b625177 | 309 | |
f3e1b244 AV |
310 | if (type == TIPV6) { |
311 | inet_prefix addr; | |
312 | ||
313 | if (get_prefix_1(&addr, *argv, AF_INET6)) | |
314 | return -1; | |
315 | ||
316 | memcpy(val, addr.data, addr.bytelen); | |
317 | ||
318 | return 0; | |
319 | } | |
7e7c7372 | 320 | |
3cd5149e AV |
321 | if (type == TMAC) { |
322 | #define MAC_ALEN 6 | |
323 | int ret = ll_addr_a2n((char *)val, MAC_ALEN, *argv); | |
324 | ||
325 | if (ret == MAC_ALEN) | |
326 | return 0; | |
327 | } | |
328 | ||
7e7c7372 | 329 | return -1; |
330 | } | |
331 | ||
d8694a30 | 332 | int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type, __u32 retain, |
7c71a40c | 333 | struct m_pedit_sel *sel, struct m_pedit_key *tkey) |
7e7c7372 | 334 | { |
fa4652ff AV |
335 | __u32 mask[4] = { 0 }; |
336 | __u32 val[4] = { 0 }; | |
337 | __u32 *m = &mask[0]; | |
338 | __u32 *v = &val[0]; | |
7e7c7372 | 339 | __u32 o = 0xFF; |
340 | int res = -1; | |
341 | int argc = *argc_p; | |
342 | char **argv = *argv_p; | |
343 | ||
344 | if (argc <= 0) | |
345 | return -1; | |
346 | ||
347 | if (pedit_debug) | |
d8694a30 JHS |
348 | printf("parse_cmd argc %d %s offset %d length %d\n", |
349 | argc, *argv, tkey->off, len); | |
7e7c7372 | 350 | |
351 | if (len == 2) | |
352 | o = 0xFFFF; | |
353 | if (len == 4) | |
354 | o = 0xFFFFFFFF; | |
355 | ||
356 | if (matches(*argv, "invert") == 0) { | |
fa4652ff | 357 | *v = *m = o; |
c05ddaf9 AV |
358 | } else if (matches(*argv, "set") == 0 || |
359 | matches(*argv, "add") == 0) { | |
360 | if (matches(*argv, "add") == 0) | |
361 | tkey->cmd = TCA_PEDIT_KEY_EX_CMD_ADD; | |
362 | ||
363 | if (!sel->extended && tkey->cmd) { | |
1e600da0 SH |
364 | fprintf(stderr, |
365 | "Non extended mode. only 'set' command is supported\n"); | |
c05ddaf9 AV |
366 | return -1; |
367 | } | |
368 | ||
7e7c7372 | 369 | NEXT_ARG(); |
fa4652ff | 370 | if (parse_val(&argc, &argv, val, type)) |
7e7c7372 | 371 | return -1; |
372 | } else if (matches(*argv, "preserve") == 0) { | |
f440e9d8 | 373 | retain = 0; |
7e7c7372 | 374 | } else { |
ae665a52 | 375 | if (matches(*argv, "clear") != 0) |
7e7c7372 | 376 | return -1; |
377 | } | |
378 | ||
d8694a30 JHS |
379 | argc--; |
380 | argv++; | |
7e7c7372 | 381 | |
382 | if (argc && matches(*argv, "retain") == 0) { | |
383 | NEXT_ARG(); | |
384 | if (parse_val(&argc, &argv, &retain, TU32)) | |
385 | return -1; | |
d8694a30 JHS |
386 | argc--; |
387 | argv++; | |
7e7c7372 | 388 | } |
389 | ||
cdca1918 AV |
390 | if (len > 4 && retain != ~0) { |
391 | fprintf(stderr, | |
392 | "retain is not supported for fields longer the 32 bits\n"); | |
393 | return -1; | |
394 | } | |
395 | ||
3cd5149e AV |
396 | if (type == TMAC) { |
397 | res = pack_mac(sel, tkey, (__u8 *)val); | |
398 | goto done; | |
399 | } | |
400 | ||
f3e1b244 AV |
401 | if (type == TIPV6) { |
402 | res = pack_ipv6(sel, tkey, val); | |
403 | goto done; | |
404 | } | |
405 | ||
fa4652ff AV |
406 | tkey->val = *v; |
407 | tkey->mask = *m; | |
7e7c7372 | 408 | |
77bed404 PS |
409 | if (type == TIPV4) |
410 | tkey->val = ntohl(tkey->val); | |
411 | ||
7e7c7372 | 412 | if (len == 1) { |
32a121cb | 413 | res = pack_key8(retain, sel, tkey); |
7e7c7372 | 414 | goto done; |
415 | } | |
416 | if (len == 2) { | |
32a121cb | 417 | res = pack_key16(retain, sel, tkey); |
7e7c7372 | 418 | goto done; |
419 | } | |
420 | if (len == 4) { | |
32a121cb | 421 | res = pack_key32(retain, sel, tkey); |
7e7c7372 | 422 | goto done; |
423 | } | |
424 | ||
425 | return -1; | |
426 | done: | |
427 | if (pedit_debug) | |
d8694a30 JHS |
428 | printf("parse_cmd done argc %d %s offset %d length %d\n", |
429 | argc, *argv, tkey->off, len); | |
7e7c7372 | 430 | *argc_p = argc; |
431 | *argv_p = argv; | |
432 | return res; | |
433 | ||
434 | } | |
435 | ||
7527b221 SH |
436 | static int parse_offset(int *argc_p, char ***argv_p, struct m_pedit_sel *sel, |
437 | struct m_pedit_key *tkey) | |
7e7c7372 | 438 | { |
439 | int off; | |
440 | __u32 len, retain; | |
441 | int argc = *argc_p; | |
442 | char **argv = *argv_p; | |
443 | int res = -1; | |
444 | ||
445 | if (argc <= 0) | |
446 | return -1; | |
447 | ||
448 | if (get_integer(&off, *argv, 0)) | |
449 | return -1; | |
450 | tkey->off = off; | |
451 | ||
452 | argc--; | |
453 | argv++; | |
454 | ||
455 | if (argc <= 0) | |
456 | return -1; | |
457 | ||
7e7c7372 | 458 | if (matches(*argv, "u32") == 0) { |
459 | len = 4; | |
460 | retain = 0xFFFFFFFF; | |
461 | goto done; | |
462 | } | |
463 | if (matches(*argv, "u16") == 0) { | |
464 | len = 2; | |
a33786b5 | 465 | retain = 0xffff; |
7e7c7372 | 466 | goto done; |
467 | } | |
468 | if (matches(*argv, "u8") == 0) { | |
469 | len = 1; | |
a33786b5 | 470 | retain = 0xff; |
7e7c7372 | 471 | goto done; |
472 | } | |
473 | ||
474 | return -1; | |
475 | ||
476 | done: | |
477 | ||
478 | NEXT_ARG(); | |
479 | ||
480 | /* [at <someval> offmask <maskval> shift <shiftval>] */ | |
481 | if (matches(*argv, "at") == 0) { | |
482 | ||
32a121cb | 483 | __u32 atv = 0, offmask = 0x0, shift = 0; |
7e7c7372 | 484 | |
485 | NEXT_ARG(); | |
486 | if (get_u32(&atv, *argv, 0)) | |
487 | return -1; | |
488 | tkey->at = atv; | |
489 | ||
490 | NEXT_ARG(); | |
ae665a52 | 491 | |
7e7c7372 | 492 | if (get_u32(&offmask, *argv, 16)) |
493 | return -1; | |
494 | tkey->offmask = offmask; | |
495 | ||
496 | NEXT_ARG(); | |
497 | ||
498 | if (get_u32(&shift, *argv, 0)) | |
499 | return -1; | |
500 | tkey->shift = shift; | |
501 | ||
502 | NEXT_ARG(); | |
503 | } | |
504 | ||
32a121cb | 505 | res = parse_cmd(&argc, &argv, len, TU32, retain, sel, tkey); |
7e7c7372 | 506 | |
507 | *argc_p = argc; | |
508 | *argv_p = argv; | |
509 | return res; | |
510 | } | |
511 | ||
7c71a40c | 512 | static int parse_munge(int *argc_p, char ***argv_p, struct m_pedit_sel *sel) |
7e7c7372 | 513 | { |
7c71a40c | 514 | struct m_pedit_key tkey = {}; |
7e7c7372 | 515 | int argc = *argc_p; |
516 | char **argv = *argv_p; | |
517 | int res = -1; | |
518 | ||
519 | if (argc <= 0) | |
520 | return -1; | |
521 | ||
7e7c7372 | 522 | if (matches(*argv, "offset") == 0) { |
523 | NEXT_ARG(); | |
32a121cb | 524 | res = parse_offset(&argc, &argv, sel, &tkey); |
7e7c7372 | 525 | goto done; |
7e7c7372 | 526 | } else { |
b317557f | 527 | char k[FILTER_NAMESZ]; |
7e7c7372 | 528 | struct m_pedit_util *p = NULL; |
529 | ||
32a121cb | 530 | strncpy(k, *argv, sizeof(k) - 1); |
7e7c7372 | 531 | |
32a121cb | 532 | if (argc > 0) { |
7e7c7372 | 533 | p = get_pedit_kind(k); |
32a121cb | 534 | if (p == NULL) |
7e7c7372 | 535 | goto bad_val; |
ec0ceeec | 536 | NEXT_ARG(); |
32a121cb | 537 | res = p->parse_peopt(&argc, &argv, sel, &tkey); |
7e7c7372 | 538 | if (res < 0) { |
32a121cb | 539 | fprintf(stderr, "bad pedit parsing\n"); |
7e7c7372 | 540 | goto bad_val; |
541 | } | |
542 | goto done; | |
543 | } | |
544 | } | |
545 | ||
546 | bad_val: | |
547 | return -1; | |
548 | ||
549 | done: | |
550 | ||
551 | *argc_p = argc; | |
552 | *argv_p = argv; | |
553 | return res; | |
554 | } | |
555 | ||
7c71a40c AV |
556 | static int pedit_keys_ex_getattr(struct rtattr *attr, |
557 | struct m_pedit_key_ex *keys_ex, int n) | |
558 | { | |
559 | struct rtattr *i; | |
560 | int rem = RTA_PAYLOAD(attr); | |
561 | struct rtattr *tb[TCA_PEDIT_KEY_EX_MAX + 1]; | |
562 | struct m_pedit_key_ex *k = keys_ex; | |
563 | ||
564 | for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
565 | if (!n) | |
566 | return -1; | |
567 | ||
568 | if (i->rta_type != TCA_PEDIT_KEY_EX) | |
569 | return -1; | |
570 | ||
571 | parse_rtattr_nested(tb, TCA_PEDIT_KEY_EX_MAX, i); | |
572 | ||
573 | k->htype = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]); | |
574 | k->cmd = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_CMD]); | |
575 | ||
576 | k++; | |
577 | n--; | |
578 | } | |
579 | ||
580 | return !!n; | |
581 | } | |
582 | ||
583 | static int pedit_keys_ex_addattr(struct m_pedit_sel *sel, struct nlmsghdr *n) | |
584 | { | |
585 | struct m_pedit_key_ex *k = sel->keys_ex; | |
586 | struct rtattr *keys_start; | |
587 | int i; | |
588 | ||
589 | if (!sel->extended) | |
590 | return 0; | |
591 | ||
592 | keys_start = addattr_nest(n, MAX_MSG, TCA_PEDIT_KEYS_EX | NLA_F_NESTED); | |
593 | ||
594 | for (i = 0; i < sel->sel.nkeys; i++) { | |
595 | struct rtattr *key_start; | |
596 | ||
597 | key_start = addattr_nest(n, MAX_MSG, | |
598 | TCA_PEDIT_KEY_EX | NLA_F_NESTED); | |
599 | ||
600 | if (addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_HTYPE, k->htype) || | |
601 | addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_CMD, k->cmd)) { | |
602 | return -1; | |
603 | } | |
604 | ||
605 | addattr_nest_end(n, key_start); | |
606 | ||
607 | k++; | |
608 | } | |
609 | ||
610 | addattr_nest_end(n, keys_start); | |
611 | ||
612 | return 0; | |
613 | } | |
614 | ||
7527b221 SH |
615 | static int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, |
616 | int tca_id, struct nlmsghdr *n) | |
7e7c7372 | 617 | { |
7c71a40c | 618 | struct m_pedit_sel sel = {}; |
7e7c7372 | 619 | |
620 | int argc = *argc_p; | |
621 | char **argv = *argv_p; | |
622 | int ok = 0, iok = 0; | |
623 | struct rtattr *tail; | |
624 | ||
7e7c7372 | 625 | while (argc > 0) { |
626 | if (pedit_debug > 1) | |
32a121cb | 627 | fprintf(stderr, "while pedit (%d:%s)\n", argc, *argv); |
7e7c7372 | 628 | if (matches(*argv, "pedit") == 0) { |
629 | NEXT_ARG(); | |
630 | ok++; | |
7c71a40c AV |
631 | |
632 | if (matches(*argv, "ex") == 0) { | |
633 | if (ok > 1) { | |
1e600da0 SH |
634 | fprintf(stderr, |
635 | "'ex' must be before first 'munge'\n"); | |
7c71a40c AV |
636 | explain(); |
637 | return -1; | |
638 | } | |
639 | sel.extended = true; | |
640 | NEXT_ARG(); | |
641 | } | |
642 | ||
7e7c7372 | 643 | continue; |
ebf32083 JHS |
644 | } else if (matches(*argv, "help") == 0) { |
645 | usage(); | |
7e7c7372 | 646 | } else if (matches(*argv, "munge") == 0) { |
647 | if (!ok) { | |
d8694a30 JHS |
648 | fprintf(stderr, "Bad pedit construct (%s)\n", |
649 | *argv); | |
ebf32083 | 650 | explain(); |
7e7c7372 | 651 | return -1; |
652 | } | |
653 | NEXT_ARG(); | |
7c71a40c AV |
654 | |
655 | if (parse_munge(&argc, &argv, &sel)) { | |
d8694a30 JHS |
656 | fprintf(stderr, "Bad pedit construct (%s)\n", |
657 | *argv); | |
ebf32083 | 658 | explain(); |
7e7c7372 | 659 | return -1; |
660 | } | |
661 | ok++; | |
662 | } else { | |
663 | break; | |
664 | } | |
665 | ||
666 | } | |
667 | ||
668 | if (!ok) { | |
ebf32083 | 669 | explain(); |
7e7c7372 | 670 | return -1; |
671 | } | |
672 | ||
e67aba55 | 673 | parse_action_control_dflt(&argc, &argv, &sel.sel.action, false, TC_ACT_OK); |
7e7c7372 | 674 | |
675 | if (argc) { | |
676 | if (matches(*argv, "index") == 0) { | |
677 | NEXT_ARG(); | |
678 | if (get_u32(&sel.sel.index, *argv, 10)) { | |
679 | fprintf(stderr, "Pedit: Illegal \"index\"\n"); | |
680 | return -1; | |
681 | } | |
682 | argc--; | |
683 | argv++; | |
684 | iok++; | |
685 | } | |
686 | } | |
687 | ||
c14f9d92 | 688 | tail = addattr_nest(n, MAX_MSG, tca_id); |
7c71a40c AV |
689 | if (!sel.extended) { |
690 | addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS, &sel, | |
691 | sizeof(sel.sel) + | |
692 | sel.sel.nkeys * sizeof(struct tc_pedit_key)); | |
693 | } else { | |
694 | addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS_EX, &sel, | |
695 | sizeof(sel.sel) + | |
696 | sel.sel.nkeys * sizeof(struct tc_pedit_key)); | |
697 | ||
698 | pedit_keys_ex_addattr(&sel, n); | |
699 | } | |
700 | ||
c14f9d92 | 701 | addattr_nest_end(n, tail); |
7e7c7372 | 702 | |
703 | *argc_p = argc; | |
704 | *argv_p = argv; | |
705 | return 0; | |
706 | } | |
707 | ||
7527b221 | 708 | static const char * const pedit_htype_str[] = { |
7c71a40c AV |
709 | [TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK] = "", |
710 | [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = "eth", | |
711 | [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = "ipv4", | |
712 | [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = "ipv6", | |
713 | [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = "tcp", | |
714 | [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp", | |
715 | }; | |
716 | ||
081d6c31 PM |
717 | static int print_pedit_location(FILE *f, |
718 | enum pedit_header_type htype, __u32 off) | |
7c71a40c | 719 | { |
081d6c31 PM |
720 | char *buf = NULL; |
721 | int rc; | |
722 | ||
723 | if (htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) { | |
724 | if (htype < ARRAY_SIZE(pedit_htype_str)) | |
725 | rc = asprintf(&buf, "%s", pedit_htype_str[htype]); | |
726 | else | |
727 | rc = asprintf(&buf, "unknown(%d)", htype); | |
728 | if (rc < 0) | |
729 | return rc; | |
730 | print_string(PRINT_ANY, "htype", "%s", buf); | |
731 | print_int(PRINT_ANY, "offset", "%+d", off); | |
732 | } else { | |
733 | print_string(PRINT_JSON, "htype", NULL, "network"); | |
734 | print_int(PRINT_ANY, "offset", "%d", off); | |
7c71a40c AV |
735 | } |
736 | ||
081d6c31 PM |
737 | free(buf); |
738 | return 0; | |
7c71a40c AV |
739 | } |
740 | ||
7527b221 | 741 | static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg) |
7e7c7372 | 742 | { |
743 | struct tc_pedit_sel *sel; | |
744 | struct rtattr *tb[TCA_PEDIT_MAX + 1]; | |
7c71a40c | 745 | struct m_pedit_key_ex *keys_ex = NULL; |
081d6c31 | 746 | int err; |
32a121cb | 747 | |
a99ebeee | 748 | print_string(PRINT_ANY, "kind", " %s ", "pedit"); |
7e7c7372 | 749 | if (arg == NULL) |
a99ebeee | 750 | return 0; |
7e7c7372 | 751 | |
78934000 | 752 | parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg); |
7e7c7372 | 753 | |
7c71a40c | 754 | if (!tb[TCA_PEDIT_PARMS] && !tb[TCA_PEDIT_PARMS_EX]) { |
d5ddb441 | 755 | fprintf(stderr, "Missing pedit parameters\n"); |
7e7c7372 | 756 | return -1; |
757 | } | |
7c71a40c AV |
758 | |
759 | if (tb[TCA_PEDIT_PARMS]) { | |
760 | sel = RTA_DATA(tb[TCA_PEDIT_PARMS]); | |
761 | } else { | |
762 | int err; | |
763 | ||
764 | sel = RTA_DATA(tb[TCA_PEDIT_PARMS_EX]); | |
765 | ||
766 | if (!tb[TCA_PEDIT_KEYS_EX]) { | |
767 | fprintf(f, "Netlink error\n"); | |
768 | return -1; | |
769 | } | |
770 | ||
771 | keys_ex = calloc(sel->nkeys, sizeof(*keys_ex)); | |
772 | if (!keys_ex) { | |
773 | fprintf(f, "Out of memory\n"); | |
774 | return -1; | |
775 | } | |
776 | ||
777 | err = pedit_keys_ex_getattr(tb[TCA_PEDIT_KEYS_EX], keys_ex, | |
778 | sel->nkeys); | |
779 | if (err) { | |
780 | fprintf(f, "Netlink error\n"); | |
781 | ||
782 | free(keys_ex); | |
783 | return -1; | |
784 | } | |
785 | } | |
7e7c7372 | 786 | |
e67aba55 | 787 | print_action_control(f, "action ", sel->action, " "); |
081d6c31 PM |
788 | print_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys); |
789 | print_uint(PRINT_ANY, "index", " \t index %u", sel->index); | |
790 | print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt); | |
791 | print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt); | |
7e7c7372 | 792 | |
793 | if (show_stats) { | |
794 | if (tb[TCA_PEDIT_TM]) { | |
795 | struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]); | |
32a121cb SH |
796 | |
797 | print_tm(f, tm); | |
7e7c7372 | 798 | } |
799 | } | |
081d6c31 | 800 | open_json_array(PRINT_JSON, "keys"); |
7e7c7372 | 801 | if (sel->nkeys) { |
802 | int i; | |
803 | struct tc_pedit_key *key = sel->keys; | |
7c71a40c | 804 | struct m_pedit_key_ex *key_ex = keys_ex; |
7e7c7372 | 805 | |
32a121cb | 806 | for (i = 0; i < sel->nkeys; i++, key++) { |
7c71a40c AV |
807 | enum pedit_header_type htype = |
808 | TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; | |
c05ddaf9 | 809 | enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET; |
7c71a40c AV |
810 | |
811 | if (keys_ex) { | |
812 | htype = key_ex->htype; | |
c05ddaf9 | 813 | cmd = key_ex->cmd; |
7c71a40c AV |
814 | |
815 | key_ex++; | |
816 | } | |
817 | ||
081d6c31 PM |
818 | open_json_object(NULL); |
819 | print_uint(PRINT_FP, NULL, "\n\t key #%d at ", i); | |
820 | ||
821 | err = print_pedit_location(f, htype, key->off); | |
822 | if (err) | |
823 | return err; | |
824 | ||
825 | /* In FP, report the "set" command as "val" to keep | |
826 | * backward compatibility. Report the true name in JSON. | |
827 | */ | |
828 | print_string(PRINT_FP, NULL, ": %s", | |
829 | cmd ? "add" : "val"); | |
830 | print_string(PRINT_JSON, "cmd", NULL, | |
831 | cmd ? "add" : "set"); | |
832 | print_hex(PRINT_ANY, "val", " %08x", | |
833 | (unsigned int)ntohl(key->val)); | |
834 | print_hex(PRINT_ANY, "mask", " mask %08x", | |
835 | (unsigned int)ntohl(key->mask)); | |
836 | close_json_object(); | |
7e7c7372 | 837 | } |
838 | } else { | |
d8694a30 JHS |
839 | fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index, |
840 | sel->nkeys); | |
7e7c7372 | 841 | } |
081d6c31 | 842 | close_json_array(PRINT_JSON, " "); |
7e7c7372 | 843 | |
7b0d424a | 844 | print_nl(); |
7c71a40c AV |
845 | |
846 | free(keys_ex); | |
7e7c7372 | 847 | return 0; |
848 | } | |
849 | ||
7e7c7372 | 850 | struct action_util pedit_action_util = { |
851 | .id = "pedit", | |
852 | .parse_aopt = parse_pedit, | |
853 | .print_aopt = print_pedit, | |
854 | }; |