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