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