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 bool mask_set; 37 struct fw_filter __rcu *ht[HTSIZE]; 38 struct rcu_head rcu; 39 }; 40 41 struct fw_filter { 42 struct fw_filter __rcu *next; 43 u32 id; 44 struct tcf_result res; 45 #ifdef CONFIG_NET_CLS_IND 46 int ifindex; 47 #endif /* CONFIG_NET_CLS_IND */ 48 struct tcf_exts exts; 49 struct tcf_proto *tp; 50 struct rcu_head rcu; 51 }; 52 53 static u32 fw_hash(u32 handle) 54 { 55 handle ^= (handle >> 16); 56 handle ^= (handle >> 8); 57 return handle % HTSIZE; 58 } 59 60 static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, 61 struct tcf_result *res) 62 { 63 struct fw_head *head = rcu_dereference_bh(tp->root); 64 struct fw_filter *f; 65 int r; 66 u32 id = skb->mark; 67 68 if (head != NULL) { 69 id &= head->mask; 70 71 for (f = rcu_dereference_bh(head->ht[fw_hash(id)]); f; 72 f = rcu_dereference_bh(f->next)) { 73 if (f->id == id) { 74 *res = f->res; 75 #ifdef CONFIG_NET_CLS_IND 76 if (!tcf_match_indev(skb, f->ifindex)) 77 continue; 78 #endif /* CONFIG_NET_CLS_IND */ 79 r = tcf_exts_exec(skb, &f->exts, res); 80 if (r < 0) 81 continue; 82 83 return r; 84 } 85 } 86 } else { 87 /* old method */ 88 if (id && (TC_H_MAJ(id) == 0 || 89 !(TC_H_MAJ(id ^ tp->q->handle)))) { 90 res->classid = id; 91 res->class = 0; 92 return 0; 93 } 94 } 95 96 return -1; 97 } 98 99 static unsigned long fw_get(struct tcf_proto *tp, u32 handle) 100 { 101 struct fw_head *head = rtnl_dereference(tp->root); 102 struct fw_filter *f; 103 104 if (head == NULL) 105 return 0; 106 107 f = rtnl_dereference(head->ht[fw_hash(handle)]); 108 for (; f; f = rtnl_dereference(f->next)) { 109 if (f->id == handle) 110 return (unsigned long)f; 111 } 112 return 0; 113 } 114 115 static int fw_init(struct tcf_proto *tp) 116 { 117 struct fw_head *head; 118 119 head = kzalloc(sizeof(struct fw_head), GFP_KERNEL); 120 if (head == NULL) 121 return -ENOBUFS; 122 123 head->mask_set = false; 124 rcu_assign_pointer(tp->root, head); 125 return 0; 126 } 127 128 static void fw_delete_filter(struct rcu_head *head) 129 { 130 struct fw_filter *f = container_of(head, struct fw_filter, rcu); 131 132 tcf_exts_destroy(&f->exts); 133 kfree(f); 134 } 135 136 static bool fw_destroy(struct tcf_proto *tp, bool force) 137 { 138 struct fw_head *head = rtnl_dereference(tp->root); 139 struct fw_filter *f; 140 int h; 141 142 if (head == NULL) 143 return true; 144 145 if (!force) { 146 for (h = 0; h < HTSIZE; h++) 147 if (rcu_access_pointer(head->ht[h])) 148 return false; 149 } 150 151 for (h = 0; h < HTSIZE; h++) { 152 while ((f = rtnl_dereference(head->ht[h])) != NULL) { 153 RCU_INIT_POINTER(head->ht[h], 154 rtnl_dereference(f->next)); 155 tcf_unbind_filter(tp, &f->res); 156 call_rcu(&f->rcu, fw_delete_filter); 157 } 158 } 159 RCU_INIT_POINTER(tp->root, NULL); 160 kfree_rcu(head, rcu); 161 return true; 162 } 163 164 static int fw_delete(struct tcf_proto *tp, unsigned long arg) 165 { 166 struct fw_head *head = rtnl_dereference(tp->root); 167 struct fw_filter *f = (struct fw_filter *)arg; 168 struct fw_filter __rcu **fp; 169 struct fw_filter *pfp; 170 171 if (head == NULL || f == NULL) 172 goto out; 173 174 fp = &head->ht[fw_hash(f->id)]; 175 176 for (pfp = rtnl_dereference(*fp); pfp; 177 fp = &pfp->next, pfp = rtnl_dereference(*fp)) { 178 if (pfp == f) { 179 RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); 180 tcf_unbind_filter(tp, &f->res); 181 call_rcu(&f->rcu, fw_delete_filter); 182 return 0; 183 } 184 } 185 out: 186 return -EINVAL; 187 } 188 189 static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { 190 [TCA_FW_CLASSID] = { .type = NLA_U32 }, 191 [TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ }, 192 [TCA_FW_MASK] = { .type = NLA_U32 }, 193 }; 194 195 static int 196 fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, 197 struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr) 198 { 199 struct fw_head *head = rtnl_dereference(tp->root); 200 struct tcf_exts e; 201 u32 mask; 202 int err; 203 204 tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE); 205 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); 206 if (err < 0) 207 return err; 208 209 if (tb[TCA_FW_CLASSID]) { 210 f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); 211 tcf_bind_filter(tp, &f->res, base); 212 } 213 214 #ifdef CONFIG_NET_CLS_IND 215 if (tb[TCA_FW_INDEV]) { 216 int ret; 217 ret = tcf_change_indev(net, tb[TCA_FW_INDEV]); 218 if (ret < 0) { 219 err = ret; 220 goto errout; 221 } 222 f->ifindex = ret; 223 } 224 #endif /* CONFIG_NET_CLS_IND */ 225 226 err = -EINVAL; 227 if (tb[TCA_FW_MASK]) { 228 mask = nla_get_u32(tb[TCA_FW_MASK]); 229 if (mask != head->mask) 230 goto errout; 231 } else if (head->mask != 0xFFFFFFFF) 232 goto errout; 233 234 tcf_exts_change(tp, &f->exts, &e); 235 236 return 0; 237 errout: 238 tcf_exts_destroy(&e); 239 return err; 240 } 241 242 static int fw_change(struct net *net, struct sk_buff *in_skb, 243 struct tcf_proto *tp, unsigned long base, 244 u32 handle, 245 struct nlattr **tca, 246 unsigned long *arg, bool ovr) 247 { 248 struct fw_head *head = rtnl_dereference(tp->root); 249 struct fw_filter *f = (struct fw_filter *) *arg; 250 struct nlattr *opt = tca[TCA_OPTIONS]; 251 struct nlattr *tb[TCA_FW_MAX + 1]; 252 int err; 253 254 if (!opt) 255 return handle ? -EINVAL : 0; 256 257 err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); 258 if (err < 0) 259 return err; 260 261 if (f) { 262 struct fw_filter *pfp, *fnew; 263 struct fw_filter __rcu **fp; 264 265 if (f->id != handle && handle) 266 return -EINVAL; 267 268 fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); 269 if (!fnew) 270 return -ENOBUFS; 271 272 fnew->id = f->id; 273 fnew->res = f->res; 274 #ifdef CONFIG_NET_CLS_IND 275 fnew->ifindex = f->ifindex; 276 #endif /* CONFIG_NET_CLS_IND */ 277 fnew->tp = f->tp; 278 279 tcf_exts_init(&fnew->exts, TCA_FW_ACT, TCA_FW_POLICE); 280 281 err = fw_change_attrs(net, tp, fnew, tb, tca, base, ovr); 282 if (err < 0) { 283 kfree(fnew); 284 return err; 285 } 286 287 fp = &head->ht[fw_hash(fnew->id)]; 288 for (pfp = rtnl_dereference(*fp); pfp; 289 fp = &pfp->next, pfp = rtnl_dereference(*fp)) 290 if (pfp == f) 291 break; 292 293 RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); 294 rcu_assign_pointer(*fp, fnew); 295 tcf_unbind_filter(tp, &f->res); 296 call_rcu(&f->rcu, fw_delete_filter); 297 298 *arg = (unsigned long)fnew; 299 return err; 300 } 301 302 if (!handle) 303 return -EINVAL; 304 305 if (!head->mask_set) { 306 head->mask = 0xFFFFFFFF; 307 if (tb[TCA_FW_MASK]) 308 head->mask = nla_get_u32(tb[TCA_FW_MASK]); 309 head->mask_set = true; 310 } 311 312 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); 313 if (f == NULL) 314 return -ENOBUFS; 315 316 tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); 317 f->id = handle; 318 f->tp = tp; 319 320 err = fw_change_attrs(net, tp, f, tb, tca, base, ovr); 321 if (err < 0) 322 goto errout; 323 324 RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]); 325 rcu_assign_pointer(head->ht[fw_hash(handle)], f); 326 327 *arg = (unsigned long)f; 328 return 0; 329 330 errout: 331 kfree(f); 332 return err; 333 } 334 335 static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) 336 { 337 struct fw_head *head = rtnl_dereference(tp->root); 338 int h; 339 340 if (head == NULL) 341 arg->stop = 1; 342 343 if (arg->stop) 344 return; 345 346 for (h = 0; h < HTSIZE; h++) { 347 struct fw_filter *f; 348 349 for (f = rtnl_dereference(head->ht[h]); f; 350 f = rtnl_dereference(f->next)) { 351 if (arg->count < arg->skip) { 352 arg->count++; 353 continue; 354 } 355 if (arg->fn(tp, (unsigned long)f, arg) < 0) { 356 arg->stop = 1; 357 return; 358 } 359 arg->count++; 360 } 361 } 362 } 363 364 static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, 365 struct sk_buff *skb, struct tcmsg *t) 366 { 367 struct fw_head *head = rtnl_dereference(tp->root); 368 struct fw_filter *f = (struct fw_filter *)fh; 369 struct nlattr *nest; 370 371 if (f == NULL) 372 return skb->len; 373 374 t->tcm_handle = f->id; 375 376 if (!f->res.classid && !tcf_exts_is_available(&f->exts)) 377 return skb->len; 378 379 nest = nla_nest_start(skb, TCA_OPTIONS); 380 if (nest == NULL) 381 goto nla_put_failure; 382 383 if (f->res.classid && 384 nla_put_u32(skb, TCA_FW_CLASSID, f->res.classid)) 385 goto nla_put_failure; 386 #ifdef CONFIG_NET_CLS_IND 387 if (f->ifindex) { 388 struct net_device *dev; 389 dev = __dev_get_by_index(net, f->ifindex); 390 if (dev && nla_put_string(skb, TCA_FW_INDEV, dev->name)) 391 goto nla_put_failure; 392 } 393 #endif /* CONFIG_NET_CLS_IND */ 394 if (head->mask != 0xFFFFFFFF && 395 nla_put_u32(skb, TCA_FW_MASK, head->mask)) 396 goto nla_put_failure; 397 398 if (tcf_exts_dump(skb, &f->exts) < 0) 399 goto nla_put_failure; 400 401 nla_nest_end(skb, nest); 402 403 if (tcf_exts_dump_stats(skb, &f->exts) < 0) 404 goto nla_put_failure; 405 406 return skb->len; 407 408 nla_put_failure: 409 nla_nest_cancel(skb, nest); 410 return -1; 411 } 412 413 static struct tcf_proto_ops cls_fw_ops __read_mostly = { 414 .kind = "fw", 415 .classify = fw_classify, 416 .init = fw_init, 417 .destroy = fw_destroy, 418 .get = fw_get, 419 .change = fw_change, 420 .delete = fw_delete, 421 .walk = fw_walk, 422 .dump = fw_dump, 423 .owner = THIS_MODULE, 424 }; 425 426 static int __init init_fw(void) 427 { 428 return register_tcf_proto_ops(&cls_fw_ops); 429 } 430 431 static void __exit exit_fw(void) 432 { 433 unregister_tcf_proto_ops(&cls_fw_ops); 434 } 435 436 module_init(init_fw) 437 module_exit(exit_fw) 438 MODULE_LICENSE("GPL"); 439