]> rtime.felk.cvut.cz Git - lisovros/iproute2_canprio.git/blob - tc/em_meta.c
vlan meta tag match
[lisovros/iproute2_canprio.git] / tc / em_meta.c
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"),
65         __A(SECTION,            "Packet attributes", "", ""),
66         __A(PRIORITY,           "priority",     "i",
67                                 "Priority of packet"),
68         __A(PROTOCOL,           "protocol",     "i",
69                                 "Link layer protocol"),
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"),
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         __A(VLAN_TAG,           "vlan",         "i",    "Vlan tag"),
113 #undef __A
114 };
115
116 static inline int map_type(char k)
117 {
118         switch (k) {
119                 case 'i': return TCF_META_TYPE_INT;
120                 case 'v': return TCF_META_TYPE_VAR;
121         }
122
123         fprintf(stderr, "BUG: Unknown map character '%c'\n", k);
124         return INT_MAX;
125 }
126
127 static struct meta_entry * lookup_meta_entry(struct bstr *kind)
128 {
129         int i;
130
131         for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++)
132                 if (!bstrcmp(kind, meta_table[i].kind) &&
133                     meta_table[i].id != 0)
134                         return &meta_table[i];
135
136         return NULL;
137 }
138
139 static struct meta_entry * lookup_meta_entry_byid(int id)
140 {
141         int i;
142
143         for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++)
144                 if (meta_table[i].id == id)
145                         return &meta_table[i];
146
147         return NULL;
148 }
149
150 static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val,
151                               struct tcf_meta_val *hdr)
152 {
153         __u32 t;
154
155         switch (TCF_META_TYPE(hdr->kind)) {
156                 case TCF_META_TYPE_INT:
157                         t = val;
158                         addattr_l(n, MAX_MSG, tlv, &t, sizeof(t));
159                         break;
160
161                 case TCF_META_TYPE_VAR:
162                         if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) {
163                                 struct bstr *a = (struct bstr *) val;
164                                 addattr_l(n, MAX_MSG, tlv, a->data, a->len);
165                         }
166                         break;
167         }
168 }
169
170 static inline int is_compatible(struct tcf_meta_val *what,
171                                 struct tcf_meta_val *needed)
172 {
173         char *p;
174         struct meta_entry *entry;
175
176         entry = lookup_meta_entry_byid(TCF_META_ID(what->kind));
177
178         if (entry == NULL)
179                 return 0;
180
181         for (p = entry->mask; p; p++)
182                 if (map_type(*p) == TCF_META_TYPE(needed->kind))
183                         return 1;
184
185         return 0;
186 }
187
188 static void list_meta_ids(FILE *fd)
189 {
190         int i;
191
192         fprintf(fd,
193             "--------------------------------------------------------\n" \
194             "  ID               Type       Description\n" \
195             "--------------------------------------------------------");
196
197         for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) {
198                 if (meta_table[i].id == TCF_META_ID_SECTION) {
199                         fprintf(fd, "\n%s:\n", meta_table[i].kind);
200                 } else {
201                         char *p = meta_table[i].mask;
202                         char buf[64] = {0};
203
204                         fprintf(fd, "  %-16s ", meta_table[i].kind);
205
206                         while (*p) {
207                                 int type = map_type(*p);
208
209                                 switch (type) {
210                                         case TCF_META_TYPE_INT:
211                                                 strcat(buf, "INT");
212                                                 break;
213
214                                         case TCF_META_TYPE_VAR:
215                                                 strcat(buf, "VAR");
216                                                 break;
217                                 }
218
219                                 if (*(++p))
220                                         strcat(buf, ",");
221                         }
222
223                         fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc);
224                 }
225         }
226
227         fprintf(fd,
228             "--------------------------------------------------------\n");
229 }
230
231 #undef TCF_META_ID_SECTION
232
233 #define PARSE_FAILURE ((void *) (-1))
234
235 #define PARSE_ERR(CARG, FMT, ARGS...) \
236         em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT ,##ARGS)
237
238 static inline int can_adopt(struct tcf_meta_val *val)
239 {
240         return !!TCF_META_ID(val->kind);
241 }
242
243 static inline int overwrite_type(struct tcf_meta_val *src,
244                                  struct tcf_meta_val *dst)
245 {
246         return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind);
247 }
248
249
250 static inline struct bstr *
251 parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj,
252              unsigned long *dst, struct tcf_meta_val *left)
253 {
254         struct meta_entry *entry;
255         unsigned long num;
256         struct bstr *a;
257
258         if (arg->quoted) {
259                 obj->kind = TCF_META_TYPE_VAR << 12;
260                 obj->kind |= TCF_META_ID_VALUE;
261                 *dst = (unsigned long) arg;
262                 return bstr_next(arg);
263         }
264
265         num = bstrtoul(arg);
266         if (num != LONG_MAX) {
267                 obj->kind = TCF_META_TYPE_INT << 12;
268                 obj->kind |= TCF_META_ID_VALUE;
269                 *dst = (unsigned long) num;
270                 return bstr_next(arg);
271         }
272
273         entry = lookup_meta_entry(arg);
274
275         if (entry == NULL) {
276                 PARSE_ERR(arg, "meta: unknown meta id\n");
277                 return PARSE_FAILURE;
278         }
279
280         obj->kind = entry->id | (map_type(entry->mask[0]) << 12);
281
282         if (left) {
283                 struct tcf_meta_val *right = obj;
284
285                 if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind))
286                         goto compatible;
287
288                 if (can_adopt(left) && !can_adopt(right)) {
289                         if (is_compatible(left, right))
290                                 left->kind = overwrite_type(left, right);
291                         else
292                                 goto not_compatible;
293                 } else if (can_adopt(right) && !can_adopt(left)) {
294                         if (is_compatible(right, left))
295                                 right->kind = overwrite_type(right, left);
296                         else
297                                 goto not_compatible;
298                 } else if (can_adopt(left) && can_adopt(right)) {
299                         if (is_compatible(left, right))
300                                 left->kind = overwrite_type(left, right);
301                         else if (is_compatible(right, left))
302                                 right->kind = overwrite_type(right, left);
303                         else
304                                 goto not_compatible;
305                 } else
306                         goto not_compatible;
307         }
308
309 compatible:
310
311         a = bstr_next(arg);
312
313         while(a) {
314                 if (!bstrcmp(a, "shift")) {
315                         unsigned long shift;
316
317                         if (a->next == NULL) {
318                                 PARSE_ERR(a, "meta: missing argument");
319                                 return PARSE_FAILURE;
320                         }
321                         a = bstr_next(a);
322
323                         shift = bstrtoul(a);
324                         if (shift == LONG_MAX) {
325                                 PARSE_ERR(a, "meta: invalid shift, must " \
326                                     "be numeric");
327                                 return PARSE_FAILURE;
328                         }
329
330                         obj->shift = (__u8) shift;
331                         a = bstr_next(a);
332                 } else if (!bstrcmp(a, "mask")) {
333                         unsigned long mask;
334
335                         if (a->next == NULL) {
336                                 PARSE_ERR(a, "meta: missing argument");
337                                 return PARSE_FAILURE;
338                         }
339                         a = bstr_next(a);
340
341                         mask = bstrtoul(a);
342                         if (mask == LONG_MAX) {
343                                 PARSE_ERR(a, "meta: invalid mask, must be " \
344                                     "numeric");
345                                 return PARSE_FAILURE;
346                         }
347                         *dst = (unsigned long) mask;
348                         a = bstr_next(a);
349                 } else
350                         break;
351         }
352
353         return a;
354
355 not_compatible:
356         PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
357         return PARSE_FAILURE;
358 }
359
360 static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
361                            struct bstr *args)
362 {
363         int opnd;
364         struct bstr *a;
365         struct tcf_meta_hdr meta_hdr;
366         unsigned long lvalue = 0, rvalue = 0;
367
368         memset(&meta_hdr, 0, sizeof(meta_hdr));
369
370         if (args == NULL)
371                 return PARSE_ERR(args, "meta: missing arguments");
372
373         if (!bstrcmp(args, "list")) {
374                 list_meta_ids(stderr);
375                 return -1;
376         }
377
378         a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL);
379         if (a == PARSE_FAILURE)
380                 return -1;
381         else if (a == NULL)
382                 return PARSE_ERR(args, "meta: missing operand");
383
384         if (!bstrcmp(a, "eq"))
385                 opnd = TCF_EM_OPND_EQ;
386         else if (!bstrcmp(a, "gt"))
387                 opnd = TCF_EM_OPND_GT;
388         else if (!bstrcmp(a, "lt"))
389                 opnd = TCF_EM_OPND_LT;
390         else
391                 return PARSE_ERR(a, "meta: invalid operand");
392
393         meta_hdr.left.op = (__u8) opnd;
394
395         if (a->next == NULL)
396                 return PARSE_ERR(args, "meta: missing rvalue");
397         a = bstr_next(a);
398
399         a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left);
400         if (a == PARSE_FAILURE)
401                 return -1;
402         else if (a != NULL)
403                 return PARSE_ERR(a, "meta: unexpected trailer");
404
405
406         addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
407
408         addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr));
409
410         dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left);
411         dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right);
412
413         return 0;
414 }
415 #undef PARSE_ERR
416
417 static inline void print_binary(FILE *fd, unsigned char *str, int len)
418 {
419         int i;
420
421         for (i = 0; i < len; i++)
422                 if (!isprint(str[i]))
423                         goto binary;
424
425         for (i = 0; i < len; i++)
426                 fprintf(fd, "%c", str[i]);
427         return;
428
429 binary:
430         for (i = 0; i < len; i++)
431                 fprintf(fd, "%02x ", str[i]);
432
433         fprintf(fd, "\"");
434         for (i = 0; i < len; i++)
435                 fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.');
436         fprintf(fd, "\"");
437 }
438
439 static inline int print_value(FILE *fd, int type, struct rtattr *rta)
440 {
441         if (rta == NULL) {
442                 fprintf(stderr, "Missing value TLV\n");
443                 return -1;
444         }
445
446         switch(type) {
447                 case TCF_META_TYPE_INT:
448                         if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
449                                 fprintf(stderr, "meta int type value TLV " \
450                                     "size mismatch.\n");
451                                 return -1;
452                         }
453                         fprintf(fd, "%d", *(__u32 *) RTA_DATA(rta));
454                         break;
455
456                 case TCF_META_TYPE_VAR:
457                         print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
458                         break;
459         }
460
461         return 0;
462 }
463
464 static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta)
465 {
466         int id = TCF_META_ID(obj->kind);
467         int type = TCF_META_TYPE(obj->kind);
468         struct meta_entry *entry;
469
470         if (id == TCF_META_ID_VALUE)
471                 return print_value(fd, type, rta);
472
473         entry = lookup_meta_entry_byid(id);
474
475         if (entry == NULL)
476                 fprintf(fd, "[unknown meta id %d]", id);
477         else
478                 fprintf(fd, "%s", entry->kind);
479
480         if (obj->shift)
481                 fprintf(fd, " shift %d", obj->shift);
482
483         switch (type) {
484                 case TCF_META_TYPE_INT:
485                         if (rta) {
486                                 if (RTA_PAYLOAD(rta) < sizeof(__u32))
487                                         goto size_mismatch;
488
489                                 fprintf(fd, " mask 0x%08x",
490                                     *(__u32*) RTA_DATA(rta));
491                         }
492                         break;
493         }
494
495         return 0;
496
497 size_mismatch:
498         fprintf(stderr, "meta int type mask TLV size mismatch\n");
499         return -1;
500 }
501
502
503 static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
504                            int data_len)
505 {
506         struct rtattr *tb[TCA_EM_META_MAX+1];
507         struct tcf_meta_hdr *meta_hdr;
508
509         if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0)
510                 return -1;
511
512         if (tb[TCA_EM_META_HDR] == NULL) {
513                 fprintf(stderr, "Missing meta header\n");
514                 return -1;
515         }
516
517         if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) {
518                 fprintf(stderr, "Meta header size mismatch\n");
519                 return -1;
520         }
521
522         meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]);
523
524         if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0)
525                 return -1;
526
527         switch (meta_hdr->left.op) {
528                 case TCF_EM_OPND_EQ:
529                         fprintf(fd, " eq ");
530                         break;
531                 case TCF_EM_OPND_LT:
532                         fprintf(fd, " lt ");
533                         break;
534                 case TCF_EM_OPND_GT:
535                         fprintf(fd, " gt ");
536                         break;
537         }
538
539         return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]);
540 }
541
542 struct ematch_util meta_ematch_util = {
543         .kind = "meta",
544         .kind_num = TCF_EM_META,
545         .parse_eopt = meta_parse_eopt,
546         .print_eopt = meta_print_eopt,
547         .print_usage = meta_print_usage
548 };