]>
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> | |
311b4145 SH |
21 | #include <errno.h> |
22 | ||
23 | #include "m_ematch.h" | |
24 | #include <linux/tc_ematch/tc_em_meta.h> | |
25 | ||
26 | extern struct ematch_util meta_ematch_util; | |
27 | ||
28 | static void meta_print_usage(FILE *fd) | |
29 | { | |
30 | fprintf(fd, | |
31 | "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \ | |
32 | "where: OBJECT := { META_ID | VALUE }\n" \ | |
33 | " META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \ | |
34 | "\n" \ | |
35 | "Example: meta(nfmark gt 24)\n" \ | |
ba26a6e8 | 36 | " meta(indev shift 1 eq \"ppp\")\n" \ |
311b4145 | 37 | " meta(tcindex mask 0xf0 eq 0xf0)\n" \ |
311b4145 SH |
38 | "\n" \ |
39 | "For a list of meta identifiers, use meta(list).\n"); | |
40 | } | |
41 | ||
42 | struct meta_entry { | |
43 | int id; | |
44 | char * kind; | |
45 | char * mask; | |
46 | char * desc; | |
47 | } meta_table[] = { | |
48 | #define TCF_META_ID_SECTION 0 | |
49 | #define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc } | |
50 | __A(SECTION, "Generic", "", ""), | |
51 | __A(RANDOM, "random", "i", | |
52 | "Random value (32 bit)"), | |
53 | __A(LOADAVG_0, "loadavg_1", "i", | |
54 | "Load average in last minute"), | |
55 | __A(LOADAVG_1, "loadavg_5", "i", | |
56 | "Load average in last 5 minutes"), | |
57 | __A(LOADAVG_2, "loadavg_15", "i", | |
58 | "Load average in last 15 minutes"), | |
59 | ||
60 | __A(SECTION, "Interfaces", "", ""), | |
61 | __A(DEV, "dev", "iv", | |
62 | "Device the packet is on"), | |
311b4145 SH |
63 | __A(SECTION, "Packet attributes", "", ""), |
64 | __A(PRIORITY, "priority", "i", | |
65 | "Priority of packet"), | |
66 | __A(PROTOCOL, "protocol", "i", | |
67 | "Link layer protocol"), | |
311b4145 SH |
68 | __A(PKTTYPE, "pkt_type", "i", |
69 | "Packet type (uni|multi|broad|...)cast"), | |
70 | __A(PKTLEN, "pkt_len", "i", | |
71 | "Length of packet"), | |
72 | __A(DATALEN, "data_len", "i", | |
73 | "Length of data in packet"), | |
74 | __A(MACLEN, "mac_len", "i", | |
75 | "Length of link layer header"), | |
76 | ||
77 | __A(SECTION, "Netfilter", "", ""), | |
78 | __A(NFMARK, "nf_mark", "i", | |
79 | "Netfilter mark"), | |
80 | __A(NFMARK, "fwmark", "i", | |
81 | "Alias for nf_mark"), | |
82 | ||
83 | __A(SECTION, "Traffic Control", "", ""), | |
84 | __A(TCINDEX, "tc_index", "i", "TC Index"), | |
311b4145 SH |
85 | __A(SECTION, "Routing", "", ""), |
86 | __A(RTCLASSID, "rt_classid", "i", | |
87 | "Routing ClassID (cls_route)"), | |
88 | __A(RTIIF, "rt_iif", "i", | |
89 | "Incoming interface index"), | |
5e76a87d | 90 | __A(VLAN_TAG, "vlan", "i", "Vlan tag"), |
311b4145 SH |
91 | |
92 | __A(SECTION, "Sockets", "", ""), | |
93 | __A(SK_FAMILY, "sk_family", "i", "Address family"), | |
94 | __A(SK_STATE, "sk_state", "i", "State"), | |
95 | __A(SK_REUSE, "sk_reuse", "i", "Reuse Flag"), | |
96 | __A(SK_BOUND_IF, "sk_bind_if", "iv", "Bound interface"), | |
97 | __A(SK_REFCNT, "sk_refcnt", "i", "Reference counter"), | |
98 | __A(SK_SHUTDOWN, "sk_shutdown", "i", "Shutdown mask"), | |
99 | __A(SK_PROTO, "sk_proto", "i", "Protocol"), | |
100 | __A(SK_TYPE, "sk_type", "i", "Type"), | |
101 | __A(SK_RCVBUF, "sk_rcvbuf", "i", "Receive buffer size"), | |
102 | __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"), | |
103 | __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"), | |
104 | __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"), | |
105 | __A(SK_WMEM_QUEUED, "sk_wmem_queue","i", "WMEM queue"), | |
106 | __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"), | |
107 | __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"), | |
108 | __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"), | |
109 | __A(SK_FORWARD_ALLOCS, "sk_fwd_alloc", "i", "Forward allocations"), | |
110 | __A(SK_SNDBUF, "sk_sndbuf", "i", "Send buffer size"), | |
111 | #undef __A | |
112 | }; | |
113 | ||
114 | static inline int map_type(char k) | |
115 | { | |
116 | switch (k) { | |
117 | case 'i': return TCF_META_TYPE_INT; | |
118 | case 'v': return TCF_META_TYPE_VAR; | |
119 | } | |
120 | ||
121 | fprintf(stderr, "BUG: Unknown map character '%c'\n", k); | |
122 | return INT_MAX; | |
123 | } | |
124 | ||
125 | static struct meta_entry * lookup_meta_entry(struct bstr *kind) | |
126 | { | |
127 | int i; | |
128 | ||
129 | for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) | |
130 | if (!bstrcmp(kind, meta_table[i].kind) && | |
131 | meta_table[i].id != 0) | |
132 | return &meta_table[i]; | |
ae665a52 | 133 | |
311b4145 SH |
134 | return NULL; |
135 | } | |
136 | ||
137 | static struct meta_entry * lookup_meta_entry_byid(int id) | |
138 | { | |
139 | int i; | |
140 | ||
141 | for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) | |
142 | if (meta_table[i].id == id) | |
143 | return &meta_table[i]; | |
ae665a52 | 144 | |
311b4145 SH |
145 | return NULL; |
146 | } | |
147 | ||
148 | static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val, | |
149 | struct tcf_meta_val *hdr) | |
150 | { | |
151 | __u32 t; | |
152 | ||
153 | switch (TCF_META_TYPE(hdr->kind)) { | |
154 | case TCF_META_TYPE_INT: | |
155 | t = val; | |
156 | addattr_l(n, MAX_MSG, tlv, &t, sizeof(t)); | |
157 | break; | |
158 | ||
159 | case TCF_META_TYPE_VAR: | |
160 | if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) { | |
161 | struct bstr *a = (struct bstr *) val; | |
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 | { | |
171 | char *p; | |
172 | struct meta_entry *entry; | |
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 | ||
195 | for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) { | |
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...) \ | |
234 | em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT ,##ARGS) | |
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 | { | |
252 | struct meta_entry *entry; | |
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 | ||
311 | while(a) { | |
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; | |
363 | struct tcf_meta_hdr meta_hdr; | |
364 | unsigned long lvalue = 0, rvalue = 0; | |
365 | ||
366 | memset(&meta_hdr, 0, sizeof(meta_hdr)); | |
367 | ||
368 | if (args == NULL) | |
369 | return PARSE_ERR(args, "meta: missing arguments"); | |
370 | ||
371 | if (!bstrcmp(args, "list")) { | |
372 | list_meta_ids(stderr); | |
373 | return -1; | |
374 | } | |
375 | ||
376 | a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL); | |
377 | if (a == PARSE_FAILURE) | |
378 | return -1; | |
379 | else if (a == NULL) | |
380 | return PARSE_ERR(args, "meta: missing operand"); | |
381 | ||
382 | if (!bstrcmp(a, "eq")) | |
383 | opnd = TCF_EM_OPND_EQ; | |
384 | else if (!bstrcmp(a, "gt")) | |
385 | opnd = TCF_EM_OPND_GT; | |
386 | else if (!bstrcmp(a, "lt")) | |
387 | opnd = TCF_EM_OPND_LT; | |
388 | else | |
389 | return PARSE_ERR(a, "meta: invalid operand"); | |
390 | ||
391 | meta_hdr.left.op = (__u8) opnd; | |
392 | ||
393 | if (a->next == NULL) | |
394 | return PARSE_ERR(args, "meta: missing rvalue"); | |
395 | a = bstr_next(a); | |
396 | ||
397 | a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left); | |
398 | if (a == PARSE_FAILURE) | |
399 | return -1; | |
400 | else if (a != NULL) | |
401 | return PARSE_ERR(a, "meta: unexpected trailer"); | |
ae665a52 | 402 | |
311b4145 SH |
403 | |
404 | addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); | |
405 | ||
406 | addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr)); | |
407 | ||
61407852 PM |
408 | dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); |
409 | dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); | |
311b4145 SH |
410 | |
411 | return 0; | |
412 | } | |
413 | #undef PARSE_ERR | |
414 | ||
415 | static inline void print_binary(FILE *fd, unsigned char *str, int len) | |
416 | { | |
417 | int i; | |
418 | ||
419 | for (i = 0; i < len; i++) | |
420 | if (!isprint(str[i])) | |
421 | goto binary; | |
422 | ||
423 | for (i = 0; i < len; i++) | |
424 | fprintf(fd, "%c", str[i]); | |
425 | return; | |
426 | ||
427 | binary: | |
428 | for (i = 0; i < len; i++) | |
429 | fprintf(fd, "%02x ", str[i]); | |
430 | ||
431 | fprintf(fd, "\""); | |
432 | for (i = 0; i < len; i++) | |
433 | fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.'); | |
434 | fprintf(fd, "\""); | |
435 | } | |
436 | ||
437 | static inline int print_value(FILE *fd, int type, struct rtattr *rta) | |
438 | { | |
439 | if (rta == NULL) { | |
440 | fprintf(stderr, "Missing value TLV\n"); | |
441 | return -1; | |
442 | } | |
443 | ||
444 | switch(type) { | |
445 | case TCF_META_TYPE_INT: | |
446 | if (RTA_PAYLOAD(rta) < sizeof(__u32)) { | |
447 | fprintf(stderr, "meta int type value TLV " \ | |
448 | "size mismatch.\n"); | |
449 | return -1; | |
450 | } | |
ff24746c | 451 | fprintf(fd, "%d", rta_getattr_u32(rta)); |
311b4145 SH |
452 | break; |
453 | ||
454 | case TCF_META_TYPE_VAR: | |
455 | print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta)); | |
456 | break; | |
457 | } | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta) | |
463 | { | |
464 | int id = TCF_META_ID(obj->kind); | |
465 | int type = TCF_META_TYPE(obj->kind); | |
466 | struct meta_entry *entry; | |
467 | ||
468 | if (id == TCF_META_ID_VALUE) | |
469 | return print_value(fd, type, rta); | |
470 | ||
471 | entry = lookup_meta_entry_byid(id); | |
472 | ||
473 | if (entry == NULL) | |
474 | fprintf(fd, "[unknown meta id %d]", id); | |
475 | else | |
476 | fprintf(fd, "%s", entry->kind); | |
477 | ||
478 | if (obj->shift) | |
479 | fprintf(fd, " shift %d", obj->shift); | |
480 | ||
481 | switch (type) { | |
482 | case TCF_META_TYPE_INT: | |
483 | if (rta) { | |
484 | if (RTA_PAYLOAD(rta) < sizeof(__u32)) | |
485 | goto size_mismatch; | |
486 | ||
487 | fprintf(fd, " mask 0x%08x", | |
ff24746c | 488 | rta_getattr_u32(rta)); |
311b4145 SH |
489 | } |
490 | break; | |
491 | } | |
492 | ||
493 | return 0; | |
494 | ||
495 | size_mismatch: | |
496 | fprintf(stderr, "meta int type mask TLV size mismatch\n"); | |
497 | return -1; | |
498 | } | |
499 | ||
500 | ||
501 | static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, | |
502 | int data_len) | |
503 | { | |
504 | struct rtattr *tb[TCA_EM_META_MAX+1]; | |
505 | struct tcf_meta_hdr *meta_hdr; | |
506 | ||
507 | if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0) | |
508 | return -1; | |
509 | ||
510 | if (tb[TCA_EM_META_HDR] == NULL) { | |
511 | fprintf(stderr, "Missing meta header\n"); | |
512 | return -1; | |
513 | } | |
514 | ||
515 | if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) { | |
516 | fprintf(stderr, "Meta header size mismatch\n"); | |
517 | return -1; | |
518 | } | |
519 | ||
520 | meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]); | |
521 | ||
522 | if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0) | |
523 | return -1; | |
524 | ||
525 | switch (meta_hdr->left.op) { | |
526 | case TCF_EM_OPND_EQ: | |
527 | fprintf(fd, " eq "); | |
528 | break; | |
529 | case TCF_EM_OPND_LT: | |
530 | fprintf(fd, " lt "); | |
531 | break; | |
532 | case TCF_EM_OPND_GT: | |
533 | fprintf(fd, " gt "); | |
534 | break; | |
535 | } | |
536 | ||
537 | return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]); | |
538 | } | |
539 | ||
540 | struct ematch_util meta_ematch_util = { | |
541 | .kind = "meta", | |
542 | .kind_num = TCF_EM_META, | |
543 | .parse_eopt = meta_parse_eopt, | |
544 | .print_eopt = meta_print_eopt, | |
545 | .print_usage = meta_print_usage | |
546 | }; |