]> git.proxmox.com Git - mirror_iproute2.git/blame - tc/em_meta.c
tc: implement support for terse dump
[mirror_iproute2.git] / tc / em_meta.c
CommitLineData
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
25extern struct ematch_util meta_ematch_util;
26
27static 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 41static 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
113static 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 124static 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 136static 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
147static 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
168static 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
186static 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
236static inline int can_adopt(struct tcf_meta_val *val)
237{
238 return !!TCF_META_ID(val->kind);
239}
240
241static 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
248static inline struct bstr *
249parse_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
307compatible:
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
353not_compatible:
354 PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
355 return PARSE_FAILURE;
356}
357
358static 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
413static 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
425binary:
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
435static 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
460static 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
494size_mismatch:
495 fprintf(stderr, "meta int type mask TLV size mismatch\n");
496 return -1;
497}
498
499
500static 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
539struct 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};