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