0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065 struct rsvp_head {
0066 u32 tmap[256/32];
0067 u32 hgenerator;
0068 u8 tgenerator;
0069 struct rsvp_session __rcu *ht[256];
0070 struct rcu_head rcu;
0071 };
0072
0073 struct rsvp_session {
0074 struct rsvp_session __rcu *next;
0075 __be32 dst[RSVP_DST_LEN];
0076 struct tc_rsvp_gpi dpi;
0077 u8 protocol;
0078 u8 tunnelid;
0079
0080 struct rsvp_filter __rcu *ht[16 + 1];
0081 struct rcu_head rcu;
0082 };
0083
0084
0085 struct rsvp_filter {
0086 struct rsvp_filter __rcu *next;
0087 __be32 src[RSVP_DST_LEN];
0088 struct tc_rsvp_gpi spi;
0089 u8 tunnelhdr;
0090
0091 struct tcf_result res;
0092 struct tcf_exts exts;
0093
0094 u32 handle;
0095 struct rsvp_session *sess;
0096 struct rcu_work rwork;
0097 };
0098
0099 static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
0100 {
0101 unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
0102
0103 h ^= h>>16;
0104 h ^= h>>8;
0105 return (h ^ protocol ^ tunnelid) & 0xFF;
0106 }
0107
0108 static inline unsigned int hash_src(__be32 *src)
0109 {
0110 unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
0111
0112 h ^= h>>16;
0113 h ^= h>>8;
0114 h ^= h>>4;
0115 return h & 0xF;
0116 }
0117
0118 #define RSVP_APPLY_RESULT() \
0119 { \
0120 int r = tcf_exts_exec(skb, &f->exts, res); \
0121 if (r < 0) \
0122 continue; \
0123 else if (r > 0) \
0124 return r; \
0125 }
0126
0127 static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
0128 struct tcf_result *res)
0129 {
0130 struct rsvp_head *head = rcu_dereference_bh(tp->root);
0131 struct rsvp_session *s;
0132 struct rsvp_filter *f;
0133 unsigned int h1, h2;
0134 __be32 *dst, *src;
0135 u8 protocol;
0136 u8 tunnelid = 0;
0137 u8 *xprt;
0138 #if RSVP_DST_LEN == 4
0139 struct ipv6hdr *nhptr;
0140
0141 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
0142 return -1;
0143 nhptr = ipv6_hdr(skb);
0144 #else
0145 struct iphdr *nhptr;
0146
0147 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
0148 return -1;
0149 nhptr = ip_hdr(skb);
0150 #endif
0151 restart:
0152
0153 #if RSVP_DST_LEN == 4
0154 src = &nhptr->saddr.s6_addr32[0];
0155 dst = &nhptr->daddr.s6_addr32[0];
0156 protocol = nhptr->nexthdr;
0157 xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
0158 #else
0159 src = &nhptr->saddr;
0160 dst = &nhptr->daddr;
0161 protocol = nhptr->protocol;
0162 xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
0163 if (ip_is_fragment(nhptr))
0164 return -1;
0165 #endif
0166
0167 h1 = hash_dst(dst, protocol, tunnelid);
0168 h2 = hash_src(src);
0169
0170 for (s = rcu_dereference_bh(head->ht[h1]); s;
0171 s = rcu_dereference_bh(s->next)) {
0172 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
0173 protocol == s->protocol &&
0174 !(s->dpi.mask &
0175 (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
0176 #if RSVP_DST_LEN == 4
0177 dst[0] == s->dst[0] &&
0178 dst[1] == s->dst[1] &&
0179 dst[2] == s->dst[2] &&
0180 #endif
0181 tunnelid == s->tunnelid) {
0182
0183 for (f = rcu_dereference_bh(s->ht[h2]); f;
0184 f = rcu_dereference_bh(f->next)) {
0185 if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
0186 !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
0187 #if RSVP_DST_LEN == 4
0188 &&
0189 src[0] == f->src[0] &&
0190 src[1] == f->src[1] &&
0191 src[2] == f->src[2]
0192 #endif
0193 ) {
0194 *res = f->res;
0195 RSVP_APPLY_RESULT();
0196
0197 matched:
0198 if (f->tunnelhdr == 0)
0199 return 0;
0200
0201 tunnelid = f->res.classid;
0202 nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
0203 goto restart;
0204 }
0205 }
0206
0207
0208 for (f = rcu_dereference_bh(s->ht[16]); f;
0209 f = rcu_dereference_bh(f->next)) {
0210 *res = f->res;
0211 RSVP_APPLY_RESULT();
0212 goto matched;
0213 }
0214 return -1;
0215 }
0216 }
0217 return -1;
0218 }
0219
0220 static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
0221 {
0222 struct rsvp_head *head = rtnl_dereference(tp->root);
0223 struct rsvp_session *s;
0224 struct rsvp_filter __rcu **ins;
0225 struct rsvp_filter *pins;
0226 unsigned int h1 = h & 0xFF;
0227 unsigned int h2 = (h >> 8) & 0xFF;
0228
0229 for (s = rtnl_dereference(head->ht[h1]); s;
0230 s = rtnl_dereference(s->next)) {
0231 for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
0232 ins = &pins->next, pins = rtnl_dereference(*ins)) {
0233 if (pins->handle == h) {
0234 RCU_INIT_POINTER(n->next, pins->next);
0235 rcu_assign_pointer(*ins, n);
0236 return;
0237 }
0238 }
0239 }
0240
0241
0242
0243
0244 BUG_ON(1);
0245 }
0246
0247 static void *rsvp_get(struct tcf_proto *tp, u32 handle)
0248 {
0249 struct rsvp_head *head = rtnl_dereference(tp->root);
0250 struct rsvp_session *s;
0251 struct rsvp_filter *f;
0252 unsigned int h1 = handle & 0xFF;
0253 unsigned int h2 = (handle >> 8) & 0xFF;
0254
0255 if (h2 > 16)
0256 return NULL;
0257
0258 for (s = rtnl_dereference(head->ht[h1]); s;
0259 s = rtnl_dereference(s->next)) {
0260 for (f = rtnl_dereference(s->ht[h2]); f;
0261 f = rtnl_dereference(f->next)) {
0262 if (f->handle == handle)
0263 return f;
0264 }
0265 }
0266 return NULL;
0267 }
0268
0269 static int rsvp_init(struct tcf_proto *tp)
0270 {
0271 struct rsvp_head *data;
0272
0273 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
0274 if (data) {
0275 rcu_assign_pointer(tp->root, data);
0276 return 0;
0277 }
0278 return -ENOBUFS;
0279 }
0280
0281 static void __rsvp_delete_filter(struct rsvp_filter *f)
0282 {
0283 tcf_exts_destroy(&f->exts);
0284 tcf_exts_put_net(&f->exts);
0285 kfree(f);
0286 }
0287
0288 static void rsvp_delete_filter_work(struct work_struct *work)
0289 {
0290 struct rsvp_filter *f = container_of(to_rcu_work(work),
0291 struct rsvp_filter,
0292 rwork);
0293 rtnl_lock();
0294 __rsvp_delete_filter(f);
0295 rtnl_unlock();
0296 }
0297
0298 static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
0299 {
0300 tcf_unbind_filter(tp, &f->res);
0301
0302
0303
0304
0305 if (tcf_exts_get_net(&f->exts))
0306 tcf_queue_work(&f->rwork, rsvp_delete_filter_work);
0307 else
0308 __rsvp_delete_filter(f);
0309 }
0310
0311 static void rsvp_destroy(struct tcf_proto *tp, bool rtnl_held,
0312 struct netlink_ext_ack *extack)
0313 {
0314 struct rsvp_head *data = rtnl_dereference(tp->root);
0315 int h1, h2;
0316
0317 if (data == NULL)
0318 return;
0319
0320 for (h1 = 0; h1 < 256; h1++) {
0321 struct rsvp_session *s;
0322
0323 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
0324 RCU_INIT_POINTER(data->ht[h1], s->next);
0325
0326 for (h2 = 0; h2 <= 16; h2++) {
0327 struct rsvp_filter *f;
0328
0329 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
0330 rcu_assign_pointer(s->ht[h2], f->next);
0331 rsvp_delete_filter(tp, f);
0332 }
0333 }
0334 kfree_rcu(s, rcu);
0335 }
0336 }
0337 kfree_rcu(data, rcu);
0338 }
0339
0340 static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last,
0341 bool rtnl_held, struct netlink_ext_ack *extack)
0342 {
0343 struct rsvp_head *head = rtnl_dereference(tp->root);
0344 struct rsvp_filter *nfp, *f = arg;
0345 struct rsvp_filter __rcu **fp;
0346 unsigned int h = f->handle;
0347 struct rsvp_session __rcu **sp;
0348 struct rsvp_session *nsp, *s = f->sess;
0349 int i, h1;
0350
0351 fp = &s->ht[(h >> 8) & 0xFF];
0352 for (nfp = rtnl_dereference(*fp); nfp;
0353 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
0354 if (nfp == f) {
0355 RCU_INIT_POINTER(*fp, f->next);
0356 rsvp_delete_filter(tp, f);
0357
0358
0359
0360 for (i = 0; i <= 16; i++)
0361 if (s->ht[i])
0362 goto out;
0363
0364
0365 sp = &head->ht[h & 0xFF];
0366 for (nsp = rtnl_dereference(*sp); nsp;
0367 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
0368 if (nsp == s) {
0369 RCU_INIT_POINTER(*sp, s->next);
0370 kfree_rcu(s, rcu);
0371 goto out;
0372 }
0373 }
0374
0375 break;
0376 }
0377 }
0378
0379 out:
0380 *last = true;
0381 for (h1 = 0; h1 < 256; h1++) {
0382 if (rcu_access_pointer(head->ht[h1])) {
0383 *last = false;
0384 break;
0385 }
0386 }
0387
0388 return 0;
0389 }
0390
0391 static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
0392 {
0393 struct rsvp_head *data = rtnl_dereference(tp->root);
0394 int i = 0xFFFF;
0395
0396 while (i-- > 0) {
0397 u32 h;
0398
0399 if ((data->hgenerator += 0x10000) == 0)
0400 data->hgenerator = 0x10000;
0401 h = data->hgenerator|salt;
0402 if (!rsvp_get(tp, h))
0403 return h;
0404 }
0405 return 0;
0406 }
0407
0408 static int tunnel_bts(struct rsvp_head *data)
0409 {
0410 int n = data->tgenerator >> 5;
0411 u32 b = 1 << (data->tgenerator & 0x1F);
0412
0413 if (data->tmap[n] & b)
0414 return 0;
0415 data->tmap[n] |= b;
0416 return 1;
0417 }
0418
0419 static void tunnel_recycle(struct rsvp_head *data)
0420 {
0421 struct rsvp_session __rcu **sht = data->ht;
0422 u32 tmap[256/32];
0423 int h1, h2;
0424
0425 memset(tmap, 0, sizeof(tmap));
0426
0427 for (h1 = 0; h1 < 256; h1++) {
0428 struct rsvp_session *s;
0429 for (s = rtnl_dereference(sht[h1]); s;
0430 s = rtnl_dereference(s->next)) {
0431 for (h2 = 0; h2 <= 16; h2++) {
0432 struct rsvp_filter *f;
0433
0434 for (f = rtnl_dereference(s->ht[h2]); f;
0435 f = rtnl_dereference(f->next)) {
0436 if (f->tunnelhdr == 0)
0437 continue;
0438 data->tgenerator = f->res.classid;
0439 tunnel_bts(data);
0440 }
0441 }
0442 }
0443 }
0444
0445 memcpy(data->tmap, tmap, sizeof(tmap));
0446 }
0447
0448 static u32 gen_tunnel(struct rsvp_head *data)
0449 {
0450 int i, k;
0451
0452 for (k = 0; k < 2; k++) {
0453 for (i = 255; i > 0; i--) {
0454 if (++data->tgenerator == 0)
0455 data->tgenerator = 1;
0456 if (tunnel_bts(data))
0457 return data->tgenerator;
0458 }
0459 tunnel_recycle(data);
0460 }
0461 return 0;
0462 }
0463
0464 static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
0465 [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
0466 [TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) },
0467 [TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) },
0468 [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
0469 };
0470
0471 static int rsvp_change(struct net *net, struct sk_buff *in_skb,
0472 struct tcf_proto *tp, unsigned long base,
0473 u32 handle, struct nlattr **tca,
0474 void **arg, u32 flags,
0475 struct netlink_ext_ack *extack)
0476 {
0477 struct rsvp_head *data = rtnl_dereference(tp->root);
0478 struct rsvp_filter *f, *nfp;
0479 struct rsvp_filter __rcu **fp;
0480 struct rsvp_session *nsp, *s;
0481 struct rsvp_session __rcu **sp;
0482 struct tc_rsvp_pinfo *pinfo = NULL;
0483 struct nlattr *opt = tca[TCA_OPTIONS];
0484 struct nlattr *tb[TCA_RSVP_MAX + 1];
0485 struct tcf_exts e;
0486 unsigned int h1, h2;
0487 __be32 *dst;
0488 int err;
0489
0490 if (opt == NULL)
0491 return handle ? -EINVAL : 0;
0492
0493 err = nla_parse_nested_deprecated(tb, TCA_RSVP_MAX, opt, rsvp_policy,
0494 NULL);
0495 if (err < 0)
0496 return err;
0497
0498 err = tcf_exts_init(&e, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
0499 if (err < 0)
0500 return err;
0501 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, flags,
0502 extack);
0503 if (err < 0)
0504 goto errout2;
0505
0506 f = *arg;
0507 if (f) {
0508
0509 struct rsvp_filter *n;
0510
0511 if (f->handle != handle && handle)
0512 goto errout2;
0513
0514 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
0515 if (!n) {
0516 err = -ENOMEM;
0517 goto errout2;
0518 }
0519
0520 err = tcf_exts_init(&n->exts, net, TCA_RSVP_ACT,
0521 TCA_RSVP_POLICE);
0522 if (err < 0) {
0523 kfree(n);
0524 goto errout2;
0525 }
0526
0527 if (tb[TCA_RSVP_CLASSID]) {
0528 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
0529 tcf_bind_filter(tp, &n->res, base);
0530 }
0531
0532 tcf_exts_change(&n->exts, &e);
0533 rsvp_replace(tp, n, handle);
0534 return 0;
0535 }
0536
0537
0538 err = -EINVAL;
0539 if (handle)
0540 goto errout2;
0541 if (tb[TCA_RSVP_DST] == NULL)
0542 goto errout2;
0543
0544 err = -ENOBUFS;
0545 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
0546 if (f == NULL)
0547 goto errout2;
0548
0549 err = tcf_exts_init(&f->exts, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
0550 if (err < 0)
0551 goto errout;
0552 h2 = 16;
0553 if (tb[TCA_RSVP_SRC]) {
0554 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
0555 h2 = hash_src(f->src);
0556 }
0557 if (tb[TCA_RSVP_PINFO]) {
0558 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
0559 f->spi = pinfo->spi;
0560 f->tunnelhdr = pinfo->tunnelhdr;
0561 }
0562 if (tb[TCA_RSVP_CLASSID])
0563 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
0564
0565 dst = nla_data(tb[TCA_RSVP_DST]);
0566 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
0567
0568 err = -ENOMEM;
0569 if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
0570 goto errout;
0571
0572 if (f->tunnelhdr) {
0573 err = -EINVAL;
0574 if (f->res.classid > 255)
0575 goto errout;
0576
0577 err = -ENOMEM;
0578 if (f->res.classid == 0 &&
0579 (f->res.classid = gen_tunnel(data)) == 0)
0580 goto errout;
0581 }
0582
0583 for (sp = &data->ht[h1];
0584 (s = rtnl_dereference(*sp)) != NULL;
0585 sp = &s->next) {
0586 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
0587 pinfo && pinfo->protocol == s->protocol &&
0588 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
0589 #if RSVP_DST_LEN == 4
0590 dst[0] == s->dst[0] &&
0591 dst[1] == s->dst[1] &&
0592 dst[2] == s->dst[2] &&
0593 #endif
0594 pinfo->tunnelid == s->tunnelid) {
0595
0596 insert:
0597
0598
0599 fp = &s->ht[h2];
0600
0601 f->sess = s;
0602 if (f->tunnelhdr == 0)
0603 tcf_bind_filter(tp, &f->res, base);
0604
0605 tcf_exts_change(&f->exts, &e);
0606
0607 fp = &s->ht[h2];
0608 for (nfp = rtnl_dereference(*fp); nfp;
0609 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
0610 __u32 mask = nfp->spi.mask & f->spi.mask;
0611
0612 if (mask != f->spi.mask)
0613 break;
0614 }
0615 RCU_INIT_POINTER(f->next, nfp);
0616 rcu_assign_pointer(*fp, f);
0617
0618 *arg = f;
0619 return 0;
0620 }
0621 }
0622
0623
0624
0625 err = -ENOBUFS;
0626 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
0627 if (s == NULL)
0628 goto errout;
0629 memcpy(s->dst, dst, sizeof(s->dst));
0630
0631 if (pinfo) {
0632 s->dpi = pinfo->dpi;
0633 s->protocol = pinfo->protocol;
0634 s->tunnelid = pinfo->tunnelid;
0635 }
0636 sp = &data->ht[h1];
0637 for (nsp = rtnl_dereference(*sp); nsp;
0638 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
0639 if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
0640 break;
0641 }
0642 RCU_INIT_POINTER(s->next, nsp);
0643 rcu_assign_pointer(*sp, s);
0644
0645 goto insert;
0646
0647 errout:
0648 tcf_exts_destroy(&f->exts);
0649 kfree(f);
0650 errout2:
0651 tcf_exts_destroy(&e);
0652 return err;
0653 }
0654
0655 static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg,
0656 bool rtnl_held)
0657 {
0658 struct rsvp_head *head = rtnl_dereference(tp->root);
0659 unsigned int h, h1;
0660
0661 if (arg->stop)
0662 return;
0663
0664 for (h = 0; h < 256; h++) {
0665 struct rsvp_session *s;
0666
0667 for (s = rtnl_dereference(head->ht[h]); s;
0668 s = rtnl_dereference(s->next)) {
0669 for (h1 = 0; h1 <= 16; h1++) {
0670 struct rsvp_filter *f;
0671
0672 for (f = rtnl_dereference(s->ht[h1]); f;
0673 f = rtnl_dereference(f->next)) {
0674 if (arg->count < arg->skip) {
0675 arg->count++;
0676 continue;
0677 }
0678 if (arg->fn(tp, f, arg) < 0) {
0679 arg->stop = 1;
0680 return;
0681 }
0682 arg->count++;
0683 }
0684 }
0685 }
0686 }
0687 }
0688
0689 static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
0690 struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
0691 {
0692 struct rsvp_filter *f = fh;
0693 struct rsvp_session *s;
0694 struct nlattr *nest;
0695 struct tc_rsvp_pinfo pinfo;
0696
0697 if (f == NULL)
0698 return skb->len;
0699 s = f->sess;
0700
0701 t->tcm_handle = f->handle;
0702
0703 nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
0704 if (nest == NULL)
0705 goto nla_put_failure;
0706
0707 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
0708 goto nla_put_failure;
0709 pinfo.dpi = s->dpi;
0710 pinfo.spi = f->spi;
0711 pinfo.protocol = s->protocol;
0712 pinfo.tunnelid = s->tunnelid;
0713 pinfo.tunnelhdr = f->tunnelhdr;
0714 pinfo.pad = 0;
0715 if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
0716 goto nla_put_failure;
0717 if (f->res.classid &&
0718 nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
0719 goto nla_put_failure;
0720 if (((f->handle >> 8) & 0xFF) != 16 &&
0721 nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
0722 goto nla_put_failure;
0723
0724 if (tcf_exts_dump(skb, &f->exts) < 0)
0725 goto nla_put_failure;
0726
0727 nla_nest_end(skb, nest);
0728
0729 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
0730 goto nla_put_failure;
0731 return skb->len;
0732
0733 nla_put_failure:
0734 nla_nest_cancel(skb, nest);
0735 return -1;
0736 }
0737
0738 static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
0739 unsigned long base)
0740 {
0741 struct rsvp_filter *f = fh;
0742
0743 if (f && f->res.classid == classid) {
0744 if (cl)
0745 __tcf_bind_filter(q, &f->res, base);
0746 else
0747 __tcf_unbind_filter(q, &f->res);
0748 }
0749 }
0750
0751 static struct tcf_proto_ops RSVP_OPS __read_mostly = {
0752 .kind = RSVP_ID,
0753 .classify = rsvp_classify,
0754 .init = rsvp_init,
0755 .destroy = rsvp_destroy,
0756 .get = rsvp_get,
0757 .change = rsvp_change,
0758 .delete = rsvp_delete,
0759 .walk = rsvp_walk,
0760 .dump = rsvp_dump,
0761 .bind_class = rsvp_bind_class,
0762 .owner = THIS_MODULE,
0763 };
0764
0765 static int __init init_rsvp(void)
0766 {
0767 return register_tcf_proto_ops(&RSVP_OPS);
0768 }
0769
0770 static void __exit exit_rsvp(void)
0771 {
0772 unregister_tcf_proto_ops(&RSVP_OPS);
0773 }
0774
0775 module_init(init_rsvp)
0776 module_exit(exit_rsvp)