9p: Remove INET dependency
[linux-block.git] / net / sched / cls_basic.c
CommitLineData
2874c5fd 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4
LT
2/*
3 * net/sched/cls_basic.c Basic Packet Classifier.
4 *
1da177e4
LT
5 * Authors: Thomas Graf <tgraf@suug.ch>
6 */
7
1da177e4 8#include <linux/module.h>
5a0e3ad6 9#include <linux/slab.h>
1da177e4
LT
10#include <linux/types.h>
11#include <linux/kernel.h>
1da177e4 12#include <linux/string.h>
1da177e4
LT
13#include <linux/errno.h>
14#include <linux/rtnetlink.h>
15#include <linux/skbuff.h>
1d8134fe 16#include <linux/idr.h>
5954894b 17#include <linux/percpu.h>
dc5fc579 18#include <net/netlink.h>
1da177e4
LT
19#include <net/act_api.h>
20#include <net/pkt_cls.h>
9f3101dc 21#include <net/tc_wrapper.h>
1da177e4 22
cc7ec456 23struct basic_head {
1da177e4 24 struct list_head flist;
1d8134fe 25 struct idr handle_idr;
9888faef 26 struct rcu_head rcu;
1da177e4
LT
27};
28
cc7ec456 29struct basic_filter {
1da177e4
LT
30 u32 handle;
31 struct tcf_exts exts;
32 struct tcf_ematch_tree ematches;
33 struct tcf_result res;
9888faef 34 struct tcf_proto *tp;
1da177e4 35 struct list_head link;
5954894b 36 struct tc_basic_pcnt __percpu *pf;
aaa908ff 37 struct rcu_work rwork;
1da177e4
LT
38};
39
9f3101dc
PT
40TC_INDIRECT_SCOPE int basic_classify(struct sk_buff *skb,
41 const struct tcf_proto *tp,
42 struct tcf_result *res)
1da177e4
LT
43{
44 int r;
9888faef 45 struct basic_head *head = rcu_dereference_bh(tp->root);
1da177e4
LT
46 struct basic_filter *f;
47
9888faef 48 list_for_each_entry_rcu(f, &head->flist, link) {
5954894b 49 __this_cpu_inc(f->pf->rcnt);
1da177e4
LT
50 if (!tcf_em_tree_match(skb, &f->ematches, NULL))
51 continue;
5954894b 52 __this_cpu_inc(f->pf->rhit);
1da177e4
LT
53 *res = f->res;
54 r = tcf_exts_exec(skb, &f->exts, res);
55 if (r < 0)
56 continue;
57 return r;
58 }
59 return -1;
60}
61
8113c095 62static void *basic_get(struct tcf_proto *tp, u32 handle)
1da177e4 63{
9888faef 64 struct basic_head *head = rtnl_dereference(tp->root);
1da177e4
LT
65 struct basic_filter *f;
66
1c1bc6bd
DB
67 list_for_each_entry(f, &head->flist, link) {
68 if (f->handle == handle) {
8113c095 69 return f;
1c1bc6bd
DB
70 }
71 }
1da177e4 72
8113c095 73 return NULL;
1da177e4
LT
74}
75
1da177e4
LT
76static int basic_init(struct tcf_proto *tp)
77{
d3fa76ee
PM
78 struct basic_head *head;
79
80 head = kzalloc(sizeof(*head), GFP_KERNEL);
81 if (head == NULL)
82 return -ENOBUFS;
83 INIT_LIST_HEAD(&head->flist);
1d8134fe 84 idr_init(&head->handle_idr);
9888faef 85 rcu_assign_pointer(tp->root, head);
1da177e4
LT
86 return 0;
87}
88
0b2a5989
CW
89static void __basic_delete_filter(struct basic_filter *f)
90{
91 tcf_exts_destroy(&f->exts);
92 tcf_em_tree_destroy(&f->ematches);
93 tcf_exts_put_net(&f->exts);
5954894b 94 free_percpu(f->pf);
0b2a5989
CW
95 kfree(f);
96}
97
c96a4838 98static void basic_delete_filter_work(struct work_struct *work)
1da177e4 99{
aaa908ff
CW
100 struct basic_filter *f = container_of(to_rcu_work(work),
101 struct basic_filter,
102 rwork);
c96a4838 103 rtnl_lock();
0b2a5989 104 __basic_delete_filter(f);
c96a4838 105 rtnl_unlock();
1da177e4
LT
106}
107
12db03b6
VB
108static void basic_destroy(struct tcf_proto *tp, bool rtnl_held,
109 struct netlink_ext_ack *extack)
1da177e4 110{
9888faef 111 struct basic_head *head = rtnl_dereference(tp->root);
1da177e4 112 struct basic_filter *f, *n;
10297b99 113
1da177e4 114 list_for_each_entry_safe(f, n, &head->flist, link) {
9888faef 115 list_del_rcu(&f->link);
18cdb37e 116 tcf_unbind_filter(tp, &f->res);
9c160941 117 idr_remove(&head->handle_idr, f->handle);
0b2a5989 118 if (tcf_exts_get_net(&f->exts))
aaa908ff 119 tcf_queue_work(&f->rwork, basic_delete_filter_work);
0b2a5989
CW
120 else
121 __basic_delete_filter(f);
1da177e4 122 }
1d8134fe 123 idr_destroy(&head->handle_idr);
9888faef 124 kfree_rcu(head, rcu);
1da177e4
LT
125}
126
571acf21 127static int basic_delete(struct tcf_proto *tp, void *arg, bool *last,
12db03b6 128 bool rtnl_held, struct netlink_ext_ack *extack)
1da177e4 129{
763dbf63 130 struct basic_head *head = rtnl_dereference(tp->root);
8113c095 131 struct basic_filter *f = arg;
1da177e4 132
e4386456
JP
133 list_del_rcu(&f->link);
134 tcf_unbind_filter(tp, &f->res);
9c160941 135 idr_remove(&head->handle_idr, f->handle);
0b2a5989 136 tcf_exts_get_net(&f->exts);
aaa908ff 137 tcf_queue_work(&f->rwork, basic_delete_filter_work);
763dbf63 138 *last = list_empty(&head->flist);
e4386456 139 return 0;
1da177e4
LT
140}
141
6fa8c014
PM
142static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
143 [TCA_BASIC_CLASSID] = { .type = NLA_U32 },
144 [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED },
145};
146
c1b52739
BL
147static int basic_set_parms(struct net *net, struct tcf_proto *tp,
148 struct basic_filter *f, unsigned long base,
149 struct nlattr **tb,
695176bf 150 struct nlattr *est, u32 flags,
50a56190 151 struct netlink_ext_ack *extack)
1da177e4 152{
6459082a 153 int err;
1da177e4 154
695176bf 155 err = tcf_exts_validate(net, tp, tb, est, &f->exts, flags, extack);
1da177e4
LT
156 if (err < 0)
157 return err;
158
4ebc1e3c 159 err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches);
1da177e4 160 if (err < 0)
ff1f8ca0 161 return err;
1da177e4 162
add93b61 163 if (tb[TCA_BASIC_CLASSID]) {
1587bac4 164 f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
1da177e4
LT
165 tcf_bind_filter(tp, &f->res, base);
166 }
167
9888faef 168 f->tp = tp;
1da177e4 169 return 0;
1da177e4
LT
170}
171
c1b52739 172static int basic_change(struct net *net, struct sk_buff *in_skb,
af4c6641 173 struct tcf_proto *tp, unsigned long base, u32 handle,
695176bf
CW
174 struct nlattr **tca, void **arg,
175 u32 flags, struct netlink_ext_ack *extack)
1da177e4 176{
cee63723 177 int err;
9888faef 178 struct basic_head *head = rtnl_dereference(tp->root);
add93b61 179 struct nlattr *tb[TCA_BASIC_MAX + 1];
9888faef
JF
180 struct basic_filter *fold = (struct basic_filter *) *arg;
181 struct basic_filter *fnew;
1da177e4 182
add93b61 183 if (tca[TCA_OPTIONS] == NULL)
1da177e4
LT
184 return -EINVAL;
185
8cb08174
JB
186 err = nla_parse_nested_deprecated(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS],
187 basic_policy, NULL);
cee63723
PM
188 if (err < 0)
189 return err;
1da177e4 190
9888faef
JF
191 if (fold != NULL) {
192 if (handle && fold->handle != handle)
1da177e4 193 return -EINVAL;
1da177e4
LT
194 }
195
9888faef 196 fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
bd42b788
JP
197 if (!fnew)
198 return -ENOBUFS;
1da177e4 199
14215108 200 err = tcf_exts_init(&fnew->exts, net, TCA_BASIC_ACT, TCA_BASIC_POLICE);
b9a24bb7
WC
201 if (err < 0)
202 goto errout;
203
05af0ebb
MW
204 if (!handle) {
205 handle = 1;
206 err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
207 INT_MAX, GFP_KERNEL);
208 } else if (!fold) {
209 err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
210 handle, GFP_KERNEL);
1da177e4 211 }
05af0ebb
MW
212 if (err)
213 goto errout;
214 fnew->handle = handle;
5954894b
CW
215 fnew->pf = alloc_percpu(struct tc_basic_pcnt);
216 if (!fnew->pf) {
217 err = -ENOMEM;
218 goto errout;
219 }
1da177e4 220
695176bf 221 err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], flags,
50a56190 222 extack);
1d8134fe
CW
223 if (err < 0) {
224 if (!fold)
9c160941 225 idr_remove(&head->handle_idr, fnew->handle);
1da177e4 226 goto errout;
1d8134fe 227 }
1da177e4 228
8113c095 229 *arg = fnew;
9888faef
JF
230
231 if (fold) {
234a4624 232 idr_replace(&head->handle_idr, fnew, fnew->handle);
9888faef 233 list_replace_rcu(&fold->link, &fnew->link);
18cdb37e 234 tcf_unbind_filter(tp, &fold->res);
0b2a5989 235 tcf_exts_get_net(&fold->exts);
aaa908ff 236 tcf_queue_work(&fold->rwork, basic_delete_filter_work);
9888faef
JF
237 } else {
238 list_add_rcu(&fnew->link, &head->flist);
239 }
1da177e4
LT
240
241 return 0;
242errout:
5954894b 243 free_percpu(fnew->pf);
b9a24bb7 244 tcf_exts_destroy(&fnew->exts);
9888faef 245 kfree(fnew);
1da177e4
LT
246 return err;
247}
248
12db03b6
VB
249static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg,
250 bool rtnl_held)
1da177e4 251{
9888faef 252 struct basic_head *head = rtnl_dereference(tp->root);
1da177e4
LT
253 struct basic_filter *f;
254
255 list_for_each_entry(f, &head->flist, link) {
5508ff7c 256 if (!tc_cls_stats_dump(tp, arg, f))
1da177e4 257 break;
1da177e4
LT
258 }
259}
260
2e24cd75
CW
261static void basic_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
262 unsigned long base)
07d79fc7
CW
263{
264 struct basic_filter *f = fh;
265
cc9039a1 266 tc_cls_bind_class(classid, cl, q, &f->res, base);
07d79fc7
CW
267}
268
8113c095 269static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh,
12db03b6 270 struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
1da177e4 271{
5954894b 272 struct tc_basic_pcnt gpf = {};
8113c095 273 struct basic_filter *f = fh;
4b3550ef 274 struct nlattr *nest;
5954894b 275 int cpu;
1da177e4
LT
276
277 if (f == NULL)
278 return skb->len;
279
280 t->tcm_handle = f->handle;
281
ae0be8de 282 nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
4b3550ef
PM
283 if (nest == NULL)
284 goto nla_put_failure;
1da177e4 285
1b34ec43
DM
286 if (f->res.classid &&
287 nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
288 goto nla_put_failure;
e1e284a4 289
5954894b
CW
290 for_each_possible_cpu(cpu) {
291 struct tc_basic_pcnt *pf = per_cpu_ptr(f->pf, cpu);
292
293 gpf.rcnt += pf->rcnt;
294 gpf.rhit += pf->rhit;
295 }
296
297 if (nla_put_64bit(skb, TCA_BASIC_PCNT,
298 sizeof(struct tc_basic_pcnt),
299 &gpf, TCA_BASIC_PAD))
300 goto nla_put_failure;
301
5da57f42 302 if (tcf_exts_dump(skb, &f->exts) < 0 ||
1da177e4 303 tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
add93b61 304 goto nla_put_failure;
1da177e4 305
4b3550ef 306 nla_nest_end(skb, nest);
4c46ee52 307
5da57f42 308 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
4c46ee52 309 goto nla_put_failure;
310
1da177e4
LT
311 return skb->len;
312
add93b61 313nla_put_failure:
4b3550ef 314 nla_nest_cancel(skb, nest);
1da177e4
LT
315 return -1;
316}
317
2eb9d75c 318static struct tcf_proto_ops cls_basic_ops __read_mostly = {
1da177e4
LT
319 .kind = "basic",
320 .classify = basic_classify,
321 .init = basic_init,
322 .destroy = basic_destroy,
323 .get = basic_get,
1da177e4
LT
324 .change = basic_change,
325 .delete = basic_delete,
326 .walk = basic_walk,
327 .dump = basic_dump,
07d79fc7 328 .bind_class = basic_bind_class,
1da177e4
LT
329 .owner = THIS_MODULE,
330};
331
332static int __init init_basic(void)
333{
334 return register_tcf_proto_ops(&cls_basic_ops);
335}
336
10297b99 337static void __exit exit_basic(void)
1da177e4
LT
338{
339 unregister_tcf_proto_ops(&cls_basic_ops);
340}
341
342module_init(init_basic)
343module_exit(exit_basic)
344MODULE_LICENSE("GPL");