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