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