1 /* 2 * net/sched/cls_fw.c Classifier mapping ipchains' fwmark to traffic class. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 10 * 11 * Changes: 12 * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_walk off by one 13 * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_delete killed all the filter (and kernel). 14 * Alex <alex@pilotsoft.com> : 2004xxyy: Added Action extension 15 * 16 * JHS: We should remove the CONFIG_NET_CLS_IND from here 17 * eventually when the meta match extension is made available 18 * 19 */ 20 21 #include <linux/module.h> 22 #include <linux/slab.h> 23 #include <linux/types.h> 24 #include <linux/kernel.h> 25 #include <linux/string.h> 26 #include <linux/errno.h> 27 #include <linux/skbuff.h> 28 #include <net/netlink.h> 29 #include <net/act_api.h> 30 #include <net/pkt_cls.h> 31 32 #define HTSIZE 256 33 34 struct fw_head { 35 u32 mask; 36 struct fw_filter __rcu *ht[HTSIZE]; 37 struct rcu_head rcu; 38 }; 39 40 struct fw_filter { 41 struct fw_filter __rcu *next; 42 u32 id; 43 struct tcf_result res; 44 #ifdef CONFIG_NET_CLS_IND 45 int ifindex; 46 #endif /* CONFIG_NET_CLS_IND */ 47 struct tcf_exts exts; 48 struct tcf_proto *tp; 49 struct rcu_head rcu; 50 }; 51 52 static u32 fw_hash(u32 handle) 53 { 54 handle ^= (handle >> 16); 55 handle ^= (handle >> 8); 56 return handle % HTSIZE; 57 } 58 59 static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, 60 struct tcf_result *res) 61 { 62 struct fw_head *head = rcu_dereference_bh(tp->root); 63 struct fw_filter *f; 64 int r; 65 u32 id = skb->mark; 66 67 if (head != NULL) { 68 id &= head->mask; 69 70 for (f = rcu_dereference_bh(head->ht[fw_hash(id)]); f; 71 f = rcu_dereference_bh(f->next)) { 72 if (f->id == id) { 73 *res = f->res; 74 #ifdef CONFIG_NET_CLS_IND 75 if (!tcf_match_indev(skb, f->ifindex)) 76 continue; 77 #endif /* CONFIG_NET_CLS_IND */ 78 r = tcf_exts_exec(skb, &f->exts, res); 79 if (r < 0) 80 continue; 81 82 return r; 83 } 84 } 85 } else { 86 /* Old method: classify the packet using its skb mark. */ 87 if (id && (TC_H_MAJ(id) == 0 || 88 !(TC_H_MAJ(id ^ tp->q->handle)))) { 89 res->classid = id; 90 res->class = 0; 91 return 0; 92 } 93 } 94 95 return -1; 96 } 97 98 static void *fw_get(struct tcf_proto *tp, u32 handle) 99 { 100 struct fw_head *head = rtnl_dereference(tp->root); 101 struct fw_filter *f; 102 103 if (head == NULL) 104 return NULL; 105 106 f = rtnl_dereference(head->ht[fw_hash(handle)]); 107 for (; f; f = rtnl_dereference(f->next)) { 108 if (f->id == handle) 109 return f; 110 } 111 return NULL; 112 } 113 114 static int fw_init(struct tcf_proto *tp) 115 { 116 /* We don't allocate fw_head here, because in the old method 117 * we don't need it at all. 118 */ 119 return 0; 120 } 121 122 static void fw_delete_filter(struct rcu_head *head) 123 { 124 struct fw_filter *f = container_of(head, struct fw_filter, rcu); 125 126 tcf_exts_destroy(&f->exts); 127 kfree(f); 128 } 129 130 static void fw_destroy(struct tcf_proto *tp) 131 { 132 struct fw_head *head = rtnl_dereference(tp->root); 133 struct fw_filter *f; 134 int h; 135 136 if (head == NULL) 137 return; 138 139 for (h = 0; h < HTSIZE; h++) { 140 while ((f = rtnl_dereference(head->ht[h])) != NULL) { 141 RCU_INIT_POINTER(head->ht[h], 142 rtnl_dereference(f->next)); 143 tcf_unbind_filter(tp, &f->res); 144 call_rcu(&f->rcu, fw_delete_filter); 145 } 146 } 147 kfree_rcu(head, rcu); 148 } 149 150 static int fw_delete(struct tcf_proto *tp, void *arg, bool *last) 151 { 152 struct fw_head *head = rtnl_dereference(tp->root); 153 struct fw_filter *f = arg; 154 struct fw_filter __rcu **fp; 155 struct fw_filter *pfp; 156 int ret = -EINVAL; 157 int h; 158 159 if (head == NULL || f == NULL) 160 goto out; 161 162 fp = &head->ht[fw_hash(f->id)]; 163 164 for (pfp = rtnl_dereference(*fp); pfp; 165 fp = &pfp->next, pfp = rtnl_dereference(*fp)) { 166 if (pfp == f) { 167 RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); 168 tcf_unbind_filter(tp, &f->res); 169 call_rcu(&f->rcu, fw_delete_filter); 170 ret = 0; 171 break; 172 } 173 } 174 175 *last = true; 176 for (h = 0; h < HTSIZE; h++) { 177 if (rcu_access_pointer(head->ht[h])) { 178 *last = false; 179 break; 180 } 181 } 182 183 out: 184 return ret; 185 } 186 187 static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { 188 [TCA_FW_CLASSID] = { .type = NLA_U32 }, 189 [TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ }, 190 [TCA_FW_MASK] = { .type = NLA_U32 }, 191 }; 192 193 static int fw_set_parms(struct net *net, struct tcf_proto *tp, 194 struct fw_filter *f, struct nlattr **tb, 195 struct nlattr **tca, unsigned long base, bool ovr) 196 { 197 struct fw_head *head = rtnl_dereference(tp->root); 198 u32 mask; 199 int err; 200 201 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &f->exts, ovr); 202 if (err < 0) 203 return err; 204 205 if (tb[TCA_FW_CLASSID]) { 206 f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); 207 tcf_bind_filter(tp, &f->res, base); 208 } 209 210 #ifdef CONFIG_NET_CLS_IND 211 if (tb[TCA_FW_INDEV]) { 212 int ret; 213 ret = tcf_change_indev(net, tb[TCA_FW_INDEV]); 214 if (ret < 0) 215 return ret; 216 f->ifindex = ret; 217 } 218 #endif /* CONFIG_NET_CLS_IND */ 219 220 err = -EINVAL; 221 if (tb[TCA_FW_MASK]) { 222 mask = nla_get_u32(tb[TCA_FW_MASK]); 223 if (mask != head->mask) 224 return err; 225 } else if (head->mask != 0xFFFFFFFF) 226 return err; 227 228 return 0; 229 } 230 231 static int fw_change(struct net *net, struct sk_buff *in_skb, 232 struct tcf_proto *tp, unsigned long base, 233 u32 handle, struct nlattr **tca, void **arg, 234 bool ovr) 235 { 236 struct fw_head *head = rtnl_dereference(tp->root); 237 struct fw_filter *f = *arg; 238 struct nlattr *opt = tca[TCA_OPTIONS]; 239 struct nlattr *tb[TCA_FW_MAX + 1]; 240 int err; 241 242 if (!opt) 243 return handle ? -EINVAL : 0; /* Succeed if it is old method. */ 244 245 err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy, NULL); 246 if (err < 0) 247 return err; 248 249 if (f) { 250 struct fw_filter *pfp, *fnew; 251 struct fw_filter __rcu **fp; 252 253 if (f->id != handle && handle) 254 return -EINVAL; 255 256 fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); 257 if (!fnew) 258 return -ENOBUFS; 259 260 fnew->id = f->id; 261 fnew->res = f->res; 262 #ifdef CONFIG_NET_CLS_IND 263 fnew->ifindex = f->ifindex; 264 #endif /* CONFIG_NET_CLS_IND */ 265 fnew->tp = f->tp; 266 267 err = tcf_exts_init(&fnew->exts, TCA_FW_ACT, TCA_FW_POLICE); 268 if (err < 0) { 269 kfree(fnew); 270 return err; 271 } 272 273 err = fw_set_parms(net, tp, fnew, tb, tca, base, ovr); 274 if (err < 0) { 275 tcf_exts_destroy(&fnew->exts); 276 kfree(fnew); 277 return err; 278 } 279 280 fp = &head->ht[fw_hash(fnew->id)]; 281 for (pfp = rtnl_dereference(*fp); pfp; 282 fp = &pfp->next, pfp = rtnl_dereference(*fp)) 283 if (pfp == f) 284 break; 285 286 RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); 287 rcu_assign_pointer(*fp, fnew); 288 tcf_unbind_filter(tp, &f->res); 289 call_rcu(&f->rcu, fw_delete_filter); 290 291 *arg = fnew; 292 return err; 293 } 294 295 if (!handle) 296 return -EINVAL; 297 298 if (!head) { 299 u32 mask = 0xFFFFFFFF; 300 if (tb[TCA_FW_MASK]) 301 mask = nla_get_u32(tb[TCA_FW_MASK]); 302 303 head = kzalloc(sizeof(*head), GFP_KERNEL); 304 if (!head) 305 return -ENOBUFS; 306 head->mask = mask; 307 308 rcu_assign_pointer(tp->root, head); 309 } 310 311 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); 312 if (f == NULL) 313 return -ENOBUFS; 314 315 err = tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); 316 if (err < 0) 317 goto errout; 318 f->id = handle; 319 f->tp = tp; 320 321 err = fw_set_parms(net, tp, f, tb, tca, base, ovr); 322 if (err < 0) 323 goto errout; 324 325 RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]); 326 rcu_assign_pointer(head->ht[fw_hash(handle)], f); 327 328 *arg = f; 329 return 0; 330 331 errout: 332 tcf_exts_destroy(&f->exts); 333 kfree(f); 334 return err; 335 } 336 337 static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) 338 { 339 struct fw_head *head = rtnl_dereference(tp->root); 340 int h; 341 342 if (head == NULL) 343 arg->stop = 1; 344 345 if (arg->stop) 346 return; 347 348 for (h = 0; h < HTSIZE; h++) { 349 struct fw_filter *f; 350 351 for (f = rtnl_dereference(head->ht[h]); f; 352 f = rtnl_dereference(f->next)) { 353 if (arg->count < arg->skip) { 354 arg->count++; 355 continue; 356 } 357 if (arg->fn(tp, f, arg) < 0) { 358 arg->stop = 1; 359 return; 360 } 361 arg->count++; 362 } 363 } 364 } 365 366 static int fw_dump(struct net *net, struct tcf_proto *tp, void *fh, 367 struct sk_buff *skb, struct tcmsg *t) 368 { 369 struct fw_head *head = rtnl_dereference(tp->root); 370 struct fw_filter *f = fh; 371 struct nlattr *nest; 372 373 if (f == NULL) 374 return skb->len; 375 376 t->tcm_handle = f->id; 377 378 if (!f->res.classid && !tcf_exts_has_actions(&f->exts)) 379 return skb->len; 380 381 nest = nla_nest_start(skb, TCA_OPTIONS); 382 if (nest == NULL) 383 goto nla_put_failure; 384 385 if (f->res.classid && 386 nla_put_u32(skb, TCA_FW_CLASSID, f->res.classid)) 387 goto nla_put_failure; 388 #ifdef CONFIG_NET_CLS_IND 389 if (f->ifindex) { 390 struct net_device *dev; 391 dev = __dev_get_by_index(net, f->ifindex); 392 if (dev && nla_put_string(skb, TCA_FW_INDEV, dev->name)) 393 goto nla_put_failure; 394 } 395 #endif /* CONFIG_NET_CLS_IND */ 396 if (head->mask != 0xFFFFFFFF && 397 nla_put_u32(skb, TCA_FW_MASK, head->mask)) 398 goto nla_put_failure; 399 400 if (tcf_exts_dump(skb, &f->exts) < 0) 401 goto nla_put_failure; 402 403 nla_nest_end(skb, nest); 404 405 if (tcf_exts_dump_stats(skb, &f->exts) < 0) 406 goto nla_put_failure; 407 408 return skb->len; 409 410 nla_put_failure: 411 nla_nest_cancel(skb, nest); 412 return -1; 413 } 414 415 static void fw_bind_class(void *fh, u32 classid, unsigned long cl) 416 { 417 struct fw_filter *f = fh; 418 419 if (f && f->res.classid == classid) 420 f->res.class = cl; 421 } 422 423 static struct tcf_proto_ops cls_fw_ops __read_mostly = { 424 .kind = "fw", 425 .classify = fw_classify, 426 .init = fw_init, 427 .destroy = fw_destroy, 428 .get = fw_get, 429 .change = fw_change, 430 .delete = fw_delete, 431 .walk = fw_walk, 432 .dump = fw_dump, 433 .bind_class = fw_bind_class, 434 .owner = THIS_MODULE, 435 }; 436 437 static int __init init_fw(void) 438 { 439 return register_tcf_proto_ops(&cls_fw_ops); 440 } 441 442 static void __exit exit_fw(void) 443 { 444 unregister_tcf_proto_ops(&cls_fw_ops); 445 } 446 447 module_init(init_fw) 448 module_exit(exit_fw) 449 MODULE_LICENSE("GPL"); 450