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
43static 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
48static 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
56static 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
67static 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
148errout_nomem:
149	err = -NLE_NOMEM;
150errout:
151	return err;
152}
153
154static 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
164static 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
185static 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
197static 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
256static 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
292static 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
304static 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
334nla_put_failure:
335	return -NLE_NOMEM;
336}
337
338/**
339 * @name Attribute Modifications
340 * @{
341 */
342
343void 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
351int 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
368int 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*/
397int 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
425int 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*/
444int 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*/
465int 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
472int 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
479int 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
504static 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
518static void __init u32_init(void)
519{
520	rtnl_cls_register(&u32_ops);
521}
522
523static void __exit u32_exit(void)
524{
525	rtnl_cls_unregister(&u32_ops);
526}
527
528/** @} */
529