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