Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.17-rc4 534 lines 13 kB view raw
1/* 2 * net/sched/cls_tcindex.c Packet classifier for skb->tc_index 3 * 4 * Written 1998,1999 by Werner Almesberger, EPFL ICA 5 */ 6 7#include <linux/config.h> 8#include <linux/module.h> 9#include <linux/types.h> 10#include <linux/kernel.h> 11#include <linux/skbuff.h> 12#include <linux/errno.h> 13#include <linux/netdevice.h> 14#include <net/ip.h> 15#include <net/act_api.h> 16#include <net/pkt_cls.h> 17#include <net/route.h> 18 19 20/* 21 * Not quite sure if we need all the xchgs Alexey uses when accessing things. 22 * Can always add them later ... :) 23 */ 24 25/* 26 * Passing parameters to the root seems to be done more awkwardly than really 27 * necessary. At least, u32 doesn't seem to use such dirty hacks. To be 28 * verified. FIXME. 29 */ 30 31#define PERFECT_HASH_THRESHOLD 64 /* use perfect hash if not bigger */ 32#define DEFAULT_HASH_SIZE 64 /* optimized for diffserv */ 33 34 35#if 1 /* control */ 36#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 37#else 38#define DPRINTK(format,args...) 39#endif 40 41#if 0 /* data */ 42#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args) 43#else 44#define D2PRINTK(format,args...) 45#endif 46 47 48#define PRIV(tp) ((struct tcindex_data *) (tp)->root) 49 50 51struct tcindex_filter_result { 52 struct tcf_exts exts; 53 struct tcf_result res; 54}; 55 56struct tcindex_filter { 57 u16 key; 58 struct tcindex_filter_result result; 59 struct tcindex_filter *next; 60}; 61 62 63struct tcindex_data { 64 struct tcindex_filter_result *perfect; /* perfect hash; NULL if none */ 65 struct tcindex_filter **h; /* imperfect hash; only used if !perfect; 66 NULL if unused */ 67 u16 mask; /* AND key with mask */ 68 int shift; /* shift ANDed key to the right */ 69 int hash; /* hash table size; 0 if undefined */ 70 int alloc_hash; /* allocated size */ 71 int fall_through; /* 0: only classify if explicit match */ 72}; 73 74static struct tcf_ext_map tcindex_ext_map = { 75 .police = TCA_TCINDEX_POLICE, 76 .action = TCA_TCINDEX_ACT 77}; 78 79static inline int 80tcindex_filter_is_set(struct tcindex_filter_result *r) 81{ 82 return tcf_exts_is_predicative(&r->exts) || r->res.classid; 83} 84 85static struct tcindex_filter_result * 86tcindex_lookup(struct tcindex_data *p, u16 key) 87{ 88 struct tcindex_filter *f; 89 90 if (p->perfect) 91 return tcindex_filter_is_set(p->perfect + key) ? 92 p->perfect + key : NULL; 93 else if (p->h) { 94 for (f = p->h[key % p->hash]; f; f = f->next) 95 if (f->key == key) 96 return &f->result; 97 } 98 99 return NULL; 100} 101 102 103static int tcindex_classify(struct sk_buff *skb, struct tcf_proto *tp, 104 struct tcf_result *res) 105{ 106 struct tcindex_data *p = PRIV(tp); 107 struct tcindex_filter_result *f; 108 int key = (skb->tc_index & p->mask) >> p->shift; 109 110 D2PRINTK("tcindex_classify(skb %p,tp %p,res %p),p %p\n",skb,tp,res,p); 111 112 f = tcindex_lookup(p, key); 113 if (!f) { 114 if (!p->fall_through) 115 return -1; 116 res->classid = TC_H_MAKE(TC_H_MAJ(tp->q->handle), key); 117 res->class = 0; 118 D2PRINTK("alg 0x%x\n",res->classid); 119 return 0; 120 } 121 *res = f->res; 122 D2PRINTK("map 0x%x\n",res->classid); 123 124 return tcf_exts_exec(skb, &f->exts, res); 125} 126 127 128static unsigned long tcindex_get(struct tcf_proto *tp, u32 handle) 129{ 130 struct tcindex_data *p = PRIV(tp); 131 struct tcindex_filter_result *r; 132 133 DPRINTK("tcindex_get(tp %p,handle 0x%08x)\n",tp,handle); 134 if (p->perfect && handle >= p->alloc_hash) 135 return 0; 136 r = tcindex_lookup(p, handle); 137 return r && tcindex_filter_is_set(r) ? (unsigned long) r : 0UL; 138} 139 140 141static void tcindex_put(struct tcf_proto *tp, unsigned long f) 142{ 143 DPRINTK("tcindex_put(tp %p,f 0x%lx)\n",tp,f); 144} 145 146 147static int tcindex_init(struct tcf_proto *tp) 148{ 149 struct tcindex_data *p; 150 151 DPRINTK("tcindex_init(tp %p)\n",tp); 152 p = kmalloc(sizeof(struct tcindex_data),GFP_KERNEL); 153 if (!p) 154 return -ENOMEM; 155 156 memset(p, 0, sizeof(*p)); 157 p->mask = 0xffff; 158 p->hash = DEFAULT_HASH_SIZE; 159 p->fall_through = 1; 160 161 tp->root = p; 162 return 0; 163} 164 165 166static int 167__tcindex_delete(struct tcf_proto *tp, unsigned long arg, int lock) 168{ 169 struct tcindex_data *p = PRIV(tp); 170 struct tcindex_filter_result *r = (struct tcindex_filter_result *) arg; 171 struct tcindex_filter *f = NULL; 172 173 DPRINTK("tcindex_delete(tp %p,arg 0x%lx),p %p,f %p\n",tp,arg,p,f); 174 if (p->perfect) { 175 if (!r->res.class) 176 return -ENOENT; 177 } else { 178 int i; 179 struct tcindex_filter **walk = NULL; 180 181 for (i = 0; i < p->hash; i++) 182 for (walk = p->h+i; *walk; walk = &(*walk)->next) 183 if (&(*walk)->result == r) 184 goto found; 185 return -ENOENT; 186 187found: 188 f = *walk; 189 if (lock) 190 tcf_tree_lock(tp); 191 *walk = f->next; 192 if (lock) 193 tcf_tree_unlock(tp); 194 } 195 tcf_unbind_filter(tp, &r->res); 196 tcf_exts_destroy(tp, &r->exts); 197 kfree(f); 198 return 0; 199} 200 201static int tcindex_delete(struct tcf_proto *tp, unsigned long arg) 202{ 203 return __tcindex_delete(tp, arg, 1); 204} 205 206static inline int 207valid_perfect_hash(struct tcindex_data *p) 208{ 209 return p->hash > (p->mask >> p->shift); 210} 211 212static int 213tcindex_set_parms(struct tcf_proto *tp, unsigned long base, u32 handle, 214 struct tcindex_data *p, struct tcindex_filter_result *r, 215 struct rtattr **tb, struct rtattr *est) 216{ 217 int err, balloc = 0; 218 struct tcindex_filter_result new_filter_result, *old_r = r; 219 struct tcindex_filter_result cr; 220 struct tcindex_data cp; 221 struct tcindex_filter *f = NULL; /* make gcc behave */ 222 struct tcf_exts e; 223 224 err = tcf_exts_validate(tp, tb, est, &e, &tcindex_ext_map); 225 if (err < 0) 226 return err; 227 228 memcpy(&cp, p, sizeof(cp)); 229 memset(&new_filter_result, 0, sizeof(new_filter_result)); 230 231 if (old_r) 232 memcpy(&cr, r, sizeof(cr)); 233 else 234 memset(&cr, 0, sizeof(cr)); 235 236 err = -EINVAL; 237 if (tb[TCA_TCINDEX_HASH-1]) { 238 if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH-1]) < sizeof(u32)) 239 goto errout; 240 cp.hash = *(u32 *) RTA_DATA(tb[TCA_TCINDEX_HASH-1]); 241 } 242 243 if (tb[TCA_TCINDEX_MASK-1]) { 244 if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK-1]) < sizeof(u16)) 245 goto errout; 246 cp.mask = *(u16 *) RTA_DATA(tb[TCA_TCINDEX_MASK-1]); 247 } 248 249 if (tb[TCA_TCINDEX_SHIFT-1]) { 250 if (RTA_PAYLOAD(tb[TCA_TCINDEX_SHIFT-1]) < sizeof(u16)) 251 goto errout; 252 cp.shift = *(u16 *) RTA_DATA(tb[TCA_TCINDEX_SHIFT-1]); 253 } 254 255 err = -EBUSY; 256 /* Hash already allocated, make sure that we still meet the 257 * requirements for the allocated hash. 258 */ 259 if (cp.perfect) { 260 if (!valid_perfect_hash(&cp) || 261 cp.hash > cp.alloc_hash) 262 goto errout; 263 } else if (cp.h && cp.hash != cp.alloc_hash) 264 goto errout; 265 266 err = -EINVAL; 267 if (tb[TCA_TCINDEX_FALL_THROUGH-1]) { 268 if (RTA_PAYLOAD(tb[TCA_TCINDEX_FALL_THROUGH-1]) < sizeof(u32)) 269 goto errout; 270 cp.fall_through = 271 *(u32 *) RTA_DATA(tb[TCA_TCINDEX_FALL_THROUGH-1]); 272 } 273 274 if (!cp.hash) { 275 /* Hash not specified, use perfect hash if the upper limit 276 * of the hashing index is below the threshold. 277 */ 278 if ((cp.mask >> cp.shift) < PERFECT_HASH_THRESHOLD) 279 cp.hash = (cp.mask >> cp.shift)+1; 280 else 281 cp.hash = DEFAULT_HASH_SIZE; 282 } 283 284 if (!cp.perfect && !cp.h) 285 cp.alloc_hash = cp.hash; 286 287 /* Note: this could be as restrictive as if (handle & ~(mask >> shift)) 288 * but then, we'd fail handles that may become valid after some future 289 * mask change. While this is extremely unlikely to ever matter, 290 * the check below is safer (and also more backwards-compatible). 291 */ 292 if (cp.perfect || valid_perfect_hash(&cp)) 293 if (handle >= cp.alloc_hash) 294 goto errout; 295 296 297 err = -ENOMEM; 298 if (!cp.perfect && !cp.h) { 299 if (valid_perfect_hash(&cp)) { 300 cp.perfect = kmalloc(cp.hash * sizeof(*r), GFP_KERNEL); 301 if (!cp.perfect) 302 goto errout; 303 memset(cp.perfect, 0, cp.hash * sizeof(*r)); 304 balloc = 1; 305 } else { 306 cp.h = kmalloc(cp.hash * sizeof(f), GFP_KERNEL); 307 if (!cp.h) 308 goto errout; 309 memset(cp.h, 0, cp.hash * sizeof(f)); 310 balloc = 2; 311 } 312 } 313 314 if (cp.perfect) 315 r = cp.perfect + handle; 316 else 317 r = tcindex_lookup(&cp, handle) ? : &new_filter_result; 318 319 if (r == &new_filter_result) { 320 f = kmalloc(sizeof(*f), GFP_KERNEL); 321 if (!f) 322 goto errout_alloc; 323 memset(f, 0, sizeof(*f)); 324 } 325 326 if (tb[TCA_TCINDEX_CLASSID-1]) { 327 cr.res.classid = *(u32 *) RTA_DATA(tb[TCA_TCINDEX_CLASSID-1]); 328 tcf_bind_filter(tp, &cr.res, base); 329 } 330 331 tcf_exts_change(tp, &cr.exts, &e); 332 333 tcf_tree_lock(tp); 334 if (old_r && old_r != r) 335 memset(old_r, 0, sizeof(*old_r)); 336 337 memcpy(p, &cp, sizeof(cp)); 338 memcpy(r, &cr, sizeof(cr)); 339 340 if (r == &new_filter_result) { 341 struct tcindex_filter **fp; 342 343 f->key = handle; 344 f->result = new_filter_result; 345 f->next = NULL; 346 for (fp = p->h+(handle % p->hash); *fp; fp = &(*fp)->next) 347 /* nothing */; 348 *fp = f; 349 } 350 tcf_tree_unlock(tp); 351 352 return 0; 353 354errout_alloc: 355 if (balloc == 1) 356 kfree(cp.perfect); 357 else if (balloc == 2) 358 kfree(cp.h); 359errout: 360 tcf_exts_destroy(tp, &e); 361 return err; 362} 363 364static int 365tcindex_change(struct tcf_proto *tp, unsigned long base, u32 handle, 366 struct rtattr **tca, unsigned long *arg) 367{ 368 struct rtattr *opt = tca[TCA_OPTIONS-1]; 369 struct rtattr *tb[TCA_TCINDEX_MAX]; 370 struct tcindex_data *p = PRIV(tp); 371 struct tcindex_filter_result *r = (struct tcindex_filter_result *) *arg; 372 373 DPRINTK("tcindex_change(tp %p,handle 0x%08x,tca %p,arg %p),opt %p," 374 "p %p,r %p,*arg 0x%lx\n", 375 tp, handle, tca, arg, opt, p, r, arg ? *arg : 0L); 376 377 if (!opt) 378 return 0; 379 380 if (rtattr_parse_nested(tb, TCA_TCINDEX_MAX, opt) < 0) 381 return -EINVAL; 382 383 return tcindex_set_parms(tp, base, handle, p, r, tb, tca[TCA_RATE-1]); 384} 385 386 387static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker) 388{ 389 struct tcindex_data *p = PRIV(tp); 390 struct tcindex_filter *f,*next; 391 int i; 392 393 DPRINTK("tcindex_walk(tp %p,walker %p),p %p\n",tp,walker,p); 394 if (p->perfect) { 395 for (i = 0; i < p->hash; i++) { 396 if (!p->perfect[i].res.class) 397 continue; 398 if (walker->count >= walker->skip) { 399 if (walker->fn(tp, 400 (unsigned long) (p->perfect+i), walker) 401 < 0) { 402 walker->stop = 1; 403 return; 404 } 405 } 406 walker->count++; 407 } 408 } 409 if (!p->h) 410 return; 411 for (i = 0; i < p->hash; i++) { 412 for (f = p->h[i]; f; f = next) { 413 next = f->next; 414 if (walker->count >= walker->skip) { 415 if (walker->fn(tp,(unsigned long) &f->result, 416 walker) < 0) { 417 walker->stop = 1; 418 return; 419 } 420 } 421 walker->count++; 422 } 423 } 424} 425 426 427static int tcindex_destroy_element(struct tcf_proto *tp, 428 unsigned long arg, struct tcf_walker *walker) 429{ 430 return __tcindex_delete(tp, arg, 0); 431} 432 433 434static void tcindex_destroy(struct tcf_proto *tp) 435{ 436 struct tcindex_data *p = PRIV(tp); 437 struct tcf_walker walker; 438 439 DPRINTK("tcindex_destroy(tp %p),p %p\n",tp,p); 440 walker.count = 0; 441 walker.skip = 0; 442 walker.fn = &tcindex_destroy_element; 443 tcindex_walk(tp,&walker); 444 kfree(p->perfect); 445 kfree(p->h); 446 kfree(p); 447 tp->root = NULL; 448} 449 450 451static int tcindex_dump(struct tcf_proto *tp, unsigned long fh, 452 struct sk_buff *skb, struct tcmsg *t) 453{ 454 struct tcindex_data *p = PRIV(tp); 455 struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh; 456 unsigned char *b = skb->tail; 457 struct rtattr *rta; 458 459 DPRINTK("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n", 460 tp,fh,skb,t,p,r,b); 461 DPRINTK("p->perfect %p p->h %p\n",p->perfect,p->h); 462 rta = (struct rtattr *) b; 463 RTA_PUT(skb,TCA_OPTIONS,0,NULL); 464 if (!fh) { 465 t->tcm_handle = ~0; /* whatever ... */ 466 RTA_PUT(skb,TCA_TCINDEX_HASH,sizeof(p->hash),&p->hash); 467 RTA_PUT(skb,TCA_TCINDEX_MASK,sizeof(p->mask),&p->mask); 468 RTA_PUT(skb,TCA_TCINDEX_SHIFT,sizeof(p->shift),&p->shift); 469 RTA_PUT(skb,TCA_TCINDEX_FALL_THROUGH,sizeof(p->fall_through), 470 &p->fall_through); 471 rta->rta_len = skb->tail-b; 472 } else { 473 if (p->perfect) { 474 t->tcm_handle = r-p->perfect; 475 } else { 476 struct tcindex_filter *f; 477 int i; 478 479 t->tcm_handle = 0; 480 for (i = 0; !t->tcm_handle && i < p->hash; i++) { 481 for (f = p->h[i]; !t->tcm_handle && f; 482 f = f->next) { 483 if (&f->result == r) 484 t->tcm_handle = f->key; 485 } 486 } 487 } 488 DPRINTK("handle = %d\n",t->tcm_handle); 489 if (r->res.class) 490 RTA_PUT(skb, TCA_TCINDEX_CLASSID, 4, &r->res.classid); 491 492 if (tcf_exts_dump(skb, &r->exts, &tcindex_ext_map) < 0) 493 goto rtattr_failure; 494 rta->rta_len = skb->tail-b; 495 496 if (tcf_exts_dump_stats(skb, &r->exts, &tcindex_ext_map) < 0) 497 goto rtattr_failure; 498 } 499 500 return skb->len; 501 502rtattr_failure: 503 skb_trim(skb, b - skb->data); 504 return -1; 505} 506 507static struct tcf_proto_ops cls_tcindex_ops = { 508 .next = NULL, 509 .kind = "tcindex", 510 .classify = tcindex_classify, 511 .init = tcindex_init, 512 .destroy = tcindex_destroy, 513 .get = tcindex_get, 514 .put = tcindex_put, 515 .change = tcindex_change, 516 .delete = tcindex_delete, 517 .walk = tcindex_walk, 518 .dump = tcindex_dump, 519 .owner = THIS_MODULE, 520}; 521 522static int __init init_tcindex(void) 523{ 524 return register_tcf_proto_ops(&cls_tcindex_ops); 525} 526 527static void __exit exit_tcindex(void) 528{ 529 unregister_tcf_proto_ops(&cls_tcindex_ops); 530} 531 532module_init(init_tcindex) 533module_exit(exit_tcindex) 534MODULE_LICENSE("GPL");