Home | History | Annotate | Download | only in tc
      1 /*
      2  * f_flow.c		Flow filter
      3  *
      4  * 		This program is free software; you can redistribute 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:	Patrick McHardy <kaber (at) trash.net>
     10  */
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <unistd.h>
     14 #include <string.h>
     15 #include <errno.h>
     16 
     17 #include "utils.h"
     18 #include "tc_util.h"
     19 #include "m_ematch.h"
     20 
     21 static void explain(void)
     22 {
     23 	fprintf(stderr,
     24 "Usage: ... flow ...\n"
     25 "\n"
     26 " [mapping mode]: map key KEY [ OPS ] ...\n"
     27 " [hashing mode]: hash keys KEY-LIST ... [ perturb SECS ]\n"
     28 "\n"
     29 "                 [ divisor NUM ] [ baseclass ID ] [ match EMATCH_TREE ]\n"
     30 "                 [ police POLICE_SPEC ] [ action ACTION_SPEC ]\n"
     31 "\n"
     32 "KEY-LIST := [ KEY-LIST , ] KEY\n"
     33 "KEY      := [ src | dst | proto | proto-src | proto-dst | iif | priority | \n"
     34 "              mark | nfct | nfct-src | nfct-dst | nfct-proto-src | \n"
     35 "              nfct-proto-dst | rt-classid | sk-uid | sk-gid |\n"
     36 "              vlan-tag ]\n"
     37 "OPS      := [ or NUM | and NUM | xor NUM | rshift NUM | addend NUM ]\n"
     38 "ID       := X:Y\n"
     39 	);
     40 }
     41 
     42 static const char *flow_keys[FLOW_KEY_MAX+1] = {
     43 	[FLOW_KEY_SRC]			= "src",
     44 	[FLOW_KEY_DST]			= "dst",
     45 	[FLOW_KEY_PROTO]		= "proto",
     46 	[FLOW_KEY_PROTO_SRC]		= "proto-src",
     47 	[FLOW_KEY_PROTO_DST]		= "proto-dst",
     48 	[FLOW_KEY_IIF]			= "iif",
     49 	[FLOW_KEY_PRIORITY]		= "priority",
     50 	[FLOW_KEY_MARK]			= "mark",
     51 	[FLOW_KEY_NFCT]			= "nfct",
     52 	[FLOW_KEY_NFCT_SRC]		= "nfct-src",
     53 	[FLOW_KEY_NFCT_DST]		= "nfct-dst",
     54 	[FLOW_KEY_NFCT_PROTO_SRC]	= "nfct-proto-src",
     55 	[FLOW_KEY_NFCT_PROTO_DST]	= "nfct-proto-dst",
     56 	[FLOW_KEY_RTCLASSID]		= "rt-classid",
     57 	[FLOW_KEY_SKUID]		= "sk-uid",
     58 	[FLOW_KEY_SKGID]		= "sk-gid",
     59 	[FLOW_KEY_VLAN_TAG]		= "vlan-tag",
     60 };
     61 
     62 static int flow_parse_keys(__u32 *keys, __u32 *nkeys, char *argv)
     63 {
     64 	char *s, *sep;
     65 	unsigned int i;
     66 
     67 	*keys = 0;
     68 	*nkeys = 0;
     69 	s = argv;
     70 	while (s != NULL) {
     71 		sep = strchr(s, ',');
     72 		if (sep)
     73 			*sep = '\0';
     74 
     75 		for (i = 0; i <= FLOW_KEY_MAX; i++) {
     76 			if (matches(s, flow_keys[i]) == 0) {
     77 				*keys |= 1 << i;
     78 				(*nkeys)++;
     79 				break;
     80 			}
     81 		}
     82 		if (i > FLOW_KEY_MAX) {
     83 			fprintf(stderr, "Unknown flow key \"%s\"\n", s);
     84 			return -1;
     85 		}
     86 		s = sep ? sep + 1 : NULL;
     87 	}
     88 	return 0;
     89 }
     90 
     91 static void transfer_bitop(__u32 *mask, __u32 *xor, __u32 m, __u32 x)
     92 {
     93 	*xor = x ^ (*xor & m);
     94 	*mask &= m;
     95 }
     96 
     97 static int get_addend(__u32 *addend, char *argv, __u32 keys)
     98 {
     99 	inet_prefix addr;
    100 	int sign = 0;
    101 	__u32 tmp;
    102 
    103 	if (*argv == '-') {
    104 		sign = 1;
    105 		argv++;
    106 	}
    107 
    108 	if (get_u32(&tmp, argv, 0) == 0)
    109 		goto out;
    110 
    111 	if (keys & (FLOW_KEY_SRC | FLOW_KEY_DST |
    112 		    FLOW_KEY_NFCT_SRC | FLOW_KEY_NFCT_DST) &&
    113 	    get_addr(&addr, argv, AF_UNSPEC) == 0) {
    114 		switch (addr.family) {
    115 		case AF_INET:
    116 			tmp = ntohl(addr.data[0]);
    117 			goto out;
    118 		case AF_INET6:
    119 			tmp = ntohl(addr.data[3]);
    120 			goto out;
    121 		}
    122 	}
    123 
    124 	return -1;
    125 out:
    126 	if (sign)
    127 		tmp = -tmp;
    128 	*addend = tmp;
    129 	return 0;
    130 }
    131 
    132 static int flow_parse_opt(struct filter_util *fu, char *handle,
    133 			  int argc, char **argv, struct nlmsghdr *n)
    134 {
    135 	struct tc_police tp;
    136 	struct tcmsg *t = NLMSG_DATA(n);
    137 	struct rtattr *tail;
    138 	__u32 mask = ~0U, xor = 0;
    139 	__u32 keys = 0, nkeys = 0;
    140 	__u32 mode = FLOW_MODE_MAP;
    141 	__u32 tmp;
    142 
    143 	memset(&tp, 0, sizeof(tp));
    144 
    145 	if (handle) {
    146 		if (get_u32(&t->tcm_handle, handle, 0)) {
    147 			fprintf(stderr, "Illegal \"handle\"\n");
    148 			return -1;
    149 		}
    150 	}
    151 
    152 	tail = NLMSG_TAIL(n);
    153 	addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
    154 
    155 	while (argc > 0) {
    156 		if (matches(*argv, "map") == 0) {
    157 			mode = FLOW_MODE_MAP;
    158 		} else if (matches(*argv, "hash") == 0) {
    159 			mode = FLOW_MODE_HASH;
    160 		} else if (matches(*argv, "keys") == 0) {
    161 			NEXT_ARG();
    162 			if (flow_parse_keys(&keys, &nkeys, *argv))
    163 				return -1;
    164 			addattr32(n, 4096, TCA_FLOW_KEYS, keys);
    165 		} else if (matches(*argv, "and") == 0) {
    166 			NEXT_ARG();
    167 			if (get_u32(&tmp, *argv, 0)) {
    168 				fprintf(stderr, "Illegal \"mask\"\n");
    169 				return -1;
    170 			}
    171 			transfer_bitop(&mask, &xor, tmp, 0);
    172 		} else if (matches(*argv, "or") == 0) {
    173 			NEXT_ARG();
    174 			if (get_u32(&tmp, *argv, 0)) {
    175 				fprintf(stderr, "Illegal \"or\"\n");
    176 				return -1;
    177 			}
    178 			transfer_bitop(&mask, &xor, ~tmp, tmp);
    179 		} else if (matches(*argv, "xor") == 0) {
    180 			NEXT_ARG();
    181 			if (get_u32(&tmp, *argv, 0)) {
    182 				fprintf(stderr, "Illegal \"xor\"\n");
    183 				return -1;
    184 			}
    185 			transfer_bitop(&mask, &xor, ~0, tmp);
    186 		} else if (matches(*argv, "rshift") == 0) {
    187 			NEXT_ARG();
    188 			if (get_u32(&tmp, *argv, 0)) {
    189 				fprintf(stderr, "Illegal \"rshift\"\n");
    190 				return -1;
    191 			}
    192 			addattr32(n, 4096, TCA_FLOW_RSHIFT, tmp);
    193 		} else if (matches(*argv, "addend") == 0) {
    194 			NEXT_ARG();
    195 			if (get_addend(&tmp, *argv, keys)) {
    196 				fprintf(stderr, "Illegal \"addend\"\n");
    197 				return -1;
    198 			}
    199 			addattr32(n, 4096, TCA_FLOW_ADDEND, tmp);
    200 		} else if (matches(*argv, "divisor") == 0) {
    201 			NEXT_ARG();
    202 			if (get_u32(&tmp, *argv, 0)) {
    203 				fprintf(stderr, "Illegal \"divisor\"\n");
    204 				return -1;
    205 			}
    206 			addattr32(n, 4096, TCA_FLOW_DIVISOR, tmp);
    207 		} else if (matches(*argv, "baseclass") == 0) {
    208 			NEXT_ARG();
    209 			if (get_tc_classid(&tmp, *argv) || TC_H_MIN(tmp) == 0) {
    210 				fprintf(stderr, "Illegal \"baseclass\"\n");
    211 				return -1;
    212 			}
    213 			addattr32(n, 4096, TCA_FLOW_BASECLASS, tmp);
    214 		} else if (matches(*argv, "perturb") == 0) {
    215 			NEXT_ARG();
    216 			if (get_u32(&tmp, *argv, 0)) {
    217 				fprintf(stderr, "Illegal \"perturb\"\n");
    218 				return -1;
    219 			}
    220 			addattr32(n, 4096, TCA_FLOW_PERTURB, tmp);
    221 		} else if (matches(*argv, "police") == 0) {
    222 			NEXT_ARG();
    223 			if (parse_police(&argc, &argv, TCA_FLOW_POLICE, n)) {
    224 				fprintf(stderr, "Illegal \"police\"\n");
    225 				return -1;
    226 			}
    227 			continue;
    228 		} else if (matches(*argv, "action") == 0) {
    229 			NEXT_ARG();
    230 			if (parse_action(&argc, &argv, TCA_FLOW_ACT, n)) {
    231 				fprintf(stderr, "Illegal \"action\"\n");
    232 				return -1;
    233 			}
    234 			continue;
    235 		} else if (matches(*argv, "match") == 0) {
    236 			NEXT_ARG();
    237 			if (parse_ematch(&argc, &argv, TCA_FLOW_EMATCHES, n)) {
    238 				fprintf(stderr, "Illegal \"ematch\"\n");
    239 				return -1;
    240 			}
    241 			continue;
    242 		} else if (matches(*argv, "help") == 0) {
    243 			explain();
    244 			return -1;
    245 		} else {
    246 			fprintf(stderr, "What is \"%s\"?\n", *argv);
    247 			explain();
    248 			return -1;
    249 		}
    250 		argv++, argc--;
    251 	}
    252 
    253 	if (nkeys > 1 && mode != FLOW_MODE_HASH) {
    254 		fprintf(stderr, "Invalid mode \"map\" for multiple keys\n");
    255 		return -1;
    256 	}
    257 	addattr32(n, 4096, TCA_FLOW_MODE, mode);
    258 
    259 	if (mask != ~0 || xor != 0) {
    260 		addattr32(n, 4096, TCA_FLOW_MASK, mask);
    261 		addattr32(n, 4096, TCA_FLOW_XOR, xor);
    262 	}
    263 
    264 	tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
    265 	return 0;
    266 }
    267 
    268 static int flow_print_opt(struct filter_util *fu, FILE *f, struct rtattr *opt,
    269 			  __u32 handle)
    270 {
    271 	struct rtattr *tb[TCA_FLOW_MAX+1];
    272 	SPRINT_BUF(b1);
    273 	unsigned int i;
    274 	__u32 mask = ~0, val = 0;
    275 
    276 	if (opt == NULL)
    277 		return -EINVAL;
    278 
    279 	parse_rtattr_nested(tb, TCA_FLOW_MAX, opt);
    280 
    281 	fprintf(f, "handle 0x%x ", handle);
    282 
    283 	if (tb[TCA_FLOW_MODE]) {
    284 		__u32 mode = *(__u32 *)RTA_DATA(tb[TCA_FLOW_MODE]);
    285 
    286 		switch (mode) {
    287 		case FLOW_MODE_MAP:
    288 			fprintf(f, "map ");
    289 			break;
    290 		case FLOW_MODE_HASH:
    291 			fprintf(f, "hash ");
    292 			break;
    293 		}
    294 	}
    295 
    296 	if (tb[TCA_FLOW_KEYS]) {
    297 		__u32 keymask = *(__u32 *)RTA_DATA(tb[TCA_FLOW_KEYS]);
    298 		char *sep = "";
    299 
    300 		fprintf(f, "keys ");
    301 		for (i = 0; i <= FLOW_KEY_MAX; i++) {
    302 			if (keymask & (1 << i)) {
    303 				fprintf(f, "%s%s", sep, flow_keys[i]);
    304 				sep = ",";
    305 			}
    306 		}
    307 		fprintf(f, " ");
    308 	}
    309 
    310 	if (tb[TCA_FLOW_MASK])
    311 		mask = *(__u32 *)RTA_DATA(tb[TCA_FLOW_MASK]);
    312 	if (tb[TCA_FLOW_XOR])
    313 		val = *(__u32 *)RTA_DATA(tb[TCA_FLOW_XOR]);
    314 
    315 	if (mask != ~0 || val != 0) {
    316 		__u32 or = (mask & val) ^ val;
    317 		__u32 xor = mask & val;
    318 
    319 		if (mask != ~0)
    320 			fprintf(f, "and 0x%.8x ", mask);
    321 		if (xor != 0)
    322 			fprintf(f, "xor 0x%.8x ", xor);
    323 		if (or != 0)
    324 			fprintf(f, "or 0x%.8x ", or);
    325 	}
    326 
    327 	if (tb[TCA_FLOW_RSHIFT])
    328 		fprintf(f, "rshift %u ",
    329 			*(__u32 *)RTA_DATA(tb[TCA_FLOW_RSHIFT]));
    330 	if (tb[TCA_FLOW_ADDEND])
    331 		fprintf(f, "addend 0x%x ",
    332 			*(__u32 *)RTA_DATA(tb[TCA_FLOW_ADDEND]));
    333 
    334 	if (tb[TCA_FLOW_DIVISOR])
    335 		fprintf(f, "divisor %u ",
    336 			*(__u32 *)RTA_DATA(tb[TCA_FLOW_DIVISOR]));
    337 	if (tb[TCA_FLOW_BASECLASS])
    338 		fprintf(f, "baseclass %s ",
    339 			sprint_tc_classid(*(__u32 *)RTA_DATA(tb[TCA_FLOW_BASECLASS]), b1));
    340 
    341 	if (tb[TCA_FLOW_PERTURB])
    342 		fprintf(f, "perturb %usec ",
    343 			*(__u32 *)RTA_DATA(tb[TCA_FLOW_PERTURB]));
    344 
    345 	if (tb[TCA_FLOW_EMATCHES])
    346 		print_ematch(f, tb[TCA_FLOW_EMATCHES]);
    347 	if (tb[TCA_FLOW_POLICE])
    348 		tc_print_police(f, tb[TCA_FLOW_POLICE]);
    349 	if (tb[TCA_FLOW_ACT]) {
    350 		fprintf(f, "\n");
    351 		tc_print_action(f, tb[TCA_FLOW_ACT]);
    352 	}
    353 	return 0;
    354 }
    355 
    356 struct filter_util flow_filter_util = {
    357 	.id		= "flow",
    358 	.parse_fopt	= flow_parse_opt,
    359 	.print_fopt	= flow_print_opt,
    360 };
    361