libnl 1.1.4
u32.c
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/** @} */
struct nl_data * nla_get_data(struct nlattr *nla)
Return payload of abstract data attribute.
Definition: attr.c:776
int nla_put_u32(struct nl_msg *n, int attrtype, uint32_t value)
Add a u32 netlink attribute to a netlink message.
Definition: attr.c:569
size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
Copy string attribute payload into a sized buffer.
Definition: attr.c:407
uint32_t nla_get_u32(struct nlattr *nla)
Return payload of u32 attribute.
Definition: attr.c:693
int nla_len(const struct nlattr *nla)
length of payload
Definition: attr.c:160
int nla_put_string(struct nl_msg *n, int attrtype, const char *str)
Add a string netlink attribute to a netlink message.
Definition: attr.c:591
int nla_put_data(struct nl_msg *n, int attrtype, struct nl_data *data)
Add an abstract data netlink attribute to a netlink message.
Definition: attr.c:623
@ NLA_STRING
character string
Definition: attr.h:41
@ NLA_U32
32bit integer
Definition: attr.h:39
int rtnl_cls_unregister(struct rtnl_cls_ops *cops)
Unregister a classifier module.
Definition: cls_api.c:59
int rtnl_cls_register(struct rtnl_cls_ops *cops)
Register a classifier module.
Definition: cls_api.c:38
void nl_data_free(struct nl_data *data)
Free an abstract data object.
Definition: data.c:110
struct nl_data * nl_data_clone(struct nl_data *src)
Clone an abstract data object.
Definition: data.c:69
int nl_data_append(struct nl_data *data, void *buf, size_t size)
Append data to an abstract data object.
Definition: data.c:85
struct nl_data * nl_data_alloc(void *buf, size_t size)
Allocate a new abstract data object.
Definition: data.c:38
struct nl_msg * nlmsg_alloc(void)
Allocate a new netlink message with the default maximum payload size.
Definition: msg.c:401
char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
Convert a traffic control handle to a character string (Reentrant).
Definition: tc.c:493
int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask, int off, int offmask)
Append new 32-bit key to the selector.
Definition: u32.c:470
int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask, int off, int offmask)
Append new selector key to match a 32-bit number.
Definition: u32.c:542
int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask, int off, int offmask)
Append new selector key to match a 16-bit number.
Definition: u32.c:521
@ NL_DUMP_FULL
Dump all attributes but no statistics.
Definition: types.h:23
@ NL_DUMP_BRIEF
Dump object in a brief one-liner.
Definition: types.h:22
@ NL_DUMP_STATS
Dump all attributes including statistics.
Definition: types.h:24
Dumping parameters.
Definition: types.h:37
enum nl_dump_type dp_type
Specifies the type of dump that is requested.
Definition: types.h:41
attribute validation policy
Definition: attr.h:73
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:75
Classifier operations.
char co_kind[32]
Kind/Name of classifier.