2 * lib/route/cls/u32.c u32 classifier
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
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
16 * @defgroup u32 Universal 32-bit Classifier
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>
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
43 static inline struct rtnl_u32 *u32_cls(struct rtnl_cls *cls)
45 return (struct rtnl_u32 *) cls->c_subdata;
48 static inline struct rtnl_u32 *u32_alloc(struct rtnl_cls *cls)
51 cls->c_subdata = calloc(1, sizeof(struct rtnl_u32));
56 static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
58 return (struct tc_u32_sel *) u->cu_selector->d_data;
61 static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u)
64 u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel));
66 return u32_selector(u);
69 static 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,
76 [TCA_U32_SEL] = { .minlen = sizeof(struct tc_u32_sel) },
77 [TCA_U32_PCNT] = { .minlen = sizeof(struct tc_u32_pcnt) },
80 static int u32_msg_parser(struct rtnl_cls *cls)
83 struct nlattr *tb[TCA_U32_MAX + 1];
86 err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy);
94 if (tb[TCA_U32_DIVISOR]) {
95 u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
96 u->cu_mask |= U32_ATTR_DIVISOR;
99 if (tb[TCA_U32_SEL]) {
100 u->cu_selector = nla_get_data(tb[TCA_U32_SEL]);
103 u->cu_mask |= U32_ATTR_SELECTOR;
106 if (tb[TCA_U32_HASH]) {
107 u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]);
108 u->cu_mask |= U32_ATTR_HASH;
111 if (tb[TCA_U32_CLASSID]) {
112 u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]);
113 u->cu_mask |= U32_ATTR_CLASSID;
116 if (tb[TCA_U32_LINK]) {
117 u->cu_link = nla_get_u32(tb[TCA_U32_LINK]);
118 u->cu_mask |= U32_ATTR_LINK;
121 if (tb[TCA_U32_ACT]) {
122 u->cu_act = nla_get_data(tb[TCA_U32_ACT]);
125 u->cu_mask |= U32_ATTR_ACTION;
128 if (tb[TCA_U32_POLICE]) {
129 u->cu_police = nla_get_data(tb[TCA_U32_POLICE]);
132 u->cu_mask |= U32_ATTR_POLICE;
135 if (tb[TCA_U32_PCNT]) {
136 struct tc_u32_sel *sel;
139 if (!tb[TCA_U32_SEL]) {
140 err = nl_error(EINVAL, "Missing TCA_U32_SEL required "
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");
153 u->cu_pcnt = nla_get_data(tb[TCA_U32_PCNT]);
156 u->cu_mask |= U32_ATTR_PCNT;
159 if (tb[TCA_U32_INDEV]) {
160 nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ);
161 u->cu_mask |= U32_ATTR_INDEV;
167 err = nl_errno(ENOMEM);
172 static void u32_free_data(struct rtnl_cls *cls)
174 struct rtnl_u32 *u = u32_cls(cls);
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);
184 free(cls->c_subdata);
187 static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
189 struct rtnl_u32 *dst, *src = u32_cls(_src);
194 dst = u32_alloc(_dst);
196 return nl_errno(ENOMEM);
198 if (src->cu_selector)
199 if (!(dst->cu_selector = nl_data_clone(src->cu_selector)))
203 if (!(dst->cu_act = nl_data_clone(src->cu_act)))
207 if (!(dst->cu_police = nl_data_clone(src->cu_police)))
211 if (!(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
216 return nl_get_errno();
219 static int u32_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p,
222 struct rtnl_u32 *u = u32_cls(cls);
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)));
238 static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
239 struct rtnl_cls *cls, struct rtnl_u32 *u, int line)
242 struct tc_u32_key *key;
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
249 dp_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
252 if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
253 dp_dump(p, " offset at %u", sel->off);
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);
261 int flags = sel->flags;
264 #define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
265 flags &= ~TC_U32_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
267 PRINT_FLAG(TERMINAL);
269 PRINT_FLAG(VAROFFSET);
277 for (i = 0; i < sel->nkeys; i++) {
278 key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
281 dp_dump_line(p, line++, " match key at %s%u ",
282 key->offmask ? "nexthdr+" : "", key->off);
285 dp_dump(p, "[0x%u] ", key->offmask);
287 dp_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
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]);
300 static int u32_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p,
303 struct rtnl_u32 *u = u32_cls(cls);
304 struct tc_u32_sel *s;
309 if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
310 dp_dump(p, "no-selector\n");
314 s = u->cu_selector->d_data;
316 dp_dump(p, "nkeys %u ", s->nkeys);
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));
322 if (u->cu_mask & U32_ATTR_LINK)
323 dp_dump(p, "link %u ", u->cu_link);
325 if (u->cu_mask & U32_ATTR_INDEV)
326 dp_dump(p, "indev %s ", u->cu_indev);
328 line = print_selector(p, s, cls, u, line);
335 #define U32_ATTR_ACTION 0x040
336 #define U32_ATTR_POLICE 0x080
339 struct nl_data police;
343 static int u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p,
346 struct rtnl_u32 *u = u32_cls(cls);
351 if (u->cu_mask & U32_ATTR_PCNT) {
352 struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
354 dp_dump_line(p, line++, "%s successful hits\n");
355 dp_dump_line(p, line++, "%s %8llu %8llu\n",
363 static struct nl_msg *u32_get_opts(struct rtnl_cls *cls)
376 if (u->cu_mask & U32_ATTR_DIVISOR)
377 nla_put_u32(msg, TCA_U32_DIVISOR, u->cu_divisor);
379 if (u->cu_mask & U32_ATTR_HASH)
380 nla_put_u32(msg, TCA_U32_HASH, u->cu_hash);
382 if (u->cu_mask & U32_ATTR_CLASSID)
383 nla_put_u32(msg, TCA_U32_CLASSID, u->cu_classid);
385 if (u->cu_mask & U32_ATTR_LINK)
386 nla_put_u32(msg, TCA_U32_LINK, u->cu_link);
388 if (u->cu_mask & U32_ATTR_SELECTOR)
389 nla_put_data(msg, TCA_U32_SEL, u->cu_selector);
391 if (u->cu_mask & U32_ATTR_ACTION)
392 nla_put_data(msg, TCA_U32_ACT, u->cu_act);
394 if (u->cu_mask & U32_ATTR_POLICE)
395 nla_put_data(msg, TCA_U32_POLICE, u->cu_police);
397 if (u->cu_mask & U32_ATTR_INDEV)
398 nla_put_string(msg, TCA_U32_INDEV, u->cu_indev);
404 * @name Attribute Modifications
408 void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
411 uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
413 tca_set_handle((struct rtnl_tca *) cls, handle );
416 int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
422 return nl_errno(ENOMEM);
424 u->cu_classid = classid;
425 u->cu_mask |= U32_ATTR_CLASSID;
433 * @name Selector Modifications
437 int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
439 struct tc_u32_sel *sel;
444 return nl_errno(ENOMEM);
446 sel = u32_selector_alloc(u);
448 return nl_errno(ENOMEM);
451 u->cu_mask |= U32_ATTR_SELECTOR;
457 * Append new 32-bit key to the selector
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
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.
470 int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
471 int off, int offmask)
473 struct tc_u32_sel *sel;
479 return nl_errno(ENOMEM);
481 sel = u32_selector_alloc(u);
483 return nl_errno(ENOMEM);
485 err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
489 /* the selector might have been moved by realloc */
490 sel = u32_selector(u);
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;
497 u->cu_mask |= U32_ATTR_SELECTOR;
502 int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
503 int off, int offmask)
505 int shift = 24 - 8 * (off & 3);
507 return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
508 htonl((uint32_t)mask << shift),
513 * Append new selector key to match a 16-bit number
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
521 int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask,
522 int off, int offmask)
524 int shift = ((off & 3) == 0 ? 16 : 0);
526 return nl_error(EINVAL, "Invalid offset alignment");
528 return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
529 htonl((uint32_t)mask << shift),
534 * Append new selector key to match a 32-bit number
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
542 int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
543 int off, int offmask)
545 return rtnl_u32_add_key(cls, htonl(val), htonl(mask),
549 int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr,
550 uint8_t bitmask, int off, int offmask)
552 uint32_t mask = 0xFFFFFFFF << (32 - bitmask);
553 return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask);
556 int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
557 uint8_t bitmask, int off, int offmask)
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)
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)
573 /* otherwise, if (32*i - bitmask >= 32) no key is generated */
581 static struct rtnl_cls_ops u32_ops = {
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,
592 static void __init u32_init(void)
594 rtnl_cls_register(&u32_ops);
597 static void __exit u32_exit(void)
599 rtnl_cls_unregister(&u32_ops);