• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lib/route/cls/u32.c		u32 classifier
3  *
4  *	This library is free software; you can redistribute it and/or
5  *	modify it under the terms of the GNU Lesser General Public
6  *	License as published by the Free Software Foundation version 2.1
7  *	of the License.
8  *
9  * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
10  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
11  * Copyright (c) 2005-2006 Siemens AG Oesterreich
12  */
13 
14 /**
15  * @ingroup cls_api
16  * @defgroup u32 Universal 32-bit Classifier
17  *
18  * @{
19  */
20 
21 #include <netlink-local.h>
22 #include <netlink-tc.h>
23 #include <netlink/netlink.h>
24 #include <netlink/attr.h>
25 #include <netlink/utils.h>
26 #include <netlink/route/tc.h>
27 #include <netlink/route/classifier.h>
28 #include <netlink/route/classifier-modules.h>
29 #include <netlink/route/cls/u32.h>
30 
31 /** @cond SKIP */
32 #define U32_ATTR_DIVISOR      0x001
33 #define U32_ATTR_HASH         0x002
34 #define U32_ATTR_CLASSID      0x004
35 #define U32_ATTR_LINK         0x008
36 #define U32_ATTR_PCNT         0x010
37 #define U32_ATTR_SELECTOR     0x020
38 #define U32_ATTR_ACTION       0x040
39 #define U32_ATTR_POLICE       0x080
40 #define U32_ATTR_INDEV        0x100
41 /** @endcond */
42 
u32_selector(struct rtnl_u32 * u)43 static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
44 {
45 	return (struct tc_u32_sel *) u->cu_selector->d_data;
46 }
47 
u32_selector_alloc(struct rtnl_u32 * u)48 static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u)
49 {
50 	if (!u->cu_selector)
51 		u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel));
52 
53 	return u32_selector(u);
54 }
55 
56 static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
57 	[TCA_U32_DIVISOR]	= { .type = NLA_U32 },
58 	[TCA_U32_HASH]		= { .type = NLA_U32 },
59 	[TCA_U32_CLASSID]	= { .type = NLA_U32 },
60 	[TCA_U32_LINK]		= { .type = NLA_U32 },
61 	[TCA_U32_INDEV]		= { .type = NLA_STRING,
62 				    .maxlen = IFNAMSIZ },
63 	[TCA_U32_SEL]		= { .minlen = sizeof(struct tc_u32_sel) },
64 	[TCA_U32_PCNT]		= { .minlen = sizeof(struct tc_u32_pcnt) },
65 };
66 
u32_msg_parser(struct rtnl_cls * cls)67 static int u32_msg_parser(struct rtnl_cls *cls)
68 {
69 	struct rtnl_u32 *u = rtnl_cls_data(cls);
70 	struct nlattr *tb[TCA_U32_MAX + 1];
71 	int err;
72 
73 	err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy);
74 	if (err < 0)
75 		return err;
76 
77 	if (tb[TCA_U32_DIVISOR]) {
78 		u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
79 		u->cu_mask |= U32_ATTR_DIVISOR;
80 	}
81 
82 	if (tb[TCA_U32_SEL]) {
83 		u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]);
84 		if (!u->cu_selector)
85 			goto errout_nomem;
86 		u->cu_mask |= U32_ATTR_SELECTOR;
87 	}
88 
89 	if (tb[TCA_U32_HASH]) {
90 		u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]);
91 		u->cu_mask |= U32_ATTR_HASH;
92 	}
93 
94 	if (tb[TCA_U32_CLASSID]) {
95 		u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]);
96 		u->cu_mask |= U32_ATTR_CLASSID;
97 	}
98 
99 	if (tb[TCA_U32_LINK]) {
100 		u->cu_link = nla_get_u32(tb[TCA_U32_LINK]);
101 		u->cu_mask |= U32_ATTR_LINK;
102 	}
103 
104 	if (tb[TCA_U32_ACT]) {
105 		u->cu_act = nl_data_alloc_attr(tb[TCA_U32_ACT]);
106 		if (!u->cu_act)
107 			goto errout_nomem;
108 		u->cu_mask |= U32_ATTR_ACTION;
109 	}
110 
111 	if (tb[TCA_U32_POLICE]) {
112 		u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]);
113 		if (!u->cu_police)
114 			goto errout_nomem;
115 		u->cu_mask |= U32_ATTR_POLICE;
116 	}
117 
118 	if (tb[TCA_U32_PCNT]) {
119 		struct tc_u32_sel *sel;
120 		int pcnt_size;
121 
122 		if (!tb[TCA_U32_SEL]) {
123 			err = -NLE_MISSING_ATTR;
124 			goto errout;
125 		}
126 
127 		sel = u->cu_selector->d_data;
128 		pcnt_size = sizeof(struct tc_u32_pcnt) +
129 				(sel->nkeys * sizeof(uint64_t));
130 		if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) {
131 			err = -NLE_INVAL;
132 			goto errout;
133 		}
134 
135 		u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]);
136 		if (!u->cu_pcnt)
137 			goto errout_nomem;
138 		u->cu_mask |= U32_ATTR_PCNT;
139 	}
140 
141 	if (tb[TCA_U32_INDEV]) {
142 		nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ);
143 		u->cu_mask |= U32_ATTR_INDEV;
144 	}
145 
146 	return 0;
147 
148 errout_nomem:
149 	err = -NLE_NOMEM;
150 errout:
151 	return err;
152 }
153 
u32_free_data(struct rtnl_cls * cls)154 static void u32_free_data(struct rtnl_cls *cls)
155 {
156 	struct rtnl_u32 *u = rtnl_cls_data(cls);
157 
158 	nl_data_free(u->cu_selector);
159 	nl_data_free(u->cu_act);
160 	nl_data_free(u->cu_police);
161 	nl_data_free(u->cu_pcnt);
162 }
163 
u32_clone(struct rtnl_cls * _dst,struct rtnl_cls * _src)164 static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
165 {
166 	struct rtnl_u32 *dst = rtnl_cls_data(_dst);
167 	struct rtnl_u32 *src = rtnl_cls_data(_src);
168 
169 	if (src->cu_selector &&
170 	    !(dst->cu_selector = nl_data_clone(src->cu_selector)))
171 		return -NLE_NOMEM;
172 
173 	if (src->cu_act && !(dst->cu_act = nl_data_clone(src->cu_act)))
174 		return -NLE_NOMEM;
175 
176 	if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
177 		return -NLE_NOMEM;
178 
179 	if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
180 		return -NLE_NOMEM;
181 
182 	return 0;
183 }
184 
u32_dump_line(struct rtnl_cls * cls,struct nl_dump_params * p)185 static void u32_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
186 {
187 	struct rtnl_u32 *u = rtnl_cls_data(cls);
188 	char buf[32];
189 
190 	if (u->cu_mask & U32_ATTR_DIVISOR)
191 		nl_dump(p, " divisor %u", u->cu_divisor);
192 	else if (u->cu_mask & U32_ATTR_CLASSID)
193 		nl_dump(p, " target %s",
194 			rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf)));
195 }
196 
print_selector(struct nl_dump_params * p,struct tc_u32_sel * sel,struct rtnl_cls * cls,struct rtnl_u32 * u)197 static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
198 			   struct rtnl_cls *cls, struct rtnl_u32 *u)
199 {
200 	int i;
201 	struct tc_u32_key *key;
202 
203 	if (sel->hmask || sel->hoff) {
204 		/* I guess this will never be used since the kernel only
205 		 * exports the selector if no divisor is set but hash offset
206 		 * and hash mask make only sense in hash filters with divisor
207 		 * set */
208 		nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
209 	}
210 
211 	if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
212 		nl_dump(p, " offset at %u", sel->off);
213 
214 		if (sel->flags & TC_U32_VAROFFSET)
215 			nl_dump(p, " variable (at %u & 0x%x) >> %u",
216 				sel->offoff, ntohs(sel->offmask), sel->offshift);
217 	}
218 
219 	if (sel->flags) {
220 		int flags = sel->flags;
221 		nl_dump(p, " <");
222 
223 #define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
224 	flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
225 
226 		PRINT_FLAG(TERMINAL);
227 		PRINT_FLAG(OFFSET);
228 		PRINT_FLAG(VAROFFSET);
229 		PRINT_FLAG(EAT);
230 #undef PRINT_FLAG
231 
232 		nl_dump(p, ">");
233 	}
234 
235 
236 	for (i = 0; i < sel->nkeys; i++) {
237 		key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
238 
239 		nl_dump(p, "\n");
240 		nl_dump_line(p, "      match key at %s%u ",
241 			key->offmask ? "nexthdr+" : "", key->off);
242 
243 		if (key->offmask)
244 			nl_dump(p, "[0x%u] ", key->offmask);
245 
246 		nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
247 
248 		if (p->dp_type == NL_DUMP_STATS &&
249 		    (u->cu_mask & U32_ATTR_PCNT)) {
250 			struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data;
251 			nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
252 		}
253 	}
254 }
255 
u32_dump_details(struct rtnl_cls * cls,struct nl_dump_params * p)256 static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
257 {
258 	struct rtnl_u32 *u = rtnl_cls_data(cls);
259 	struct tc_u32_sel *s;
260 
261 	if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
262 		nl_dump(p, "no-selector\n");
263 		return;
264 	}
265 
266 	s = u->cu_selector->d_data;
267 
268 	nl_dump(p, "nkeys %u ", s->nkeys);
269 
270 	if (u->cu_mask & U32_ATTR_HASH)
271 		nl_dump(p, "ht key 0x%x hash 0x%u",
272 			TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
273 
274 	if (u->cu_mask & U32_ATTR_LINK)
275 		nl_dump(p, "link %u ", u->cu_link);
276 
277 	if (u->cu_mask & U32_ATTR_INDEV)
278 		nl_dump(p, "indev %s ", u->cu_indev);
279 
280 	print_selector(p, s, cls, u);
281 	nl_dump(p, "\n");
282 
283 #if 0
284 #define U32_ATTR_ACTION       0x040
285 #define U32_ATTR_POLICE       0x080
286 
287 	struct nl_data   act;
288 	struct nl_data   police;
289 #endif
290 }
291 
u32_dump_stats(struct rtnl_cls * cls,struct nl_dump_params * p)292 static void u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p)
293 {
294 	struct rtnl_u32 *u = rtnl_cls_data(cls);
295 
296 	if (u->cu_mask & U32_ATTR_PCNT) {
297 		struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
298 		nl_dump(p, "\n");
299 		nl_dump_line(p, "    hit %8llu count %8llu\n",
300 			     pc->rhit, pc->rcnt);
301 	}
302 }
303 
u32_get_opts(struct rtnl_cls * cls,struct nl_msg * msg)304 static int u32_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
305 {
306 	struct rtnl_u32 *u = rtnl_cls_data(cls);
307 
308 	if (u->cu_mask & U32_ATTR_DIVISOR)
309 		NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
310 
311 	if (u->cu_mask & U32_ATTR_HASH)
312 		NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash);
313 
314 	if (u->cu_mask & U32_ATTR_CLASSID)
315 		NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid);
316 
317 	if (u->cu_mask & U32_ATTR_LINK)
318 		NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link);
319 
320 	if (u->cu_mask & U32_ATTR_SELECTOR)
321 		NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
322 
323 	if (u->cu_mask & U32_ATTR_ACTION)
324 		NLA_PUT_DATA(msg, TCA_U32_ACT, u->cu_act);
325 
326 	if (u->cu_mask & U32_ATTR_POLICE)
327 		NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
328 
329 	if (u->cu_mask & U32_ATTR_INDEV)
330 		NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev);
331 
332 	return 0;
333 
334 nla_put_failure:
335 	return -NLE_NOMEM;
336 }
337 
338 /**
339  * @name Attribute Modifications
340  * @{
341  */
342 
rtnl_u32_set_handle(struct rtnl_cls * cls,int htid,int hash,int nodeid)343 void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
344 			 int nodeid)
345 {
346 	uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
347 
348 	tca_set_handle((struct rtnl_tca *) cls, handle );
349 }
350 
rtnl_u32_set_classid(struct rtnl_cls * cls,uint32_t classid)351 int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
352 {
353 	struct rtnl_u32 *u = rtnl_cls_data(cls);
354 
355 	u->cu_classid = classid;
356 	u->cu_mask |= U32_ATTR_CLASSID;
357 
358 	return 0;
359 }
360 
361 /** @} */
362 
363 /**
364  * @name Selector Modifications
365  * @{
366  */
367 
rtnl_u32_set_flags(struct rtnl_cls * cls,int flags)368 int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
369 {
370 	struct tc_u32_sel *sel;
371 	struct rtnl_u32 *u = rtnl_cls_data(cls);
372 
373 	sel = u32_selector_alloc(u);
374 	if (!sel)
375 		return -NLE_NOMEM;
376 
377 	sel->flags |= flags;
378 	u->cu_mask |= U32_ATTR_SELECTOR;
379 
380 	return 0;
381 }
382 
383 /**
384  * Append new 32-bit key to the selector
385  *
386  * @arg cls	classifier to be modifier
387  * @arg val	value to be matched (network byte-order)
388  * @arg mask	mask to be applied before matching (network byte-order)
389  * @arg off	offset, in bytes, to start matching
390  * @arg offmask	offset mask
391  *
392  * General selectors define the pattern, mask and offset the pattern will be
393  * matched to the packet contents. Using the general selectors you can match
394  * virtually any single bit in the IP (or upper layer) header.
395  *
396 */
rtnl_u32_add_key(struct rtnl_cls * cls,uint32_t val,uint32_t mask,int off,int offmask)397 int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
398 		     int off, int offmask)
399 {
400 	struct tc_u32_sel *sel;
401 	struct rtnl_u32 *u = rtnl_cls_data(cls);
402 	int err;
403 
404 	sel = u32_selector_alloc(u);
405 	if (!sel)
406 		return -NLE_NOMEM;
407 
408 	err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
409 	if (err < 0)
410 		return err;
411 
412 	/* the selector might have been moved by realloc */
413 	sel = u32_selector(u);
414 
415 	sel->keys[sel->nkeys].mask = mask;
416 	sel->keys[sel->nkeys].val = val & mask;
417 	sel->keys[sel->nkeys].off = off;
418 	sel->keys[sel->nkeys].offmask = offmask;
419 	sel->nkeys++;
420 	u->cu_mask |= U32_ATTR_SELECTOR;
421 
422 	return 0;
423 }
424 
rtnl_u32_add_key_uint8(struct rtnl_cls * cls,uint8_t val,uint8_t mask,int off,int offmask)425 int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
426 			   int off, int offmask)
427 {
428 	int shift = 24 - 8 * (off & 3);
429 
430 	return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
431 				htonl((uint32_t)mask << shift),
432 				off & ~3, offmask);
433 }
434 
435 /**
436  * Append new selector key to match a 16-bit number
437  *
438  * @arg cls	classifier to be modified
439  * @arg val	value to be matched (host byte-order)
440  * @arg mask	mask to be applied before matching (host byte-order)
441  * @arg off	offset, in bytes, to start matching
442  * @arg offmask	offset mask
443 */
rtnl_u32_add_key_uint16(struct rtnl_cls * cls,uint16_t val,uint16_t mask,int off,int offmask)444 int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask,
445 			    int off, int offmask)
446 {
447 	int shift = ((off & 3) == 0 ? 16 : 0);
448 	if (off % 2)
449 		return -NLE_INVAL;
450 
451 	return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
452 				htonl((uint32_t)mask << shift),
453 				off & ~3, offmask);
454 }
455 
456 /**
457  * Append new selector key to match a 32-bit number
458  *
459  * @arg cls	classifier to be modified
460  * @arg val	value to be matched (host byte-order)
461  * @arg mask	mask to be applied before matching (host byte-order)
462  * @arg off	offset, in bytes, to start matching
463  * @arg offmask	offset mask
464 */
rtnl_u32_add_key_uint32(struct rtnl_cls * cls,uint32_t val,uint32_t mask,int off,int offmask)465 int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
466 			    int off, int offmask)
467 {
468 	return rtnl_u32_add_key(cls, htonl(val), htonl(mask),
469 				off & ~3, offmask);
470 }
471 
rtnl_u32_add_key_in_addr(struct rtnl_cls * cls,struct in_addr * addr,uint8_t bitmask,int off,int offmask)472 int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr,
473 			     uint8_t bitmask, int off, int offmask)
474 {
475 	uint32_t mask = 0xFFFFFFFF << (32 - bitmask);
476 	return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask);
477 }
478 
rtnl_u32_add_key_in6_addr(struct rtnl_cls * cls,struct in6_addr * addr,uint8_t bitmask,int off,int offmask)479 int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
480 			      uint8_t bitmask, int off, int offmask)
481 {
482 	int i, err;
483 
484 	for (i = 1; i <= 4; i++) {
485 		if (32 * i - bitmask <= 0) {
486 			if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
487 						0xFFFFFFFF, off+4*(i-1), offmask)) < 0)
488 				return err;
489 		}
490 		else if (32 * i - bitmask < 32) {
491 			uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask);
492 			if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
493 						htonl(mask), off+4*(i-1), offmask)) < 0)
494 				return err;
495 		}
496 		/* otherwise, if (32*i - bitmask >= 32) no key is generated */
497 	}
498 
499 	return 0;
500 }
501 
502 /** @} */
503 
504 static struct rtnl_cls_ops u32_ops = {
505 	.co_kind		= "u32",
506 	.co_size		= sizeof(struct rtnl_u32),
507 	.co_msg_parser		= u32_msg_parser,
508 	.co_free_data		= u32_free_data,
509 	.co_clone		= u32_clone,
510 	.co_get_opts		= u32_get_opts,
511 	.co_dump = {
512 	    [NL_DUMP_LINE]	= u32_dump_line,
513 	    [NL_DUMP_DETAILS]	= u32_dump_details,
514 	    [NL_DUMP_STATS]	= u32_dump_stats,
515 	},
516 };
517 
u32_init(void)518 static void __init u32_init(void)
519 {
520 	rtnl_cls_register(&u32_ops);
521 }
522 
u32_exit(void)523 static void __exit u32_exit(void)
524 {
525 	rtnl_cls_unregister(&u32_ops);
526 }
527 
528 /** @} */
529