1 /* 2 * net/sched/cls_basic.c Basic Packet Classifier. 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: Thomas Graf <tgraf@suug.ch> 10 */ 11 12 #include <linux/module.h> 13 #include <linux/slab.h> 14 #include <linux/types.h> 15 #include <linux/kernel.h> 16 #include <linux/string.h> 17 #include <linux/errno.h> 18 #include <linux/rtnetlink.h> 19 #include <linux/skbuff.h> 20 #include <linux/idr.h> 21 #include <net/netlink.h> 22 #include <net/act_api.h> 23 #include <net/pkt_cls.h> 24 25 struct basic_head { 26 struct list_head flist; 27 struct idr handle_idr; 28 struct rcu_head rcu; 29 }; 30 31 struct basic_filter { 32 u32 handle; 33 struct tcf_exts exts; 34 struct tcf_ematch_tree ematches; 35 struct tcf_result res; 36 struct tcf_proto *tp; 37 struct list_head link; 38 union { 39 struct work_struct work; 40 struct rcu_head rcu; 41 }; 42 }; 43 44 static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp, 45 struct tcf_result *res) 46 { 47 int r; 48 struct basic_head *head = rcu_dereference_bh(tp->root); 49 struct basic_filter *f; 50 51 list_for_each_entry_rcu(f, &head->flist, link) { 52 if (!tcf_em_tree_match(skb, &f->ematches, NULL)) 53 continue; 54 *res = f->res; 55 r = tcf_exts_exec(skb, &f->exts, res); 56 if (r < 0) 57 continue; 58 return r; 59 } 60 return -1; 61 } 62 63 static void *basic_get(struct tcf_proto *tp, u32 handle) 64 { 65 struct basic_head *head = rtnl_dereference(tp->root); 66 struct basic_filter *f; 67 68 list_for_each_entry(f, &head->flist, link) { 69 if (f->handle == handle) { 70 return f; 71 } 72 } 73 74 return NULL; 75 } 76 77 static int basic_init(struct tcf_proto *tp) 78 { 79 struct basic_head *head; 80 81 head = kzalloc(sizeof(*head), GFP_KERNEL); 82 if (head == NULL) 83 return -ENOBUFS; 84 INIT_LIST_HEAD(&head->flist); 85 idr_init(&head->handle_idr); 86 rcu_assign_pointer(tp->root, head); 87 return 0; 88 } 89 90 static void __basic_delete_filter(struct basic_filter *f) 91 { 92 tcf_exts_destroy(&f->exts); 93 tcf_em_tree_destroy(&f->ematches); 94 tcf_exts_put_net(&f->exts); 95 kfree(f); 96 } 97 98 static void basic_delete_filter_work(struct work_struct *work) 99 { 100 struct basic_filter *f = container_of(work, struct basic_filter, work); 101 102 rtnl_lock(); 103 __basic_delete_filter(f); 104 rtnl_unlock(); 105 } 106 107 static void basic_delete_filter(struct rcu_head *head) 108 { 109 struct basic_filter *f = container_of(head, struct basic_filter, rcu); 110 111 INIT_WORK(&f->work, basic_delete_filter_work); 112 tcf_queue_work(&f->work); 113 } 114 115 static void basic_destroy(struct tcf_proto *tp) 116 { 117 struct basic_head *head = rtnl_dereference(tp->root); 118 struct basic_filter *f, *n; 119 120 list_for_each_entry_safe(f, n, &head->flist, link) { 121 list_del_rcu(&f->link); 122 tcf_unbind_filter(tp, &f->res); 123 idr_remove_ext(&head->handle_idr, f->handle); 124 if (tcf_exts_get_net(&f->exts)) 125 call_rcu(&f->rcu, basic_delete_filter); 126 else 127 __basic_delete_filter(f); 128 } 129 idr_destroy(&head->handle_idr); 130 kfree_rcu(head, rcu); 131 } 132 133 static int basic_delete(struct tcf_proto *tp, void *arg, bool *last) 134 { 135 struct basic_head *head = rtnl_dereference(tp->root); 136 struct basic_filter *f = arg; 137 138 list_del_rcu(&f->link); 139 tcf_unbind_filter(tp, &f->res); 140 idr_remove_ext(&head->handle_idr, f->handle); 141 tcf_exts_get_net(&f->exts); 142 call_rcu(&f->rcu, basic_delete_filter); 143 *last = list_empty(&head->flist); 144 return 0; 145 } 146 147 static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = { 148 [TCA_BASIC_CLASSID] = { .type = NLA_U32 }, 149 [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED }, 150 }; 151 152 static int basic_set_parms(struct net *net, struct tcf_proto *tp, 153 struct basic_filter *f, unsigned long base, 154 struct nlattr **tb, 155 struct nlattr *est, bool ovr) 156 { 157 int err; 158 159 err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr); 160 if (err < 0) 161 return err; 162 163 err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches); 164 if (err < 0) 165 return err; 166 167 if (tb[TCA_BASIC_CLASSID]) { 168 f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]); 169 tcf_bind_filter(tp, &f->res, base); 170 } 171 172 f->tp = tp; 173 return 0; 174 } 175 176 static int basic_change(struct net *net, struct sk_buff *in_skb, 177 struct tcf_proto *tp, unsigned long base, u32 handle, 178 struct nlattr **tca, void **arg, bool ovr) 179 { 180 int err; 181 struct basic_head *head = rtnl_dereference(tp->root); 182 struct nlattr *tb[TCA_BASIC_MAX + 1]; 183 struct basic_filter *fold = (struct basic_filter *) *arg; 184 struct basic_filter *fnew; 185 unsigned long idr_index; 186 187 if (tca[TCA_OPTIONS] == NULL) 188 return -EINVAL; 189 190 err = nla_parse_nested(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS], 191 basic_policy, NULL); 192 if (err < 0) 193 return err; 194 195 if (fold != NULL) { 196 if (handle && fold->handle != handle) 197 return -EINVAL; 198 } 199 200 fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 201 if (!fnew) 202 return -ENOBUFS; 203 204 err = tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE); 205 if (err < 0) 206 goto errout; 207 208 if (handle) { 209 fnew->handle = handle; 210 if (!fold) { 211 err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index, 212 handle, handle + 1, GFP_KERNEL); 213 if (err) 214 goto errout; 215 } 216 } else { 217 err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index, 218 1, 0x7FFFFFFF, GFP_KERNEL); 219 if (err) 220 goto errout; 221 fnew->handle = idr_index; 222 } 223 224 err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr); 225 if (err < 0) { 226 if (!fold) 227 idr_remove_ext(&head->handle_idr, fnew->handle); 228 goto errout; 229 } 230 231 *arg = fnew; 232 233 if (fold) { 234 idr_replace_ext(&head->handle_idr, fnew, fnew->handle); 235 list_replace_rcu(&fold->link, &fnew->link); 236 tcf_unbind_filter(tp, &fold->res); 237 tcf_exts_get_net(&fold->exts); 238 call_rcu(&fold->rcu, basic_delete_filter); 239 } else { 240 list_add_rcu(&fnew->link, &head->flist); 241 } 242 243 return 0; 244 errout: 245 tcf_exts_destroy(&fnew->exts); 246 kfree(fnew); 247 return err; 248 } 249 250 static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg) 251 { 252 struct basic_head *head = rtnl_dereference(tp->root); 253 struct basic_filter *f; 254 255 list_for_each_entry(f, &head->flist, link) { 256 if (arg->count < arg->skip) 257 goto skip; 258 259 if (arg->fn(tp, f, arg) < 0) { 260 arg->stop = 1; 261 break; 262 } 263 skip: 264 arg->count++; 265 } 266 } 267 268 static void basic_bind_class(void *fh, u32 classid, unsigned long cl) 269 { 270 struct basic_filter *f = fh; 271 272 if (f && f->res.classid == classid) 273 f->res.class = cl; 274 } 275 276 static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh, 277 struct sk_buff *skb, struct tcmsg *t) 278 { 279 struct basic_filter *f = fh; 280 struct nlattr *nest; 281 282 if (f == NULL) 283 return skb->len; 284 285 t->tcm_handle = f->handle; 286 287 nest = nla_nest_start(skb, TCA_OPTIONS); 288 if (nest == NULL) 289 goto nla_put_failure; 290 291 if (f->res.classid && 292 nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid)) 293 goto nla_put_failure; 294 295 if (tcf_exts_dump(skb, &f->exts) < 0 || 296 tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0) 297 goto nla_put_failure; 298 299 nla_nest_end(skb, nest); 300 301 if (tcf_exts_dump_stats(skb, &f->exts) < 0) 302 goto nla_put_failure; 303 304 return skb->len; 305 306 nla_put_failure: 307 nla_nest_cancel(skb, nest); 308 return -1; 309 } 310 311 static struct tcf_proto_ops cls_basic_ops __read_mostly = { 312 .kind = "basic", 313 .classify = basic_classify, 314 .init = basic_init, 315 .destroy = basic_destroy, 316 .get = basic_get, 317 .change = basic_change, 318 .delete = basic_delete, 319 .walk = basic_walk, 320 .dump = basic_dump, 321 .bind_class = basic_bind_class, 322 .owner = THIS_MODULE, 323 }; 324 325 static int __init init_basic(void) 326 { 327 return register_tcf_proto_ops(&cls_basic_ops); 328 } 329 330 static void __exit exit_basic(void) 331 { 332 unregister_tcf_proto_ops(&cls_basic_ops); 333 } 334 335 module_init(init_basic) 336 module_exit(exit_basic) 337 MODULE_LICENSE("GPL"); 338 339