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