Home | History | Annotate | Download | only in tc
      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 (at) 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 
     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(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 
     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 
    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 
    125 static 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 
    137 static 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 
    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 				addattr_l(n, MAX_MSG, tlv, a->data, a->len);
    163 			}
    164 			break;
    165 	}
    166 }
    167 
    168 static 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 
    186 static 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 
    236 static inline int can_adopt(struct tcf_meta_val *val)
    237 {
    238 	return !!TCF_META_ID(val->kind);
    239 }
    240 
    241 static 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 
    248 static inline struct bstr *
    249 parse_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 
    307 compatible:
    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 
    353 not_compatible:
    354 	PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
    355 	return PARSE_FAILURE;
    356 }
    357 
    358 static 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 
    415 static 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 
    427 binary:
    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 
    437 static 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", *(__u32 *) RTA_DATA(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 
    462 static 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 				    *(__u32*) RTA_DATA(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 
    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