]>
Commit | Line | Data |
---|---|---|
311b4145 SH |
1 | /* |
2 | * em_meta.c Metadata Ematch | |
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 | * | |
9 | * Authors: Thomas Graf <tgraf@suug.ch> | |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <unistd.h> | |
311b4145 SH |
15 | #include <fcntl.h> |
16 | #include <sys/socket.h> | |
17 | #include <netinet/in.h> | |
18 | #include <arpa/inet.h> | |
19 | #include <string.h> | |
311b4145 SH |
20 | #include <errno.h> |
21 | ||
22 | #include "m_ematch.h" | |
23 | #include <linux/tc_ematch/tc_em_meta.h> | |
24 | ||
25 | extern struct ematch_util meta_ematch_util; | |
26 | ||
27 | static void meta_print_usage(FILE *fd) | |
28 | { | |
29 | fprintf(fd, | |
30 | "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \ | |
31 | "where: OBJECT := { META_ID | VALUE }\n" \ | |
32 | " META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \ | |
33 | "\n" \ | |
a07c6d61 | 34 | "Example: meta(nf_mark gt 24)\n" \ |
ba26a6e8 | 35 | " meta(indev shift 1 eq \"ppp\")\n" \ |
311b4145 | 36 | " meta(tcindex mask 0xf0 eq 0xf0)\n" \ |
311b4145 SH |
37 | "\n" \ |
38 | "For a list of meta identifiers, use meta(list).\n"); | |
39 | } | |
40 | ||
42d9eed4 | 41 | static const struct meta_entry { |
311b4145 | 42 | int id; |
32a121cb SH |
43 | char *kind; |
44 | char *mask; | |
45 | char *desc; | |
311b4145 SH |
46 | } meta_table[] = { |
47 | #define TCF_META_ID_SECTION 0 | |
48 | #define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc } | |
49 | __A(SECTION, "Generic", "", ""), | |
50 | __A(RANDOM, "random", "i", | |
51 | "Random value (32 bit)"), | |
52 | __A(LOADAVG_0, "loadavg_1", "i", | |
53 | "Load average in last minute"), | |
54 | __A(LOADAVG_1, "loadavg_5", "i", | |
55 | "Load average in last 5 minutes"), | |
56 | __A(LOADAVG_2, "loadavg_15", "i", | |
57 | "Load average in last 15 minutes"), | |
58 | ||
59 | __A(SECTION, "Interfaces", "", ""), | |
60 | __A(DEV, "dev", "iv", | |
61 | "Device the packet is on"), | |
311b4145 SH |
62 | __A(SECTION, "Packet attributes", "", ""), |
63 | __A(PRIORITY, "priority", "i", | |
64 | "Priority of packet"), | |
65 | __A(PROTOCOL, "protocol", "i", | |
66 | "Link layer protocol"), | |
311b4145 SH |
67 | __A(PKTTYPE, "pkt_type", "i", |
68 | "Packet type (uni|multi|broad|...)cast"), | |
69 | __A(PKTLEN, "pkt_len", "i", | |
70 | "Length of packet"), | |
71 | __A(DATALEN, "data_len", "i", | |
72 | "Length of data in packet"), | |
73 | __A(MACLEN, "mac_len", "i", | |
74 | "Length of link layer header"), | |
75 | ||
76 | __A(SECTION, "Netfilter", "", ""), | |
77 | __A(NFMARK, "nf_mark", "i", | |
78 | "Netfilter mark"), | |
79 | __A(NFMARK, "fwmark", "i", | |
80 | "Alias for nf_mark"), | |
81 | ||
82 | __A(SECTION, "Traffic Control", "", ""), | |
83 | __A(TCINDEX, "tc_index", "i", "TC Index"), | |
311b4145 SH |
84 | __A(SECTION, "Routing", "", ""), |
85 | __A(RTCLASSID, "rt_classid", "i", | |
86 | "Routing ClassID (cls_route)"), | |
87 | __A(RTIIF, "rt_iif", "i", | |
88 | "Incoming interface index"), | |
5e76a87d | 89 | __A(VLAN_TAG, "vlan", "i", "Vlan tag"), |
311b4145 SH |
90 | |
91 | __A(SECTION, "Sockets", "", ""), | |
92 | __A(SK_FAMILY, "sk_family", "i", "Address family"), | |
93 | __A(SK_STATE, "sk_state", "i", "State"), | |
94 | __A(SK_REUSE, "sk_reuse", "i", "Reuse Flag"), | |
95 | __A(SK_BOUND_IF, "sk_bind_if", "iv", "Bound interface"), | |
96 | __A(SK_REFCNT, "sk_refcnt", "i", "Reference counter"), | |
97 | __A(SK_SHUTDOWN, "sk_shutdown", "i", "Shutdown mask"), | |
98 | __A(SK_PROTO, "sk_proto", "i", "Protocol"), | |
99 | __A(SK_TYPE, "sk_type", "i", "Type"), | |
100 | __A(SK_RCVBUF, "sk_rcvbuf", "i", "Receive buffer size"), | |
101 | __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"), | |
102 | __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"), | |
103 | __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"), | |
32a121cb | 104 | __A(SK_WMEM_QUEUED, "sk_wmem_queue", "i", "WMEM queue"), |
311b4145 SH |
105 | __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"), |
106 | __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"), | |
107 | __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"), | |
108 | __A(SK_FORWARD_ALLOCS, "sk_fwd_alloc", "i", "Forward allocations"), | |
109 | __A(SK_SNDBUF, "sk_sndbuf", "i", "Send buffer size"), | |
110 | #undef __A | |
111 | }; | |
112 | ||
113 | static inline int map_type(char k) | |
114 | { | |
115 | switch (k) { | |
116 | case 'i': return TCF_META_TYPE_INT; | |
117 | case 'v': return TCF_META_TYPE_VAR; | |
118 | } | |
119 | ||
120 | fprintf(stderr, "BUG: Unknown map character '%c'\n", k); | |
121 | return INT_MAX; | |
122 | } | |
123 | ||
42d9eed4 | 124 | static const struct meta_entry *lookup_meta_entry(struct bstr *kind) |
311b4145 SH |
125 | { |
126 | int i; | |
127 | ||
32a121cb | 128 | for (i = 0; i < ARRAY_SIZE(meta_table); i++) |
311b4145 SH |
129 | if (!bstrcmp(kind, meta_table[i].kind) && |
130 | meta_table[i].id != 0) | |
131 | return &meta_table[i]; | |
ae665a52 | 132 | |
311b4145 SH |
133 | return NULL; |
134 | } | |
135 | ||
42d9eed4 | 136 | static const struct meta_entry *lookup_meta_entry_byid(int id) |
311b4145 SH |
137 | { |
138 | int i; | |
139 | ||
32a121cb | 140 | for (i = 0; i < ARRAY_SIZE(meta_table); i++) |
311b4145 SH |
141 | if (meta_table[i].id == id) |
142 | return &meta_table[i]; | |
ae665a52 | 143 | |
311b4145 SH |
144 | return NULL; |
145 | } | |
146 | ||
147 | static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val, | |
148 | struct tcf_meta_val *hdr) | |
149 | { | |
150 | __u32 t; | |
151 | ||
152 | switch (TCF_META_TYPE(hdr->kind)) { | |
153 | case TCF_META_TYPE_INT: | |
154 | t = val; | |
155 | addattr_l(n, MAX_MSG, tlv, &t, sizeof(t)); | |
156 | break; | |
157 | ||
158 | case TCF_META_TYPE_VAR: | |
159 | if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) { | |
160 | struct bstr *a = (struct bstr *) val; | |
32a121cb | 161 | |
311b4145 SH |
162 | addattr_l(n, MAX_MSG, tlv, a->data, a->len); |
163 | } | |
164 | break; | |
165 | } | |
166 | } | |
167 | ||
168 | static inline int is_compatible(struct tcf_meta_val *what, | |
169 | struct tcf_meta_val *needed) | |
170 | { | |
42d9eed4 | 171 | const struct meta_entry *entry; |
311b4145 | 172 | char *p; |
ae665a52 | 173 | |
311b4145 SH |
174 | entry = lookup_meta_entry_byid(TCF_META_ID(what->kind)); |
175 | ||
176 | if (entry == NULL) | |
177 | return 0; | |
ae665a52 | 178 | |
311b4145 SH |
179 | for (p = entry->mask; p; p++) |
180 | if (map_type(*p) == TCF_META_TYPE(needed->kind)) | |
181 | return 1; | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | static void list_meta_ids(FILE *fd) | |
187 | { | |
188 | int i; | |
189 | ||
190 | fprintf(fd, | |
191 | "--------------------------------------------------------\n" \ | |
192 | " ID Type Description\n" \ | |
193 | "--------------------------------------------------------"); | |
194 | ||
32a121cb | 195 | for (i = 0; i < ARRAY_SIZE(meta_table); i++) { |
311b4145 SH |
196 | if (meta_table[i].id == TCF_META_ID_SECTION) { |
197 | fprintf(fd, "\n%s:\n", meta_table[i].kind); | |
198 | } else { | |
199 | char *p = meta_table[i].mask; | |
200 | char buf[64] = {0}; | |
201 | ||
202 | fprintf(fd, " %-16s ", meta_table[i].kind); | |
203 | ||
204 | while (*p) { | |
205 | int type = map_type(*p); | |
206 | ||
207 | switch (type) { | |
208 | case TCF_META_TYPE_INT: | |
209 | strcat(buf, "INT"); | |
210 | break; | |
211 | ||
212 | case TCF_META_TYPE_VAR: | |
213 | strcat(buf, "VAR"); | |
214 | break; | |
215 | } | |
216 | ||
217 | if (*(++p)) | |
218 | strcat(buf, ","); | |
219 | } | |
220 | ||
221 | fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc); | |
222 | } | |
223 | } | |
224 | ||
225 | fprintf(fd, | |
226 | "--------------------------------------------------------\n"); | |
227 | } | |
228 | ||
229 | #undef TCF_META_ID_SECTION | |
230 | ||
231 | #define PARSE_FAILURE ((void *) (-1)) | |
232 | ||
233 | #define PARSE_ERR(CARG, FMT, ARGS...) \ | |
32a121cb | 234 | em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT, ##ARGS) |
311b4145 SH |
235 | |
236 | static inline int can_adopt(struct tcf_meta_val *val) | |
237 | { | |
238 | return !!TCF_META_ID(val->kind); | |
239 | } | |
240 | ||
241 | static inline int overwrite_type(struct tcf_meta_val *src, | |
242 | struct tcf_meta_val *dst) | |
243 | { | |
244 | return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind); | |
245 | } | |
ae665a52 | 246 | |
311b4145 SH |
247 | |
248 | static inline struct bstr * | |
249 | parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj, | |
250 | unsigned long *dst, struct tcf_meta_val *left) | |
251 | { | |
42d9eed4 | 252 | const struct meta_entry *entry; |
311b4145 SH |
253 | unsigned long num; |
254 | struct bstr *a; | |
255 | ||
256 | if (arg->quoted) { | |
257 | obj->kind = TCF_META_TYPE_VAR << 12; | |
258 | obj->kind |= TCF_META_ID_VALUE; | |
259 | *dst = (unsigned long) arg; | |
260 | return bstr_next(arg); | |
261 | } | |
262 | ||
263 | num = bstrtoul(arg); | |
11bbe7fd | 264 | if (num != ULONG_MAX) { |
311b4145 SH |
265 | obj->kind = TCF_META_TYPE_INT << 12; |
266 | obj->kind |= TCF_META_ID_VALUE; | |
267 | *dst = (unsigned long) num; | |
268 | return bstr_next(arg); | |
269 | } | |
270 | ||
271 | entry = lookup_meta_entry(arg); | |
272 | ||
273 | if (entry == NULL) { | |
274 | PARSE_ERR(arg, "meta: unknown meta id\n"); | |
275 | return PARSE_FAILURE; | |
276 | } | |
277 | ||
278 | obj->kind = entry->id | (map_type(entry->mask[0]) << 12); | |
279 | ||
280 | if (left) { | |
281 | struct tcf_meta_val *right = obj; | |
ae665a52 | 282 | |
311b4145 SH |
283 | if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind)) |
284 | goto compatible; | |
285 | ||
286 | if (can_adopt(left) && !can_adopt(right)) { | |
287 | if (is_compatible(left, right)) | |
288 | left->kind = overwrite_type(left, right); | |
289 | else | |
290 | goto not_compatible; | |
291 | } else if (can_adopt(right) && !can_adopt(left)) { | |
292 | if (is_compatible(right, left)) | |
293 | right->kind = overwrite_type(right, left); | |
294 | else | |
295 | goto not_compatible; | |
296 | } else if (can_adopt(left) && can_adopt(right)) { | |
297 | if (is_compatible(left, right)) | |
298 | left->kind = overwrite_type(left, right); | |
299 | else if (is_compatible(right, left)) | |
300 | right->kind = overwrite_type(right, left); | |
301 | else | |
302 | goto not_compatible; | |
ae665a52 | 303 | } else |
311b4145 SH |
304 | goto not_compatible; |
305 | } | |
306 | ||
307 | compatible: | |
308 | ||
309 | a = bstr_next(arg); | |
310 | ||
32a121cb | 311 | while (a) { |
311b4145 SH |
312 | if (!bstrcmp(a, "shift")) { |
313 | unsigned long shift; | |
314 | ||
315 | if (a->next == NULL) { | |
316 | PARSE_ERR(a, "meta: missing argument"); | |
317 | return PARSE_FAILURE; | |
318 | } | |
319 | a = bstr_next(a); | |
ae665a52 | 320 | |
311b4145 | 321 | shift = bstrtoul(a); |
11bbe7fd | 322 | if (shift == ULONG_MAX) { |
311b4145 SH |
323 | PARSE_ERR(a, "meta: invalid shift, must " \ |
324 | "be numeric"); | |
325 | return PARSE_FAILURE; | |
326 | } | |
327 | ||
328 | obj->shift = (__u8) shift; | |
329 | a = bstr_next(a); | |
330 | } else if (!bstrcmp(a, "mask")) { | |
331 | unsigned long mask; | |
332 | ||
333 | if (a->next == NULL) { | |
334 | PARSE_ERR(a, "meta: missing argument"); | |
335 | return PARSE_FAILURE; | |
336 | } | |
337 | a = bstr_next(a); | |
ae665a52 | 338 | |
311b4145 | 339 | mask = bstrtoul(a); |
11bbe7fd | 340 | if (mask == ULONG_MAX) { |
311b4145 SH |
341 | PARSE_ERR(a, "meta: invalid mask, must be " \ |
342 | "numeric"); | |
343 | return PARSE_FAILURE; | |
344 | } | |
345 | *dst = (unsigned long) mask; | |
346 | a = bstr_next(a); | |
347 | } else | |
348 | break; | |
349 | } | |
350 | ||
351 | return a; | |
352 | ||
353 | not_compatible: | |
354 | PARSE_ERR(arg, "lvalue and rvalue are not compatible."); | |
355 | return PARSE_FAILURE; | |
356 | } | |
357 | ||
358 | static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, | |
359 | struct bstr *args) | |
360 | { | |
361 | int opnd; | |
362 | struct bstr *a; | |
d17b136f | 363 | struct tcf_meta_hdr meta_hdr = {}; |
311b4145 SH |
364 | unsigned long lvalue = 0, rvalue = 0; |
365 | ||
311b4145 SH |
366 | if (args == NULL) |
367 | return PARSE_ERR(args, "meta: missing arguments"); | |
368 | ||
369 | if (!bstrcmp(args, "list")) { | |
370 | list_meta_ids(stderr); | |
371 | return -1; | |
372 | } | |
373 | ||
374 | a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL); | |
375 | if (a == PARSE_FAILURE) | |
376 | return -1; | |
377 | else if (a == NULL) | |
378 | return PARSE_ERR(args, "meta: missing operand"); | |
379 | ||
380 | if (!bstrcmp(a, "eq")) | |
381 | opnd = TCF_EM_OPND_EQ; | |
382 | else if (!bstrcmp(a, "gt")) | |
383 | opnd = TCF_EM_OPND_GT; | |
384 | else if (!bstrcmp(a, "lt")) | |
385 | opnd = TCF_EM_OPND_LT; | |
386 | else | |
387 | return PARSE_ERR(a, "meta: invalid operand"); | |
388 | ||
389 | meta_hdr.left.op = (__u8) opnd; | |
390 | ||
391 | if (a->next == NULL) | |
392 | return PARSE_ERR(args, "meta: missing rvalue"); | |
393 | a = bstr_next(a); | |
394 | ||
395 | a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left); | |
396 | if (a == PARSE_FAILURE) | |
397 | return -1; | |
398 | else if (a != NULL) | |
399 | return PARSE_ERR(a, "meta: unexpected trailer"); | |
ae665a52 | 400 | |
311b4145 SH |
401 | |
402 | addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); | |
403 | ||
404 | addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr)); | |
405 | ||
61407852 PM |
406 | dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); |
407 | dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); | |
311b4145 SH |
408 | |
409 | return 0; | |
410 | } | |
411 | #undef PARSE_ERR | |
412 | ||
413 | static inline void print_binary(FILE *fd, unsigned char *str, int len) | |
414 | { | |
415 | int i; | |
416 | ||
417 | for (i = 0; i < len; i++) | |
418 | if (!isprint(str[i])) | |
419 | goto binary; | |
420 | ||
421 | for (i = 0; i < len; i++) | |
422 | fprintf(fd, "%c", str[i]); | |
423 | return; | |
424 | ||
425 | binary: | |
426 | for (i = 0; i < len; i++) | |
427 | fprintf(fd, "%02x ", str[i]); | |
428 | ||
429 | fprintf(fd, "\""); | |
430 | for (i = 0; i < len; i++) | |
431 | fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.'); | |
432 | fprintf(fd, "\""); | |
433 | } | |
434 | ||
435 | static inline int print_value(FILE *fd, int type, struct rtattr *rta) | |
436 | { | |
437 | if (rta == NULL) { | |
438 | fprintf(stderr, "Missing value TLV\n"); | |
439 | return -1; | |
440 | } | |
441 | ||
32a121cb | 442 | switch (type) { |
311b4145 SH |
443 | case TCF_META_TYPE_INT: |
444 | if (RTA_PAYLOAD(rta) < sizeof(__u32)) { | |
445 | fprintf(stderr, "meta int type value TLV " \ | |
446 | "size mismatch.\n"); | |
447 | return -1; | |
448 | } | |
ff24746c | 449 | fprintf(fd, "%d", rta_getattr_u32(rta)); |
311b4145 SH |
450 | break; |
451 | ||
452 | case TCF_META_TYPE_VAR: | |
453 | print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta)); | |
454 | break; | |
455 | } | |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta) | |
461 | { | |
462 | int id = TCF_META_ID(obj->kind); | |
463 | int type = TCF_META_TYPE(obj->kind); | |
42d9eed4 | 464 | const struct meta_entry *entry; |
311b4145 SH |
465 | |
466 | if (id == TCF_META_ID_VALUE) | |
467 | return print_value(fd, type, rta); | |
468 | ||
469 | entry = lookup_meta_entry_byid(id); | |
470 | ||
471 | if (entry == NULL) | |
472 | fprintf(fd, "[unknown meta id %d]", id); | |
473 | else | |
474 | fprintf(fd, "%s", entry->kind); | |
475 | ||
476 | if (obj->shift) | |
477 | fprintf(fd, " shift %d", obj->shift); | |
478 | ||
479 | switch (type) { | |
480 | case TCF_META_TYPE_INT: | |
481 | if (rta) { | |
482 | if (RTA_PAYLOAD(rta) < sizeof(__u32)) | |
483 | goto size_mismatch; | |
484 | ||
247ace61 PS |
485 | if (rta_getattr_u32(rta)) |
486 | fprintf(fd, " mask 0x%08x", | |
487 | rta_getattr_u32(rta)); | |
311b4145 SH |
488 | } |
489 | break; | |
490 | } | |
491 | ||
492 | return 0; | |
493 | ||
494 | size_mismatch: | |
495 | fprintf(stderr, "meta int type mask TLV size mismatch\n"); | |
496 | return -1; | |
497 | } | |
498 | ||
499 | ||
500 | static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, | |
501 | int data_len) | |
502 | { | |
503 | struct rtattr *tb[TCA_EM_META_MAX+1]; | |
504 | struct tcf_meta_hdr *meta_hdr; | |
505 | ||
506 | if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0) | |
507 | return -1; | |
508 | ||
509 | if (tb[TCA_EM_META_HDR] == NULL) { | |
510 | fprintf(stderr, "Missing meta header\n"); | |
511 | return -1; | |
512 | } | |
513 | ||
514 | if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) { | |
515 | fprintf(stderr, "Meta header size mismatch\n"); | |
516 | return -1; | |
517 | } | |
518 | ||
519 | meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]); | |
520 | ||
521 | if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0) | |
522 | return -1; | |
523 | ||
524 | switch (meta_hdr->left.op) { | |
525 | case TCF_EM_OPND_EQ: | |
526 | fprintf(fd, " eq "); | |
527 | break; | |
528 | case TCF_EM_OPND_LT: | |
529 | fprintf(fd, " lt "); | |
530 | break; | |
531 | case TCF_EM_OPND_GT: | |
532 | fprintf(fd, " gt "); | |
533 | break; | |
534 | } | |
535 | ||
536 | return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]); | |
537 | } | |
538 | ||
539 | struct ematch_util meta_ematch_util = { | |
540 | .kind = "meta", | |
541 | .kind_num = TCF_EM_META, | |
542 | .parse_eopt = meta_parse_eopt, | |
543 | .print_eopt = meta_print_eopt, | |
544 | .print_usage = meta_print_usage | |
545 | }; |