u32.c

00001 /*
00002  * lib/route/cls/u32.c          u32 classifier
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
00010  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
00011  * Copyright (c) 2005-2006 Siemens AG Oesterreich
00012  */
00013 
00014 /**
00015  * @ingroup cls
00016  * @defgroup cls_u32 Universal 32-bit Classifier
00017  *
00018  * @{
00019  */
00020 
00021 #include <netlink-local.h>
00022 #include <netlink-tc.h>
00023 #include <netlink/netlink.h>
00024 #include <netlink/attr.h>
00025 #include <netlink/utils.h>
00026 #include <netlink/route/tc-api.h>
00027 #include <netlink/route/classifier.h>
00028 #include <netlink/route/cls/u32.h>
00029 
00030 /** @cond SKIP */
00031 #define U32_ATTR_DIVISOR      0x001
00032 #define U32_ATTR_HASH         0x002
00033 #define U32_ATTR_CLASSID      0x004
00034 #define U32_ATTR_LINK         0x008
00035 #define U32_ATTR_PCNT         0x010
00036 #define U32_ATTR_SELECTOR     0x020
00037 #define U32_ATTR_ACTION       0x040
00038 #define U32_ATTR_POLICE       0x080
00039 #define U32_ATTR_INDEV        0x100
00040 /** @endcond */
00041 
00042 static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
00043 {
00044         return (struct tc_u32_sel *) u->cu_selector->d_data;
00045 }
00046 
00047 static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u)
00048 {
00049         if (!u->cu_selector)
00050                 u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel));
00051 
00052         return u32_selector(u);
00053 }
00054 
00055 static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
00056         [TCA_U32_DIVISOR]       = { .type = NLA_U32 },
00057         [TCA_U32_HASH]          = { .type = NLA_U32 },
00058         [TCA_U32_CLASSID]       = { .type = NLA_U32 },
00059         [TCA_U32_LINK]          = { .type = NLA_U32 },
00060         [TCA_U32_INDEV]         = { .type = NLA_STRING,
00061                                     .maxlen = IFNAMSIZ },
00062         [TCA_U32_SEL]           = { .minlen = sizeof(struct tc_u32_sel) },
00063         [TCA_U32_PCNT]          = { .minlen = sizeof(struct tc_u32_pcnt) },
00064 };
00065 
00066 static int u32_msg_parser(struct rtnl_tc *tc, void *data)
00067 {
00068         struct rtnl_u32 *u = data;
00069         struct nlattr *tb[TCA_U32_MAX + 1];
00070         int err;
00071 
00072         err = tca_parse(tb, TCA_U32_MAX, tc, u32_policy);
00073         if (err < 0)
00074                 return err;
00075 
00076         if (tb[TCA_U32_DIVISOR]) {
00077                 u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
00078                 u->cu_mask |= U32_ATTR_DIVISOR;
00079         }
00080 
00081         if (tb[TCA_U32_SEL]) {
00082                 u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]);
00083                 if (!u->cu_selector)
00084                         goto errout_nomem;
00085                 u->cu_mask |= U32_ATTR_SELECTOR;
00086         }
00087 
00088         if (tb[TCA_U32_HASH]) {
00089                 u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]);
00090                 u->cu_mask |= U32_ATTR_HASH;
00091         }
00092 
00093         if (tb[TCA_U32_CLASSID]) {
00094                 u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]);
00095                 u->cu_mask |= U32_ATTR_CLASSID;
00096         }
00097 
00098         if (tb[TCA_U32_LINK]) {
00099                 u->cu_link = nla_get_u32(tb[TCA_U32_LINK]);
00100                 u->cu_mask |= U32_ATTR_LINK;
00101         }
00102 
00103         if (tb[TCA_U32_ACT]) {
00104                 u->cu_act = nl_data_alloc_attr(tb[TCA_U32_ACT]);
00105                 if (!u->cu_act)
00106                         goto errout_nomem;
00107                 u->cu_mask |= U32_ATTR_ACTION;
00108         }
00109 
00110         if (tb[TCA_U32_POLICE]) {
00111                 u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]);
00112                 if (!u->cu_police)
00113                         goto errout_nomem;
00114                 u->cu_mask |= U32_ATTR_POLICE;
00115         }
00116 
00117         if (tb[TCA_U32_PCNT]) {
00118                 struct tc_u32_sel *sel;
00119                 int pcnt_size;
00120 
00121                 if (!tb[TCA_U32_SEL]) {
00122                         err = -NLE_MISSING_ATTR;
00123                         goto errout;
00124                 }
00125                 
00126                 sel = u->cu_selector->d_data;
00127                 pcnt_size = sizeof(struct tc_u32_pcnt) +
00128                                 (sel->nkeys * sizeof(uint64_t));
00129                 if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) {
00130                         err = -NLE_INVAL;
00131                         goto errout;
00132                 }
00133 
00134                 u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]);
00135                 if (!u->cu_pcnt)
00136                         goto errout_nomem;
00137                 u->cu_mask |= U32_ATTR_PCNT;
00138         }
00139 
00140         if (tb[TCA_U32_INDEV]) {
00141                 nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ);
00142                 u->cu_mask |= U32_ATTR_INDEV;
00143         }
00144 
00145         return 0;
00146 
00147 errout_nomem:
00148         err = -NLE_NOMEM;
00149 errout:
00150         return err;
00151 }
00152 
00153 static void u32_free_data(struct rtnl_tc *tc, void *data)
00154 {
00155         struct rtnl_u32 *u = data;
00156 
00157         nl_data_free(u->cu_selector);
00158         nl_data_free(u->cu_act);
00159         nl_data_free(u->cu_police);
00160         nl_data_free(u->cu_pcnt);
00161 }
00162 
00163 static int u32_clone(void *_dst, void *_src)
00164 {
00165         struct rtnl_u32 *dst = _dst, *src = _src;
00166 
00167         if (src->cu_selector &&
00168             !(dst->cu_selector = nl_data_clone(src->cu_selector)))
00169                 return -NLE_NOMEM;
00170 
00171         if (src->cu_act && !(dst->cu_act = nl_data_clone(src->cu_act)))
00172                 return -NLE_NOMEM;
00173 
00174         if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
00175                 return -NLE_NOMEM;
00176 
00177         if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
00178                 return -NLE_NOMEM;
00179 
00180         return 0;
00181 }
00182 
00183 static void u32_dump_line(struct rtnl_tc *tc, void *data,
00184                           struct nl_dump_params *p)
00185 {
00186         struct rtnl_u32 *u = data;
00187         char buf[32];
00188         
00189         if (!u)
00190                 return;
00191 
00192         if (u->cu_mask & U32_ATTR_DIVISOR)
00193                 nl_dump(p, " divisor %u", u->cu_divisor);
00194         else if (u->cu_mask & U32_ATTR_CLASSID)
00195                 nl_dump(p, " target %s",
00196                         rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf)));
00197 }
00198 
00199 static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
00200                            struct rtnl_u32 *u)
00201 {
00202         int i;
00203         struct tc_u32_key *key;
00204 
00205         if (sel->hmask || sel->hoff) {
00206                 /* I guess this will never be used since the kernel only
00207                  * exports the selector if no divisor is set but hash offset
00208                  * and hash mask make only sense in hash filters with divisor
00209                  * set */
00210                 nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
00211         }
00212 
00213         if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
00214                 nl_dump(p, " offset at %u", sel->off);
00215 
00216                 if (sel->flags & TC_U32_VAROFFSET)
00217                         nl_dump(p, " variable (at %u & 0x%x) >> %u",
00218                                 sel->offoff, ntohs(sel->offmask), sel->offshift);
00219         }
00220 
00221         if (sel->flags) {
00222                 int flags = sel->flags;
00223                 nl_dump(p, " <");
00224 
00225 #define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
00226         flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
00227 
00228                 PRINT_FLAG(TERMINAL);
00229                 PRINT_FLAG(OFFSET);
00230                 PRINT_FLAG(VAROFFSET);
00231                 PRINT_FLAG(EAT);
00232 #undef PRINT_FLAG
00233 
00234                 nl_dump(p, ">");
00235         }
00236                 
00237         
00238         for (i = 0; i < sel->nkeys; i++) {
00239                 key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
00240 
00241                 nl_dump(p, "\n");
00242                 nl_dump_line(p, "      match key at %s%u ",
00243                         key->offmask ? "nexthdr+" : "", key->off);
00244 
00245                 if (key->offmask)
00246                         nl_dump(p, "[0x%u] ", key->offmask);
00247 
00248                 nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
00249 
00250                 if (p->dp_type == NL_DUMP_STATS &&
00251                     (u->cu_mask & U32_ATTR_PCNT)) {
00252                         struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data;
00253                         nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
00254                 }
00255         }
00256 }
00257 
00258 static void u32_dump_details(struct rtnl_tc *tc, void *data,
00259                              struct nl_dump_params *p)
00260 {
00261         struct rtnl_u32 *u = data;
00262         struct tc_u32_sel *s;
00263 
00264         if (!u)
00265                 return;
00266 
00267         if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
00268                 nl_dump(p, "no-selector\n");
00269                 return;
00270         }
00271         
00272         s = u->cu_selector->d_data;
00273 
00274         nl_dump(p, "nkeys %u ", s->nkeys);
00275 
00276         if (u->cu_mask & U32_ATTR_HASH)
00277                 nl_dump(p, "ht key 0x%x hash 0x%u",
00278                         TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
00279 
00280         if (u->cu_mask & U32_ATTR_LINK)
00281                 nl_dump(p, "link %u ", u->cu_link);
00282 
00283         if (u->cu_mask & U32_ATTR_INDEV)
00284                 nl_dump(p, "indev %s ", u->cu_indev);
00285 
00286         print_selector(p, s, u);
00287         nl_dump(p, "\n");
00288 
00289 #if 0   
00290 #define U32_ATTR_ACTION       0x040
00291 #define U32_ATTR_POLICE       0x080
00292 
00293         struct nl_data   act;
00294         struct nl_data   police;
00295 #endif
00296 }
00297 
00298 static void u32_dump_stats(struct rtnl_tc *tc, void *data,
00299                            struct nl_dump_params *p)
00300 {
00301         struct rtnl_u32 *u = data;
00302 
00303         if (!u)
00304                 return;
00305 
00306         if (u->cu_mask & U32_ATTR_PCNT) {
00307                 struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
00308                 nl_dump(p, "\n");
00309                 nl_dump_line(p, "    hit %8llu count %8llu\n",
00310                              pc->rhit, pc->rcnt);
00311         }
00312 }
00313 
00314 static int u32_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
00315 {
00316         struct rtnl_u32 *u = data;
00317 
00318         if (!u)
00319                 return 0;
00320         
00321         if (u->cu_mask & U32_ATTR_DIVISOR)
00322                 NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
00323 
00324         if (u->cu_mask & U32_ATTR_HASH)
00325                 NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash);
00326 
00327         if (u->cu_mask & U32_ATTR_CLASSID)
00328                 NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid);
00329 
00330         if (u->cu_mask & U32_ATTR_LINK)
00331                 NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link);
00332 
00333         if (u->cu_mask & U32_ATTR_SELECTOR)
00334                 NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
00335 
00336         if (u->cu_mask & U32_ATTR_ACTION)
00337                 NLA_PUT_DATA(msg, TCA_U32_ACT, u->cu_act);
00338 
00339         if (u->cu_mask & U32_ATTR_POLICE)
00340                 NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
00341 
00342         if (u->cu_mask & U32_ATTR_INDEV)
00343                 NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev);
00344 
00345         return 0;
00346 
00347 nla_put_failure:
00348         return -NLE_NOMEM;
00349 }
00350 
00351 /**
00352  * @name Attribute Modifications
00353  * @{
00354  */
00355 
00356 void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
00357                          int nodeid)
00358 {
00359         uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
00360 
00361         rtnl_tc_set_handle((struct rtnl_tc *) cls, handle );
00362 }
00363  
00364 int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
00365 {
00366         struct rtnl_u32 *u;
00367 
00368         if (!(u = rtnl_tc_data(TC_CAST(cls))))
00369                 return -NLE_NOMEM;
00370         
00371         u->cu_classid = classid;
00372         u->cu_mask |= U32_ATTR_CLASSID;
00373 
00374         return 0;
00375 }
00376 
00377 /** @} */
00378 
00379 /**
00380  * @name Selector Modifications
00381  * @{
00382  */
00383 
00384 int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
00385 {
00386         struct tc_u32_sel *sel;
00387         struct rtnl_u32 *u;
00388 
00389         if (!(u = rtnl_tc_data(TC_CAST(cls))))
00390                 return -NLE_NOMEM;
00391 
00392         sel = u32_selector_alloc(u);
00393         if (!sel)
00394                 return -NLE_NOMEM;
00395 
00396         sel->flags |= flags;
00397         u->cu_mask |= U32_ATTR_SELECTOR;
00398 
00399         return 0;
00400 }
00401 
00402 /**
00403  * Append new 32-bit key to the selector
00404  *
00405  * @arg cls     classifier to be modifier
00406  * @arg val     value to be matched (network byte-order)
00407  * @arg mask    mask to be applied before matching (network byte-order)
00408  * @arg off     offset, in bytes, to start matching
00409  * @arg offmask offset mask
00410  *
00411  * General selectors define the pattern, mask and offset the pattern will be
00412  * matched to the packet contents. Using the general selectors you can match
00413  * virtually any single bit in the IP (or upper layer) header.
00414  *
00415 */
00416 int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
00417                      int off, int offmask)
00418 {
00419         struct tc_u32_sel *sel;
00420         struct rtnl_u32 *u;
00421         int err;
00422 
00423         if (!(u = rtnl_tc_data(TC_CAST(cls))))
00424                 return -NLE_NOMEM;
00425 
00426         sel = u32_selector_alloc(u);
00427         if (!sel)
00428                 return -NLE_NOMEM;
00429 
00430         err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
00431         if (err < 0)
00432                 return err;
00433 
00434         /* the selector might have been moved by realloc */
00435         sel = u32_selector(u);
00436 
00437         sel->keys[sel->nkeys].mask = mask;
00438         sel->keys[sel->nkeys].val = val & mask;
00439         sel->keys[sel->nkeys].off = off;
00440         sel->keys[sel->nkeys].offmask = offmask;
00441         sel->nkeys++;
00442         u->cu_mask |= U32_ATTR_SELECTOR;
00443 
00444         return 0;
00445 }
00446 
00447 int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
00448                            int off, int offmask)
00449 {
00450         int shift = 24 - 8 * (off & 3);
00451 
00452         return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
00453                                 htonl((uint32_t)mask << shift),
00454                                 off & ~3, offmask);
00455 }
00456 
00457 /**
00458  * Append new selector key to match a 16-bit number
00459  *
00460  * @arg cls     classifier to be modified
00461  * @arg val     value to be matched (host byte-order)
00462  * @arg mask    mask to be applied before matching (host byte-order)
00463  * @arg off     offset, in bytes, to start matching
00464  * @arg offmask offset mask
00465 */
00466 int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask,
00467                             int off, int offmask)
00468 {
00469         int shift = ((off & 3) == 0 ? 16 : 0);
00470         if (off % 2)
00471                 return -NLE_INVAL;
00472 
00473         return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
00474                                 htonl((uint32_t)mask << shift),
00475                                 off & ~3, offmask);
00476 }
00477 
00478 /**
00479  * Append new selector key to match a 32-bit number
00480  *
00481  * @arg cls     classifier to be modified
00482  * @arg val     value to be matched (host byte-order)
00483  * @arg mask    mask to be applied before matching (host byte-order)
00484  * @arg off     offset, in bytes, to start matching
00485  * @arg offmask offset mask
00486 */
00487 int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
00488                             int off, int offmask)
00489 {
00490         return rtnl_u32_add_key(cls, htonl(val), htonl(mask),
00491                                 off & ~3, offmask);
00492 }
00493 
00494 int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr,
00495                              uint8_t bitmask, int off, int offmask)
00496 {
00497         uint32_t mask = 0xFFFFFFFF << (32 - bitmask);
00498         return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask);
00499 }
00500 
00501 int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
00502                               uint8_t bitmask, int off, int offmask)
00503 {
00504         int i, err;
00505 
00506         for (i = 1; i <= 4; i++) {
00507                 if (32 * i - bitmask <= 0) {
00508                         if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
00509                                                 0xFFFFFFFF, off+4*(i-1), offmask)) < 0)
00510                                 return err;
00511                 }
00512                 else if (32 * i - bitmask < 32) {
00513                         uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask);
00514                         if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1],
00515                                                 htonl(mask), off+4*(i-1), offmask)) < 0)
00516                                 return err;
00517                 }
00518                 /* otherwise, if (32*i - bitmask >= 32) no key is generated */
00519         }
00520 
00521         return 0;
00522 }
00523 
00524 /** @} */
00525 
00526 static struct rtnl_tc_ops u32_ops = {
00527         .to_kind                = "u32",
00528         .to_type                = RTNL_TC_TYPE_CLS,
00529         .to_size                = sizeof(struct rtnl_u32),
00530         .to_msg_parser          = u32_msg_parser,
00531         .to_free_data           = u32_free_data,
00532         .to_clone               = u32_clone,
00533         .to_msg_fill            = u32_msg_fill,
00534         .to_dump = {
00535             [NL_DUMP_LINE]      = u32_dump_line,
00536             [NL_DUMP_DETAILS]   = u32_dump_details,
00537             [NL_DUMP_STATS]     = u32_dump_stats,
00538         },
00539 };
00540 
00541 static void __init u32_init(void)
00542 {
00543         rtnl_tc_register(&u32_ops);
00544 }
00545 
00546 static void __exit u32_exit(void)
00547 {
00548         rtnl_tc_unregister(&u32_ops);
00549 }
00550 
00551 /** @} */