libnl  3.2.24-rc1
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-2013 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 cls_u32 Universal 32-bit Classifier
17  *
18  * @{
19  */
20 
21 #include <netlink-private/netlink.h>
22 #include <netlink-private/tc.h>
23 #include <netlink/netlink.h>
24 #include <netlink/attr.h>
25 #include <netlink/utils.h>
26 #include <netlink-private/route/tc-api.h>
27 #include <netlink/route/classifier.h>
28 #include <netlink/route/cls/u32.h>
29 #include <netlink/route/action.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 tc_u32_sel *u32_selector(struct rtnl_u32 *u)
44 {
45  return (struct tc_u32_sel *) u->cu_selector->d_data;
46 }
47 
48 static 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 
56 static 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 
67 static int u32_msg_parser(struct rtnl_tc *tc, void *data)
68 {
69  struct rtnl_u32 *u = data;
70  struct nlattr *tb[TCA_U32_MAX + 1];
71  int err;
72 
73  err = tca_parse(tb, TCA_U32_MAX, tc, 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_mask |= U32_ATTR_ACTION;
106  err = rtnl_act_parse(&u->cu_act, tb[TCA_U32_ACT]);
107  if (err)
108  return err;
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  size_t 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 
148 errout_nomem:
149  err = -NLE_NOMEM;
150 errout:
151  return err;
152 }
153 
154 static void u32_free_data(struct rtnl_tc *tc, void *data)
155 {
156  struct rtnl_u32 *u = data;
157 
158  if (u->cu_act)
159  rtnl_act_put_all(&u->cu_act);
160  nl_data_free(u->cu_selector);
161  nl_data_free(u->cu_police);
162  nl_data_free(u->cu_pcnt);
163 }
164 
165 static int u32_clone(void *_dst, void *_src)
166 {
167  struct rtnl_u32 *dst = _dst, *src = _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 = rtnl_act_alloc()))
174  return -NLE_NOMEM;
175  memcpy(dst->cu_act, src->cu_act, sizeof(struct rtnl_act));
176 
177  if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
178  return -NLE_NOMEM;
179 
180  if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
181  return -NLE_NOMEM;
182 
183  return 0;
184 }
185 
186 static void u32_dump_line(struct rtnl_tc *tc, void *data,
187  struct nl_dump_params *p)
188 {
189  struct rtnl_u32 *u = data;
190  char buf[32];
191 
192  if (!u)
193  return;
194 
195  if (u->cu_mask & U32_ATTR_DIVISOR)
196  nl_dump(p, " divisor %u", u->cu_divisor);
197  else if (u->cu_mask & U32_ATTR_CLASSID)
198  nl_dump(p, " target %s",
199  rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf)));
200 }
201 
202 static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
203  struct rtnl_u32 *u)
204 {
205  int i;
206  struct tc_u32_key *key;
207 
208  if (sel->hmask || sel->hoff) {
209  /* I guess this will never be used since the kernel only
210  * exports the selector if no divisor is set but hash offset
211  * and hash mask make only sense in hash filters with divisor
212  * set */
213  nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
214  }
215 
216  if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
217  nl_dump(p, " offset at %u", sel->off);
218 
219  if (sel->flags & TC_U32_VAROFFSET)
220  nl_dump(p, " variable (at %u & 0x%x) >> %u",
221  sel->offoff, ntohs(sel->offmask), sel->offshift);
222  }
223 
224  if (sel->flags) {
225  int flags = sel->flags;
226  nl_dump(p, " <");
227 
228 #define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
229  flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
230 
231  PRINT_FLAG(TERMINAL);
232  PRINT_FLAG(OFFSET);
233  PRINT_FLAG(VAROFFSET);
234  PRINT_FLAG(EAT);
235 #undef PRINT_FLAG
236 
237  nl_dump(p, ">");
238  }
239 
240 
241  for (i = 0; i < sel->nkeys; i++) {
242  key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
243 
244  nl_dump(p, "\n");
245  nl_dump_line(p, " match key at %s%u ",
246  key->offmask ? "nexthdr+" : "", key->off);
247 
248  if (key->offmask)
249  nl_dump(p, "[0x%u] ", key->offmask);
250 
251  nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
252 
253  if (p->dp_type == NL_DUMP_STATS &&
254  (u->cu_mask & U32_ATTR_PCNT)) {
255  struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data;
256  nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
257  }
258  }
259 }
260 
261 static void u32_dump_details(struct rtnl_tc *tc, void *data,
262  struct nl_dump_params *p)
263 {
264  struct rtnl_u32 *u = data;
265  struct tc_u32_sel *s;
266 
267  if (!u)
268  return;
269 
270  if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
271  nl_dump(p, "no-selector\n");
272  return;
273  }
274 
275  s = u->cu_selector->d_data;
276 
277  nl_dump(p, "nkeys %u ", s->nkeys);
278 
279  if (u->cu_mask & U32_ATTR_HASH)
280  nl_dump(p, "ht key 0x%x hash 0x%u",
281  TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
282 
283  if (u->cu_mask & U32_ATTR_LINK)
284  nl_dump(p, "link %u ", u->cu_link);
285 
286  if (u->cu_mask & U32_ATTR_INDEV)
287  nl_dump(p, "indev %s ", u->cu_indev);
288 
289  print_selector(p, s, u);
290  nl_dump(p, "\n");
291 
292 #if 0
293 #define U32_ATTR_ACTION 0x040
294 #define U32_ATTR_POLICE 0x080
295 
296  struct nl_data act;
297  struct nl_data police;
298 #endif
299 }
300 
301 static void u32_dump_stats(struct rtnl_tc *tc, void *data,
302  struct nl_dump_params *p)
303 {
304  struct rtnl_u32 *u = data;
305 
306  if (!u)
307  return;
308 
309  if (u->cu_mask & U32_ATTR_PCNT) {
310  struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
311  nl_dump(p, "\n");
312  nl_dump_line(p, " hit %8" PRIu64 " count %8" PRIu64 "\n",
313  pc->rhit, pc->rcnt);
314  }
315 }
316 
317 static int u32_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
318 {
319  struct rtnl_u32 *u = data;
320 
321  if (!u)
322  return 0;
323 
324  if (u->cu_mask & U32_ATTR_DIVISOR)
325  NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
326 
327  if (u->cu_mask & U32_ATTR_HASH)
328  NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash);
329 
330  if (u->cu_mask & U32_ATTR_CLASSID)
331  NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid);
332 
333  if (u->cu_mask & U32_ATTR_LINK)
334  NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link);
335 
336  if (u->cu_mask & U32_ATTR_SELECTOR)
337  NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
338 
339  if (u->cu_mask & U32_ATTR_ACTION) {
340  int err;
341 
342  err = rtnl_act_fill(msg, TCA_U32_ACT, u->cu_act);
343  if (err)
344  return err;
345  }
346 
347  if (u->cu_mask & U32_ATTR_POLICE)
348  NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
349 
350  if (u->cu_mask & U32_ATTR_INDEV)
351  NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev);
352 
353  return 0;
354 
355 nla_put_failure:
356  return -NLE_NOMEM;
357 }
358 
359 /**
360  * @name Attribute Modifications
361  * @{
362  */
363 
364 void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
365  int nodeid)
366 {
367  uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
368 
369  rtnl_tc_set_handle((struct rtnl_tc *) cls, handle );
370 }
371 
372 int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
373 {
374  struct rtnl_u32 *u;
375 
376  if (!(u = rtnl_tc_data(TC_CAST(cls))))
377  return -NLE_NOMEM;
378 
379  u->cu_classid = classid;
380  u->cu_mask |= U32_ATTR_CLASSID;
381 
382  return 0;
383 }
384 
385 int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor)
386 {
387  struct rtnl_u32 *u;
388 
389  if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
390  return -NLE_NOMEM;
391 
392  u->cu_divisor = divisor;
393  u->cu_mask |= U32_ATTR_DIVISOR;
394  return 0;
395 }
396 
397 int rtnl_u32_set_link(struct rtnl_cls *cls, uint32_t link)
398 {
399  struct rtnl_u32 *u;
400 
401  if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
402  return -NLE_NOMEM;
403 
404  u->cu_link = link;
405  u->cu_mask |= U32_ATTR_LINK;
406  return 0;
407 }
408 
409 int rtnl_u32_set_hashtable(struct rtnl_cls *cls, uint32_t ht)
410 {
411  struct rtnl_u32 *u;
412 
413  if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
414  return -NLE_NOMEM;
415 
416  u->cu_hash = ht;
417  u->cu_mask |= U32_ATTR_HASH;
418  return 0;
419 }
420 
421 int rtnl_u32_set_hashmask(struct rtnl_cls *cls, uint32_t hashmask, uint32_t offset)
422 {
423  struct rtnl_u32 *u;
424  struct tc_u32_sel *sel;
425  int err;
426 
427  hashmask = htonl(hashmask);
428 
429  if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
430  return -NLE_NOMEM;
431 
432  sel = u32_selector_alloc(u);
433  if (!sel)
434  return -NLE_NOMEM;
435 
436  err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
437  if(err < 0)
438  return err;
439 
440  sel = u32_selector(u);
441 
442  sel->hmask = hashmask;
443  sel->hoff = offset;
444  return 0;
445 }
446 
447 int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls)
448 {
449  struct rtnl_u32 *u;
450  struct tc_u32_sel *sel;
451  int err;
452 
453  if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
454  return -NLE_NOMEM;
455 
456  sel = u32_selector_alloc(u);
457  if (!sel)
458  return -NLE_NOMEM;
459 
460  err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
461  if(err < 0)
462  return err;
463 
464  sel = u32_selector(u);
465 
466  sel->flags |= TC_U32_TERMINAL;
467  return 0;
468 }
469 
470 int rtnl_u32_add_action(struct rtnl_cls *cls, struct rtnl_act *act)
471 {
472  struct rtnl_u32 *u;
473 
474  if (!act)
475  return 0;
476 
477  if (!(u = rtnl_tc_data(TC_CAST(cls))))
478  return -NLE_NOMEM;
479 
480  u->cu_mask |= U32_ATTR_ACTION;
481  return rtnl_act_append(&u->cu_act, act);
482 }
483 
484 int rtnl_u32_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
485 {
486  struct rtnl_u32 *u;
487  int ret;
488 
489  if (!act)
490  return 0;
491 
492  if (!(u = rtnl_tc_data(TC_CAST(cls))))
493  return -NLE_NOMEM;
494 
495  if (!(u->cu_mask & U32_ATTR_ACTION))
496  return -NLE_INVAL;
497 
498  ret = rtnl_act_remove(&u->cu_act, act);
499  if (!u->cu_act)
500  u->cu_mask &= ~U32_ATTR_ACTION;
501  return ret;
502 }
503 /** @} */
504 
505 /**
506  * @name Selector Modifications
507  * @{
508  */
509 
510 int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
511 {
512  struct tc_u32_sel *sel;
513  struct rtnl_u32 *u;
514 
515  if (!(u = rtnl_tc_data(TC_CAST(cls))))
516  return -NLE_NOMEM;
517 
518  sel = u32_selector_alloc(u);
519  if (!sel)
520  return -NLE_NOMEM;
521 
522  sel->flags |= flags;
523  u->cu_mask |= U32_ATTR_SELECTOR;
524 
525  return 0;
526 }
527 
528 /**
529  * Append new 32-bit key to the selector
530  *
531  * @arg cls classifier to be modifier
532  * @arg val value to be matched (network byte-order)
533  * @arg mask mask to be applied before matching (network byte-order)
534  * @arg off offset, in bytes, to start matching
535  * @arg offmask offset mask
536  *
537  * General selectors define the pattern, mask and offset the pattern will be
538  * matched to the packet contents. Using the general selectors you can match
539  * virtually any single bit in the IP (or upper layer) header.
540  *
541 */
542 int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
543  int off, int offmask)
544 {
545  struct tc_u32_sel *sel;
546  struct rtnl_u32 *u;
547  int err;
548 
549  if (!(u = rtnl_tc_data(TC_CAST(cls))))
550  return -NLE_NOMEM;
551 
552  sel = u32_selector_alloc(u);
553  if (!sel)
554  return -NLE_NOMEM;
555 
556  err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
557  if (err < 0)
558  return err;
559 
560  /* the selector might have been moved by realloc */
561  sel = u32_selector(u);
562 
563  sel->keys[sel->nkeys].mask = mask;
564  sel->keys[sel->nkeys].val = val & mask;
565  sel->keys[sel->nkeys].off = off;
566  sel->keys[sel->nkeys].offmask = offmask;
567  sel->nkeys++;
568  u->cu_mask |= U32_ATTR_SELECTOR;
569 
570  return 0;
571 }
572 
573 /**
574  * Get the 32-bit key from the selector
575  *
576  * @arg cls classifier to be retrieve
577  * @arg index the index of the array of keys, start with 0
578  * @arg val pointer to store value after masked (network byte-order)
579  * @arg mask pointer to store the mask (network byte-order)
580  * @arg off pointer to store the offset
581  * @arg offmask pointer to store offset mask
582  *
583 */
584 int rtnl_u32_get_key(struct rtnl_cls *cls, uint8_t index,
585  uint32_t *val, uint32_t *mask, int *off, int *offmask)
586 {
587  struct tc_u32_sel *sel;
588  struct rtnl_u32 *u;
589 
590  if (!(u = rtnl_tc_data(TC_CAST(cls))))
591  return -NLE_NOMEM;
592 
593  if (!(u->cu_mask & U32_ATTR_SELECTOR))
594  return -NLE_INVAL;
595 
596  /* the selector might have been moved by realloc */
597  sel = u32_selector(u);
598  if (index >= sel->nkeys)
599  return -NLE_RANGE;
600 
601  *mask = sel->keys[index].mask;
602  *val = sel->keys[index].val;
603  *off = sel->keys[index].off;
604  *offmask = sel->keys[index].offmask;
605  return 0;
606 }
607 
608 
609 int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
610  int off, int offmask)
611 {
612  int shift = 24 - 8 * (off & 3);
613 
614  return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
615  htonl((uint32_t)mask << shift),
616  off & ~3, offmask);
617 }
618 
619 /**
620  * Append new selector key to match a 16-bit number
621  *
622  * @arg cls classifier to be modified
623  * @arg val value to be matched (host byte-order)
624  * @arg mask mask to be applied before matching (host byte-order)
625  * @arg off offset, in bytes, to start matching
626  * @arg offmask offset mask
627 */
628 int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask,
629  int off, int offmask)
630 {
631  int shift = ((off & 3) == 0 ? 16 : 0);
632  if (off % 2)
633  return -NLE_INVAL;
634 
635  return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
636  htonl((uint32_t)mask << shift),
637  off & ~3, offmask);
638 }
639 
640 /**
641  * Append new selector key to match a 32-bit number
642  *
643  * @arg cls classifier to be modified
644  * @arg val value to be matched (host byte-order)
645  * @arg mask mask to be applied before matching (host byte-order)
646  * @arg off offset, in bytes, to start matching
647  * @arg offmask offset mask
648 */
649 int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
650  int off, int offmask)
651 {
652  return rtnl_u32_add_key(cls, htonl(val), htonl(mask),
653  off & ~3, offmask);
654 }
655 
656 int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr,
657  uint8_t bitmask, int off, int offmask)
658 {
659  uint32_t mask = 0xFFFFFFFF << (32 - bitmask);
660  return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask);
661 }
662 
663 int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
664  uint8_t bitmask, int off, int offmask)
665 {
666  int i, err;
667 
668  for (i = 1; i <= 4; i++) {
669  if (32 * i - bitmask <= 0) {
670  if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
671  0xFFFFFFFF, off+4*(i-1), offmask)) < 0)
672  return err;
673  }
674  else if (32 * i - bitmask < 32) {
675  uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask);
676  if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
677  htonl(mask), off+4*(i-1), offmask)) < 0)
678  return err;
679  }
680  /* otherwise, if (32*i - bitmask >= 32) no key is generated */
681  }
682 
683  return 0;
684 }
685 
686 /** @} */
687 
688 static struct rtnl_tc_ops u32_ops = {
689  .to_kind = "u32",
690  .to_type = RTNL_TC_TYPE_CLS,
691  .to_size = sizeof(struct rtnl_u32),
692  .to_msg_parser = u32_msg_parser,
693  .to_free_data = u32_free_data,
694  .to_clone = u32_clone,
695  .to_msg_fill = u32_msg_fill,
696  .to_dump = {
697  [NL_DUMP_LINE] = u32_dump_line,
698  [NL_DUMP_DETAILS] = u32_dump_details,
699  [NL_DUMP_STATS] = u32_dump_stats,
700  },
701 };
702 
703 static void __init u32_init(void)
704 {
705  rtnl_tc_register(&u32_ops);
706 }
707 
708 static void __exit u32_exit(void)
709 {
710  rtnl_tc_unregister(&u32_ops);
711 }
712 
713 /** @} */