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 <errno.h>
22
23#include "m_ematch.h"
24#include <linux/tc_ematch/tc_em_meta.h>
25
26extern struct ematch_util meta_ematch_util;
27
28static 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" \
36	    "         meta(indev shift 1 eq \"ppp\")\n" \
37	    "         meta(tcindex mask 0xf0 eq 0xf0)\n" \
38	    "\n" \
39	    "For a list of meta identifiers, use meta(list).\n");
40}
41
42struct 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"),
63	__A(SECTION,		"Packet attributes", "", ""),
64	__A(PRIORITY,		"priority",	"i",
65				"Priority of packet"),
66	__A(PROTOCOL,		"protocol",	"i",
67				"Link layer protocol"),
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"),
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"),
90	__A(VLAN_TAG,		"vlan",		"i",	"Vlan tag"),
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
114static 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
125static 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];
133
134	return NULL;
135}
136
137static 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];
144
145	return NULL;
146}
147
148static 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
168static inline int is_compatible(struct tcf_meta_val *what,
169				struct tcf_meta_val *needed)
170{
171	char *p;
172	struct meta_entry *entry;
173
174	entry = lookup_meta_entry_byid(TCF_META_ID(what->kind));
175
176	if (entry == NULL)
177		return 0;
178
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
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
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}
246
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{
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);
264	if (num != ULONG_MAX) {
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;
282
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;
303		} else
304			goto not_compatible;
305	}
306
307compatible:
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);
320
321			shift = bstrtoul(a);
322			if (shift == ULONG_MAX) {
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);
338
339			mask = bstrtoul(a);
340			if (mask == ULONG_MAX) {
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;
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");
402
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
408	dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left);
409	dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right);
410
411	return 0;
412}
413#undef PARSE_ERR
414
415static 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
427binary:
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
437static 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			}
451			fprintf(fd, "%d", rta_getattr_u32(rta));
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
462static 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",
488				    rta_getattr_u32(rta));
489			}
490			break;
491	}
492
493	return 0;
494
495size_mismatch:
496	fprintf(stderr, "meta int type mask TLV size mismatch\n");
497	return -1;
498}
499
500
501static 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
540struct 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};
547