u32.c revision eed2afaab7aa72fae393a395a8879b91a922ff5e
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-2006 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
43static inline struct rtnl_u32 *u32_cls(struct rtnl_cls *cls)
44{
45	return (struct rtnl_u32 *) cls->c_subdata;
46}
47
48static inline struct rtnl_u32 *u32_alloc(struct rtnl_cls *cls)
49{
50	if (!cls->c_subdata)
51		cls->c_subdata = calloc(1, sizeof(struct rtnl_u32));
52
53	return u32_cls(cls);
54}
55
56static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
57{
58	return (struct tc_u32_sel *) u->cu_selector->d_data;
59}
60
61static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u)
62{
63	if (!u->cu_selector)
64		u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel));
65
66	return u32_selector(u);
67}
68
69static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
70	[TCA_U32_DIVISOR]	= { .type = NLA_U32 },
71	[TCA_U32_HASH]		= { .type = NLA_U32 },
72	[TCA_U32_CLASSID]	= { .type = NLA_U32 },
73	[TCA_U32_LINK]		= { .type = NLA_U32 },
74	[TCA_U32_INDEV]		= { .type = NLA_STRING,
75				    .maxlen = IFNAMSIZ },
76	[TCA_U32_SEL]		= { .minlen = sizeof(struct tc_u32_sel) },
77	[TCA_U32_PCNT]		= { .minlen = sizeof(struct tc_u32_pcnt) },
78};
79
80static int u32_msg_parser(struct rtnl_cls *cls)
81{
82	int err;
83	struct nlattr *tb[TCA_U32_MAX + 1];
84	struct rtnl_u32 *u;
85
86	err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy);
87	if (err < 0)
88		return err;
89
90	u = u32_alloc(cls);
91	if (!u)
92		goto errout_nomem;
93
94	if (tb[TCA_U32_DIVISOR]) {
95		u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
96		u->cu_mask |= U32_ATTR_DIVISOR;
97	}
98
99	if (tb[TCA_U32_SEL]) {
100		u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]);
101		if (!u->cu_selector)
102			goto errout_nomem;
103		u->cu_mask |= U32_ATTR_SELECTOR;
104	}
105
106	if (tb[TCA_U32_HASH]) {
107		u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]);
108		u->cu_mask |= U32_ATTR_HASH;
109	}
110
111	if (tb[TCA_U32_CLASSID]) {
112		u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]);
113		u->cu_mask |= U32_ATTR_CLASSID;
114	}
115
116	if (tb[TCA_U32_LINK]) {
117		u->cu_link = nla_get_u32(tb[TCA_U32_LINK]);
118		u->cu_mask |= U32_ATTR_LINK;
119	}
120
121	if (tb[TCA_U32_ACT]) {
122		u->cu_act = nl_data_alloc_attr(tb[TCA_U32_ACT]);
123		if (!u->cu_act)
124			goto errout_nomem;
125		u->cu_mask |= U32_ATTR_ACTION;
126	}
127
128	if (tb[TCA_U32_POLICE]) {
129		u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]);
130		if (!u->cu_police)
131			goto errout_nomem;
132		u->cu_mask |= U32_ATTR_POLICE;
133	}
134
135	if (tb[TCA_U32_PCNT]) {
136		struct tc_u32_sel *sel;
137		int pcnt_size;
138
139		if (!tb[TCA_U32_SEL]) {
140			err = -NLE_MISSING_ATTR;
141			goto errout;
142		}
143
144		sel = u->cu_selector->d_data;
145		pcnt_size = sizeof(struct tc_u32_pcnt) +
146				(sel->nkeys * sizeof(uint64_t));
147		if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) {
148			err = -NLE_INVAL;
149			goto errout;
150		}
151
152		u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]);
153		if (!u->cu_pcnt)
154			goto errout_nomem;
155		u->cu_mask |= U32_ATTR_PCNT;
156	}
157
158	if (tb[TCA_U32_INDEV]) {
159		nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ);
160		u->cu_mask |= U32_ATTR_INDEV;
161	}
162
163	return 0;
164
165errout_nomem:
166	err = -NLE_NOMEM;
167errout:
168	return err;
169}
170
171static void u32_free_data(struct rtnl_cls *cls)
172{
173	struct rtnl_u32 *u = u32_cls(cls);
174
175	if (!u)
176		return;
177
178	nl_data_free(u->cu_selector);
179	nl_data_free(u->cu_act);
180	nl_data_free(u->cu_police);
181	nl_data_free(u->cu_pcnt);
182
183	free(cls->c_subdata);
184}
185
186static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
187{
188	struct rtnl_u32 *dst, *src = u32_cls(_src);
189
190	if (!src)
191		return 0;
192
193	dst = u32_alloc(_dst);
194	if (!dst)
195		return -NLE_NOMEM;
196
197	if (src->cu_selector)
198		if (!(dst->cu_selector = nl_data_clone(src->cu_selector)))
199			return -NLE_NOMEM;
200
201	if (src->cu_act)
202		if (!(dst->cu_act = nl_data_clone(src->cu_act)))
203			return -NLE_NOMEM;
204
205	if (src->cu_police)
206		if (!(dst->cu_police = nl_data_clone(src->cu_police)))
207			return -NLE_NOMEM;
208
209	if (src->cu_pcnt)
210		if (!(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
211			return -NLE_NOMEM;
212
213	return 0;
214}
215
216static int u32_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p,
217			  int line)
218{
219	struct rtnl_u32 *u = u32_cls(cls);
220	char buf[32];
221
222	if (!u)
223		goto ignore;
224
225	if (u->cu_mask & U32_ATTR_DIVISOR)
226		dp_dump(p, " divisor %u", u->cu_divisor);
227	else if (u->cu_mask & U32_ATTR_CLASSID)
228		dp_dump(p, " target %s",
229			rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf)));
230
231ignore:
232	return line;
233}
234
235static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
236			  struct rtnl_cls *cls, struct rtnl_u32 *u, int line)
237{
238	int i;
239	struct tc_u32_key *key;
240
241	if (sel->hmask || sel->hoff) {
242		/* I guess this will never be used since the kernel only
243		 * exports the selector if no divisor is set but hash offset
244		 * and hash mask make only sense in hash filters with divisor
245		 * set */
246		dp_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
247	}
248
249	if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
250		dp_dump(p, " offset at %u", sel->off);
251
252		if (sel->flags & TC_U32_VAROFFSET)
253			dp_dump(p, " variable (at %u & 0x%x) >> %u",
254				sel->offoff, ntohs(sel->offmask), sel->offshift);
255	}
256
257	if (sel->flags) {
258		int flags = sel->flags;
259		dp_dump(p, " <");
260
261#define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
262	flags &= ~TC_U32_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
263
264		PRINT_FLAG(TERMINAL);
265		PRINT_FLAG(OFFSET);
266		PRINT_FLAG(VAROFFSET);
267		PRINT_FLAG(EAT);
268#undef PRINT_FLAG
269
270		dp_dump(p, ">");
271	}
272
273
274	for (i = 0; i < sel->nkeys; i++) {
275		key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
276
277		dp_dump(p, "\n");
278		dp_dump_line(p, line++, "      match key at %s%u ",
279		key->offmask ? "nexthdr+" : "", key->off);
280
281		if (key->offmask)
282			dp_dump(p, "[0x%u] ", key->offmask);
283
284		dp_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
285
286		if (p->dp_type == NL_DUMP_STATS &&
287		    (u->cu_mask & U32_ATTR_PCNT)) {
288			struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data;
289			dp_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
290		}
291	}
292
293	return line;
294}
295
296
297static int u32_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p,
298			 int line)
299{
300	struct rtnl_u32 *u = u32_cls(cls);
301	struct tc_u32_sel *s;
302
303	if (!u)
304		goto ignore;
305
306	if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
307		dp_dump(p, "no-selector\n");
308		return line;
309	}
310
311	s = u->cu_selector->d_data;
312
313	dp_dump(p, "nkeys %u ", s->nkeys);
314
315	if (u->cu_mask & U32_ATTR_HASH)
316		dp_dump(p, "ht key 0x%x hash 0x%u",
317			TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
318
319	if (u->cu_mask & U32_ATTR_LINK)
320		dp_dump(p, "link %u ", u->cu_link);
321
322	if (u->cu_mask & U32_ATTR_INDEV)
323		dp_dump(p, "indev %s ", u->cu_indev);
324
325	line = print_selector(p, s, cls, u, line);
326	dp_dump(p, "\n");
327
328ignore:
329	return line;
330
331#if 0
332#define U32_ATTR_ACTION       0x040
333#define U32_ATTR_POLICE       0x080
334
335	struct nl_data   act;
336	struct nl_data   police;
337#endif
338}
339
340static int u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p,
341			  int line)
342{
343	struct rtnl_u32 *u = u32_cls(cls);
344
345	if (!u)
346		goto ignore;
347
348	if (u->cu_mask & U32_ATTR_PCNT) {
349		struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
350		dp_dump(p, "\n");
351		dp_dump_line(p, line++, "%s         successful       hits\n");
352		dp_dump_line(p, line++, "%s           %8llu   %8llu\n",
353			     pc->rhit, pc->rcnt);
354	}
355
356ignore:
357	return line;
358}
359
360static struct nl_msg *u32_get_opts(struct rtnl_cls *cls)
361{
362	struct rtnl_u32 *u;
363	struct nl_msg *msg;
364
365	u = u32_cls(cls);
366	if (!u)
367		return NULL;
368
369	msg = nlmsg_alloc();
370	if (!msg)
371		return NULL;
372
373	if (u->cu_mask & U32_ATTR_DIVISOR)
374		nla_put_u32(msg, TCA_U32_DIVISOR, u->cu_divisor);
375
376	if (u->cu_mask & U32_ATTR_HASH)
377		nla_put_u32(msg, TCA_U32_HASH, u->cu_hash);
378
379	if (u->cu_mask & U32_ATTR_CLASSID)
380		nla_put_u32(msg, TCA_U32_CLASSID, u->cu_classid);
381
382	if (u->cu_mask & U32_ATTR_LINK)
383		nla_put_u32(msg, TCA_U32_LINK, u->cu_link);
384
385	if (u->cu_mask & U32_ATTR_SELECTOR)
386		nla_put_data(msg, TCA_U32_SEL, u->cu_selector);
387
388	if (u->cu_mask & U32_ATTR_ACTION)
389		nla_put_data(msg, TCA_U32_ACT, u->cu_act);
390
391	if (u->cu_mask & U32_ATTR_POLICE)
392		nla_put_data(msg, TCA_U32_POLICE, u->cu_police);
393
394	if (u->cu_mask & U32_ATTR_INDEV)
395		nla_put_string(msg, TCA_U32_INDEV, u->cu_indev);
396
397	return msg;
398}
399
400/**
401 * @name Attribute Modifications
402 * @{
403 */
404
405void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
406			 int nodeid)
407{
408	uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
409
410	tca_set_handle((struct rtnl_tca *) cls, handle );
411}
412
413int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
414{
415	struct rtnl_u32 *u;
416
417	u = u32_alloc(cls);
418	if (!u)
419		return -NLE_NOMEM;
420
421	u->cu_classid = classid;
422	u->cu_mask |= U32_ATTR_CLASSID;
423
424	return 0;
425}
426
427/** @} */
428
429/**
430 * @name Selector Modifications
431 * @{
432 */
433
434int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
435{
436	struct tc_u32_sel *sel;
437	struct rtnl_u32 *u;
438
439	u = u32_alloc(cls);
440	if (!u)
441		return -NLE_NOMEM;
442
443	sel = u32_selector_alloc(u);
444	if (!sel)
445		return -NLE_NOMEM;
446
447	sel->flags |= flags;
448	u->cu_mask |= U32_ATTR_SELECTOR;
449
450	return 0;
451}
452
453/**
454 * Append new 32-bit key to the selector
455 *
456 * @arg cls	classifier to be modifier
457 * @arg val	value to be matched (network byte-order)
458 * @arg mask	mask to be applied before matching (network byte-order)
459 * @arg off	offset, in bytes, to start matching
460 * @arg offmask	offset mask
461 *
462 * General selectors define the pattern, mask and offset the pattern will be
463 * matched to the packet contents. Using the general selectors you can match
464 * virtually any single bit in the IP (or upper layer) header.
465 *
466*/
467int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
468		     int off, int offmask)
469{
470	struct tc_u32_sel *sel;
471	struct rtnl_u32 *u;
472	int err;
473
474	u = u32_alloc(cls);
475	if (!u)
476		return -NLE_NOMEM;
477
478	sel = u32_selector_alloc(u);
479	if (!sel)
480		return -NLE_NOMEM;
481
482	err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
483	if (err < 0)
484		return err;
485
486	/* the selector might have been moved by realloc */
487	sel = u32_selector(u);
488
489	sel->keys[sel->nkeys].mask = mask;
490	sel->keys[sel->nkeys].val = val & mask;
491	sel->keys[sel->nkeys].off = off;
492	sel->keys[sel->nkeys].offmask = offmask;
493	sel->nkeys++;
494	u->cu_mask |= U32_ATTR_SELECTOR;
495
496	return 0;
497}
498
499int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
500			   int off, int offmask)
501{
502	int shift = 24 - 8 * (off & 3);
503
504	return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
505				htonl((uint32_t)mask << shift),
506				off & ~3, offmask);
507}
508
509/**
510 * Append new selector key to match a 16-bit number
511 *
512 * @arg cls	classifier to be modified
513 * @arg val	value to be matched (host byte-order)
514 * @arg mask	mask to be applied before matching (host byte-order)
515 * @arg off	offset, in bytes, to start matching
516 * @arg offmask	offset mask
517*/
518int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask,
519			    int off, int offmask)
520{
521	int shift = ((off & 3) == 0 ? 16 : 0);
522	if (off % 2)
523		return -NLE_INVAL;
524
525	return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
526				htonl((uint32_t)mask << shift),
527				off & ~3, offmask);
528}
529
530/**
531 * Append new selector key to match a 32-bit number
532 *
533 * @arg cls	classifier to be modified
534 * @arg val	value to be matched (host byte-order)
535 * @arg mask	mask to be applied before matching (host byte-order)
536 * @arg off	offset, in bytes, to start matching
537 * @arg offmask	offset mask
538*/
539int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
540			    int off, int offmask)
541{
542	return rtnl_u32_add_key(cls, htonl(val), htonl(mask),
543				off & ~3, offmask);
544}
545
546int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr,
547			     uint8_t bitmask, int off, int offmask)
548{
549	uint32_t mask = 0xFFFFFFFF << (32 - bitmask);
550	return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask);
551}
552
553int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
554			      uint8_t bitmask, int off, int offmask)
555{
556	int i, err;
557
558	for (i = 1; i <= 4; i++) {
559		if (32 * i - bitmask <= 0) {
560			if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
561						0xFFFFFFFF, off+4*(i-1), offmask)) < 0)
562				return err;
563		}
564		else if (32 * i - bitmask < 32) {
565			uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask);
566			if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
567						htonl(mask), off+4*(i-1), offmask)) < 0)
568				return err;
569		}
570		/* otherwise, if (32*i - bitmask >= 32) no key is generated */
571	}
572
573	return 0;
574}
575
576/** @} */
577
578static struct rtnl_cls_ops u32_ops = {
579	.co_kind		= "u32",
580	.co_msg_parser		= u32_msg_parser,
581	.co_free_data		= u32_free_data,
582	.co_clone		= u32_clone,
583	.co_get_opts		= u32_get_opts,
584	.co_dump[NL_DUMP_BRIEF]	= u32_dump_brief,
585	.co_dump[NL_DUMP_FULL]	= u32_dump_full,
586	.co_dump[NL_DUMP_STATS]	= u32_dump_stats,
587};
588
589static void __init u32_init(void)
590{
591	rtnl_cls_register(&u32_ops);
592}
593
594static void __exit u32_exit(void)
595{
596	rtnl_cls_unregister(&u32_ops);
597}
598
599/** @} */
600