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 
26 extern struct ematch_util meta_ematch_util;
27 
meta_print_usage(FILE * fd)28 static 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(nf_mark 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 
42 struct 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 
map_type(char k)114 static 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 
lookup_meta_entry(struct bstr * kind)125 static struct meta_entry *lookup_meta_entry(struct bstr *kind)
126 {
127 	int i;
128 
129 	for (i = 0; i < ARRAY_SIZE(meta_table); 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 
lookup_meta_entry_byid(int id)137 static struct meta_entry *lookup_meta_entry_byid(int id)
138 {
139 	int i;
140 
141 	for (i = 0; i < ARRAY_SIZE(meta_table); i++)
142 		if (meta_table[i].id == id)
143 			return &meta_table[i];
144 
145 	return NULL;
146 }
147 
dump_value(struct nlmsghdr * n,int tlv,unsigned long val,struct tcf_meta_val * hdr)148 static 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 
163 				addattr_l(n, MAX_MSG, tlv, a->data, a->len);
164 			}
165 			break;
166 	}
167 }
168 
is_compatible(struct tcf_meta_val * what,struct tcf_meta_val * needed)169 static 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 
list_meta_ids(FILE * fd)187 static 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 < ARRAY_SIZE(meta_table); 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 
can_adopt(struct tcf_meta_val * val)237 static inline int can_adopt(struct tcf_meta_val *val)
238 {
239 	return !!TCF_META_ID(val->kind);
240 }
241 
overwrite_type(struct tcf_meta_val * src,struct tcf_meta_val * dst)242 static 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 
249 static inline struct bstr *
parse_object(struct bstr * args,struct bstr * arg,struct tcf_meta_val * obj,unsigned long * dst,struct tcf_meta_val * left)250 parse_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 != ULONG_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 
308 compatible:
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 == ULONG_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 == ULONG_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 
354 not_compatible:
355 	PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
356 	return PARSE_FAILURE;
357 }
358 
meta_parse_eopt(struct nlmsghdr * n,struct tcf_ematch_hdr * hdr,struct bstr * args)359 static 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 	if (args == NULL)
368 		return PARSE_ERR(args, "meta: missing arguments");
369 
370 	if (!bstrcmp(args, "list")) {
371 		list_meta_ids(stderr);
372 		return -1;
373 	}
374 
375 	a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL);
376 	if (a == PARSE_FAILURE)
377 		return -1;
378 	else if (a == NULL)
379 		return PARSE_ERR(args, "meta: missing operand");
380 
381 	if (!bstrcmp(a, "eq"))
382 		opnd = TCF_EM_OPND_EQ;
383 	else if (!bstrcmp(a, "gt"))
384 		opnd = TCF_EM_OPND_GT;
385 	else if (!bstrcmp(a, "lt"))
386 		opnd = TCF_EM_OPND_LT;
387 	else
388 		return PARSE_ERR(a, "meta: invalid operand");
389 
390 	meta_hdr.left.op = (__u8) opnd;
391 
392 	if (a->next == NULL)
393 		return PARSE_ERR(args, "meta: missing rvalue");
394 	a = bstr_next(a);
395 
396 	a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left);
397 	if (a == PARSE_FAILURE)
398 		return -1;
399 	else if (a != NULL)
400 		return PARSE_ERR(a, "meta: unexpected trailer");
401 
402 
403 	addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
404 
405 	addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr));
406 
407 	dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left);
408 	dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right);
409 
410 	return 0;
411 }
412 #undef PARSE_ERR
413 
print_binary(FILE * fd,unsigned char * str,int len)414 static inline void print_binary(FILE *fd, unsigned char *str, int len)
415 {
416 	int i;
417 
418 	for (i = 0; i < len; i++)
419 		if (!isprint(str[i]))
420 			goto binary;
421 
422 	for (i = 0; i < len; i++)
423 		fprintf(fd, "%c", str[i]);
424 	return;
425 
426 binary:
427 	for (i = 0; i < len; i++)
428 		fprintf(fd, "%02x ", str[i]);
429 
430 	fprintf(fd, "\"");
431 	for (i = 0; i < len; i++)
432 		fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.');
433 	fprintf(fd, "\"");
434 }
435 
print_value(FILE * fd,int type,struct rtattr * rta)436 static inline int print_value(FILE *fd, int type, struct rtattr *rta)
437 {
438 	if (rta == NULL) {
439 		fprintf(stderr, "Missing value TLV\n");
440 		return -1;
441 	}
442 
443 	switch (type) {
444 		case TCF_META_TYPE_INT:
445 			if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
446 				fprintf(stderr, "meta int type value TLV " \
447 				    "size mismatch.\n");
448 				return -1;
449 			}
450 			fprintf(fd, "%d", rta_getattr_u32(rta));
451 			break;
452 
453 		case TCF_META_TYPE_VAR:
454 			print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
455 			break;
456 	}
457 
458 	return 0;
459 }
460 
print_object(FILE * fd,struct tcf_meta_val * obj,struct rtattr * rta)461 static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta)
462 {
463 	int id = TCF_META_ID(obj->kind);
464 	int type = TCF_META_TYPE(obj->kind);
465 	struct meta_entry *entry;
466 
467 	if (id == TCF_META_ID_VALUE)
468 		return print_value(fd, type, rta);
469 
470 	entry = lookup_meta_entry_byid(id);
471 
472 	if (entry == NULL)
473 		fprintf(fd, "[unknown meta id %d]", id);
474 	else
475 		fprintf(fd, "%s", entry->kind);
476 
477 	if (obj->shift)
478 		fprintf(fd, " shift %d", obj->shift);
479 
480 	switch (type) {
481 		case TCF_META_TYPE_INT:
482 			if (rta) {
483 				if (RTA_PAYLOAD(rta) < sizeof(__u32))
484 					goto size_mismatch;
485 
486 				if (rta_getattr_u32(rta))
487 					fprintf(fd, " mask 0x%08x",
488 						rta_getattr_u32(rta));
489 			}
490 			break;
491 	}
492 
493 	return 0;
494 
495 size_mismatch:
496 	fprintf(stderr, "meta int type mask TLV size mismatch\n");
497 	return -1;
498 }
499 
500 
meta_print_eopt(FILE * fd,struct tcf_ematch_hdr * hdr,void * data,int data_len)501 static 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 
540 struct 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