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