]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/sched/cls_basic.c
pkt_sched: fq: do not accept silly TCA_FQ_QUANTUM
[mirror_ubuntu-bionic-kernel.git] / net / sched / cls_basic.c
CommitLineData
1da177e4
LT
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
1da177e4 12#include <linux/module.h>
5a0e3ad6 13#include <linux/slab.h>
1da177e4
LT
14#include <linux/types.h>
15#include <linux/kernel.h>
1da177e4 16#include <linux/string.h>
1da177e4
LT
17#include <linux/errno.h>
18#include <linux/rtnetlink.h>
19#include <linux/skbuff.h>
1d8134fe 20#include <linux/idr.h>
dc5fc579 21#include <net/netlink.h>
1da177e4
LT
22#include <net/act_api.h>
23#include <net/pkt_cls.h>
24
cc7ec456 25struct basic_head {
1da177e4 26 struct list_head flist;
1d8134fe 27 struct idr handle_idr;
9888faef 28 struct rcu_head rcu;
1da177e4
LT
29};
30
cc7ec456 31struct basic_filter {
1da177e4
LT
32 u32 handle;
33 struct tcf_exts exts;
34 struct tcf_ematch_tree ematches;
35 struct tcf_result res;
9888faef 36 struct tcf_proto *tp;
1da177e4 37 struct list_head link;
7afe8e4f 38 struct rcu_work rwork;
1da177e4
LT
39};
40
dc7f9f6e 41static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
1da177e4
LT
42 struct tcf_result *res)
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) {
1da177e4
LT
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
8113c095 60static void *basic_get(struct tcf_proto *tp, u32 handle)
1da177e4 61{
9888faef 62 struct basic_head *head = rtnl_dereference(tp->root);
1da177e4
LT
63 struct basic_filter *f;
64
1c1bc6bd
DB
65 list_for_each_entry(f, &head->flist, link) {
66 if (f->handle == handle) {
8113c095 67 return f;
1c1bc6bd
DB
68 }
69 }
1da177e4 70
8113c095 71 return NULL;
1da177e4
LT
72}
73
1da177e4
LT
74static int basic_init(struct tcf_proto *tp)
75{
d3fa76ee
PM
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);
1d8134fe 82 idr_init(&head->handle_idr);
9888faef 83 rcu_assign_pointer(tp->root, head);
1da177e4
LT
84 return 0;
85}
86
0b2a5989
CW
87static 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
c96a4838 95static void basic_delete_filter_work(struct work_struct *work)
1da177e4 96{
7afe8e4f
CW
97 struct basic_filter *f = container_of(to_rcu_work(work),
98 struct basic_filter,
99 rwork);
c96a4838 100 rtnl_lock();
0b2a5989 101 __basic_delete_filter(f);
c96a4838 102 rtnl_unlock();
1da177e4
LT
103}
104
763dbf63 105static void basic_destroy(struct tcf_proto *tp)
1da177e4 106{
9888faef 107 struct basic_head *head = rtnl_dereference(tp->root);
1da177e4 108 struct basic_filter *f, *n;
10297b99 109
1da177e4 110 list_for_each_entry_safe(f, n, &head->flist, link) {
9888faef 111 list_del_rcu(&f->link);
18cdb37e 112 tcf_unbind_filter(tp, &f->res);
1d8134fe 113 idr_remove_ext(&head->handle_idr, f->handle);
0b2a5989 114 if (tcf_exts_get_net(&f->exts))
7afe8e4f 115 tcf_queue_work(&f->rwork, basic_delete_filter_work);
0b2a5989
CW
116 else
117 __basic_delete_filter(f);
1da177e4 118 }
1d8134fe 119 idr_destroy(&head->handle_idr);
9888faef 120 kfree_rcu(head, rcu);
1da177e4
LT
121}
122
8113c095 123static int basic_delete(struct tcf_proto *tp, void *arg, bool *last)
1da177e4 124{
763dbf63 125 struct basic_head *head = rtnl_dereference(tp->root);
8113c095 126 struct basic_filter *f = arg;
1da177e4 127
e4386456
JP
128 list_del_rcu(&f->link);
129 tcf_unbind_filter(tp, &f->res);
1d8134fe 130 idr_remove_ext(&head->handle_idr, f->handle);
0b2a5989 131 tcf_exts_get_net(&f->exts);
7afe8e4f 132 tcf_queue_work(&f->rwork, basic_delete_filter_work);
763dbf63 133 *last = list_empty(&head->flist);
e4386456 134 return 0;
1da177e4
LT
135}
136
6fa8c014
PM
137static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
138 [TCA_BASIC_CLASSID] = { .type = NLA_U32 },
139 [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED },
140};
141
c1b52739
BL
142static int basic_set_parms(struct net *net, struct tcf_proto *tp,
143 struct basic_filter *f, unsigned long base,
144 struct nlattr **tb,
2f7ef2f8 145 struct nlattr *est, bool ovr)
1da177e4 146{
6459082a 147 int err;
1da177e4 148
ff1f8ca0 149 err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr);
1da177e4
LT
150 if (err < 0)
151 return err;
152
4ebc1e3c 153 err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches);
1da177e4 154 if (err < 0)
ff1f8ca0 155 return err;
1da177e4 156
add93b61 157 if (tb[TCA_BASIC_CLASSID]) {
1587bac4 158 f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
1da177e4
LT
159 tcf_bind_filter(tp, &f->res, base);
160 }
161
9888faef 162 f->tp = tp;
1da177e4 163 return 0;
1da177e4
LT
164}
165
c1b52739 166static int basic_change(struct net *net, struct sk_buff *in_skb,
af4c6641 167 struct tcf_proto *tp, unsigned long base, u32 handle,
8113c095 168 struct nlattr **tca, void **arg, bool ovr)
1da177e4 169{
cee63723 170 int err;
9888faef 171 struct basic_head *head = rtnl_dereference(tp->root);
add93b61 172 struct nlattr *tb[TCA_BASIC_MAX + 1];
9888faef
JF
173 struct basic_filter *fold = (struct basic_filter *) *arg;
174 struct basic_filter *fnew;
1d8134fe 175 unsigned long idr_index;
1da177e4 176
add93b61 177 if (tca[TCA_OPTIONS] == NULL)
1da177e4
LT
178 return -EINVAL;
179
6fa8c014 180 err = nla_parse_nested(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS],
fceb6435 181 basic_policy, NULL);
cee63723
PM
182 if (err < 0)
183 return err;
1da177e4 184
9888faef
JF
185 if (fold != NULL) {
186 if (handle && fold->handle != handle)
1da177e4 187 return -EINVAL;
1da177e4
LT
188 }
189
9888faef 190 fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
bd42b788
JP
191 if (!fnew)
192 return -ENOBUFS;
1da177e4 193
fb6ad63b 194 err = tcf_exts_init(&fnew->exts, net, TCA_BASIC_ACT, TCA_BASIC_POLICE);
b9a24bb7
WC
195 if (err < 0)
196 goto errout;
197
9888faef
JF
198 if (handle) {
199 fnew->handle = handle;
1d8134fe
CW
200 if (!fold) {
201 err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
202 handle, handle + 1, GFP_KERNEL);
203 if (err)
204 goto errout;
205 }
9888faef 206 } else {
1d8134fe
CW
207 err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index,
208 1, 0x7FFFFFFF, GFP_KERNEL);
209 if (err)
1da177e4 210 goto errout;
1d8134fe 211 fnew->handle = idr_index;
1da177e4
LT
212 }
213
9888faef 214 err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr);
1d8134fe
CW
215 if (err < 0) {
216 if (!fold)
217 idr_remove_ext(&head->handle_idr, fnew->handle);
1da177e4 218 goto errout;
1d8134fe 219 }
1da177e4 220
8113c095 221 *arg = fnew;
9888faef
JF
222
223 if (fold) {
1d8134fe 224 idr_replace_ext(&head->handle_idr, fnew, fnew->handle);
9888faef 225 list_replace_rcu(&fold->link, &fnew->link);
18cdb37e 226 tcf_unbind_filter(tp, &fold->res);
0b2a5989 227 tcf_exts_get_net(&fold->exts);
7afe8e4f 228 tcf_queue_work(&fold->rwork, basic_delete_filter_work);
9888faef
JF
229 } else {
230 list_add_rcu(&fnew->link, &head->flist);
231 }
1da177e4
LT
232
233 return 0;
234errout:
b9a24bb7 235 tcf_exts_destroy(&fnew->exts);
9888faef 236 kfree(fnew);
1da177e4
LT
237 return err;
238}
239
240static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg)
241{
9888faef 242 struct basic_head *head = rtnl_dereference(tp->root);
1da177e4
LT
243 struct basic_filter *f;
244
245 list_for_each_entry(f, &head->flist, link) {
246 if (arg->count < arg->skip)
247 goto skip;
248
8113c095 249 if (arg->fn(tp, f, arg) < 0) {
1da177e4
LT
250 arg->stop = 1;
251 break;
252 }
253skip:
254 arg->count++;
255 }
256}
257
07d79fc7
CW
258static void basic_bind_class(void *fh, u32 classid, unsigned long cl)
259{
260 struct basic_filter *f = fh;
261
262 if (f && f->res.classid == classid)
263 f->res.class = cl;
264}
265
8113c095 266static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh,
1da177e4
LT
267 struct sk_buff *skb, struct tcmsg *t)
268{
8113c095 269 struct basic_filter *f = fh;
4b3550ef 270 struct nlattr *nest;
1da177e4
LT
271
272 if (f == NULL)
273 return skb->len;
274
275 t->tcm_handle = f->handle;
276
4b3550ef
PM
277 nest = nla_nest_start(skb, TCA_OPTIONS);
278 if (nest == NULL)
279 goto nla_put_failure;
1da177e4 280
1b34ec43
DM
281 if (f->res.classid &&
282 nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
283 goto nla_put_failure;
e1e284a4 284
5da57f42 285 if (tcf_exts_dump(skb, &f->exts) < 0 ||
1da177e4 286 tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
add93b61 287 goto nla_put_failure;
1da177e4 288
4b3550ef 289 nla_nest_end(skb, nest);
4c46ee52 290
5da57f42 291 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
4c46ee52 292 goto nla_put_failure;
293
1da177e4
LT
294 return skb->len;
295
add93b61 296nla_put_failure:
4b3550ef 297 nla_nest_cancel(skb, nest);
1da177e4
LT
298 return -1;
299}
300
2eb9d75c 301static struct tcf_proto_ops cls_basic_ops __read_mostly = {
1da177e4
LT
302 .kind = "basic",
303 .classify = basic_classify,
304 .init = basic_init,
305 .destroy = basic_destroy,
306 .get = basic_get,
1da177e4
LT
307 .change = basic_change,
308 .delete = basic_delete,
309 .walk = basic_walk,
310 .dump = basic_dump,
07d79fc7 311 .bind_class = basic_bind_class,
1da177e4
LT
312 .owner = THIS_MODULE,
313};
314
315static int __init init_basic(void)
316{
317 return register_tcf_proto_ops(&cls_basic_ops);
318}
319
10297b99 320static void __exit exit_basic(void)
1da177e4
LT
321{
322 unregister_tcf_proto_ops(&cls_basic_ops);
323}
324
325module_init(init_basic)
326module_exit(exit_basic)
327MODULE_LICENSE("GPL");
328