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