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