]>
Commit | Line | Data |
---|---|---|
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 <[email protected]> | |
10 | */ | |
11 | ||
12 | #include <linux/config.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/types.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/sched.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/rtnetlink.h> | |
21 | #include <linux/skbuff.h> | |
22 | #include <net/act_api.h> | |
23 | #include <net/pkt_cls.h> | |
24 | ||
25 | struct basic_head | |
26 | { | |
27 | u32 hgenerator; | |
28 | struct list_head flist; | |
29 | }; | |
30 | ||
31 | struct basic_filter | |
32 | { | |
33 | u32 handle; | |
34 | struct tcf_exts exts; | |
35 | struct tcf_ematch_tree ematches; | |
36 | struct tcf_result res; | |
37 | struct list_head link; | |
38 | }; | |
39 | ||
40 | static struct tcf_ext_map basic_ext_map = { | |
41 | .action = TCA_BASIC_ACT, | |
42 | .police = TCA_BASIC_POLICE | |
43 | }; | |
44 | ||
45 | static int basic_classify(struct sk_buff *skb, struct tcf_proto *tp, | |
46 | struct tcf_result *res) | |
47 | { | |
48 | int r; | |
49 | struct basic_head *head = (struct basic_head *) tp->root; | |
50 | struct basic_filter *f; | |
51 | ||
52 | list_for_each_entry(f, &head->flist, link) { | |
53 | if (!tcf_em_tree_match(skb, &f->ematches, NULL)) | |
54 | continue; | |
55 | *res = f->res; | |
56 | r = tcf_exts_exec(skb, &f->exts, res); | |
57 | if (r < 0) | |
58 | continue; | |
59 | return r; | |
60 | } | |
61 | return -1; | |
62 | } | |
63 | ||
64 | static unsigned long basic_get(struct tcf_proto *tp, u32 handle) | |
65 | { | |
66 | unsigned long l = 0UL; | |
67 | struct basic_head *head = (struct basic_head *) tp->root; | |
68 | struct basic_filter *f; | |
69 | ||
70 | if (head == NULL) | |
71 | return 0UL; | |
72 | ||
73 | list_for_each_entry(f, &head->flist, link) | |
74 | if (f->handle == handle) | |
75 | l = (unsigned long) f; | |
76 | ||
77 | return l; | |
78 | } | |
79 | ||
80 | static void basic_put(struct tcf_proto *tp, unsigned long f) | |
81 | { | |
82 | } | |
83 | ||
84 | static int basic_init(struct tcf_proto *tp) | |
85 | { | |
86 | return 0; | |
87 | } | |
88 | ||
89 | static inline void basic_delete_filter(struct tcf_proto *tp, | |
90 | struct basic_filter *f) | |
91 | { | |
92 | tcf_unbind_filter(tp, &f->res); | |
93 | tcf_exts_destroy(tp, &f->exts); | |
94 | tcf_em_tree_destroy(tp, &f->ematches); | |
95 | kfree(f); | |
96 | } | |
97 | ||
98 | static void basic_destroy(struct tcf_proto *tp) | |
99 | { | |
100 | struct basic_head *head = (struct basic_head *) xchg(&tp->root, NULL); | |
101 | struct basic_filter *f, *n; | |
102 | ||
103 | list_for_each_entry_safe(f, n, &head->flist, link) { | |
104 | list_del(&f->link); | |
105 | basic_delete_filter(tp, f); | |
106 | } | |
107 | } | |
108 | ||
109 | static int basic_delete(struct tcf_proto *tp, unsigned long arg) | |
110 | { | |
111 | struct basic_head *head = (struct basic_head *) tp->root; | |
112 | struct basic_filter *t, *f = (struct basic_filter *) arg; | |
113 | ||
114 | list_for_each_entry(t, &head->flist, link) | |
115 | if (t == f) { | |
116 | tcf_tree_lock(tp); | |
117 | list_del(&t->link); | |
118 | tcf_tree_unlock(tp); | |
119 | basic_delete_filter(tp, t); | |
120 | return 0; | |
121 | } | |
122 | ||
123 | return -ENOENT; | |
124 | } | |
125 | ||
126 | static inline int basic_set_parms(struct tcf_proto *tp, struct basic_filter *f, | |
127 | unsigned long base, struct rtattr **tb, | |
128 | struct rtattr *est) | |
129 | { | |
130 | int err = -EINVAL; | |
131 | struct tcf_exts e; | |
132 | struct tcf_ematch_tree t; | |
133 | ||
134 | if (tb[TCA_BASIC_CLASSID-1]) | |
135 | if (RTA_PAYLOAD(tb[TCA_BASIC_CLASSID-1]) < sizeof(u32)) | |
136 | return err; | |
137 | ||
138 | err = tcf_exts_validate(tp, tb, est, &e, &basic_ext_map); | |
139 | if (err < 0) | |
140 | return err; | |
141 | ||
142 | err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES-1], &t); | |
143 | if (err < 0) | |
144 | goto errout; | |
145 | ||
146 | if (tb[TCA_BASIC_CLASSID-1]) { | |
147 | f->res.classid = *(u32*)RTA_DATA(tb[TCA_BASIC_CLASSID-1]); | |
148 | tcf_bind_filter(tp, &f->res, base); | |
149 | } | |
150 | ||
151 | tcf_exts_change(tp, &f->exts, &e); | |
152 | tcf_em_tree_change(tp, &f->ematches, &t); | |
153 | ||
154 | return 0; | |
155 | errout: | |
156 | tcf_exts_destroy(tp, &e); | |
157 | return err; | |
158 | } | |
159 | ||
160 | static int basic_change(struct tcf_proto *tp, unsigned long base, u32 handle, | |
161 | struct rtattr **tca, unsigned long *arg) | |
162 | { | |
163 | int err = -EINVAL; | |
164 | struct basic_head *head = (struct basic_head *) tp->root; | |
165 | struct rtattr *tb[TCA_BASIC_MAX]; | |
166 | struct basic_filter *f = (struct basic_filter *) *arg; | |
167 | ||
168 | if (tca[TCA_OPTIONS-1] == NULL) | |
169 | return -EINVAL; | |
170 | ||
171 | if (rtattr_parse_nested(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS-1]) < 0) | |
172 | return -EINVAL; | |
173 | ||
174 | if (f != NULL) { | |
175 | if (handle && f->handle != handle) | |
176 | return -EINVAL; | |
177 | return basic_set_parms(tp, f, base, tb, tca[TCA_RATE-1]); | |
178 | } | |
179 | ||
180 | err = -ENOBUFS; | |
181 | if (head == NULL) { | |
182 | head = kmalloc(sizeof(*head), GFP_KERNEL); | |
183 | if (head == NULL) | |
184 | goto errout; | |
185 | ||
186 | memset(head, 0, sizeof(*head)); | |
187 | INIT_LIST_HEAD(&head->flist); | |
188 | tp->root = head; | |
189 | } | |
190 | ||
191 | f = kmalloc(sizeof(*f), GFP_KERNEL); | |
192 | if (f == NULL) | |
193 | goto errout; | |
194 | memset(f, 0, sizeof(*f)); | |
195 | ||
196 | err = -EINVAL; | |
197 | if (handle) | |
198 | f->handle = handle; | |
199 | else { | |
200 | int i = 0x80000000; | |
201 | do { | |
202 | if (++head->hgenerator == 0x7FFFFFFF) | |
203 | head->hgenerator = 1; | |
204 | } while (--i > 0 && basic_get(tp, head->hgenerator)); | |
205 | ||
206 | if (i <= 0) { | |
207 | printk(KERN_ERR "Insufficient number of handles\n"); | |
208 | goto errout; | |
209 | } | |
210 | ||
211 | f->handle = head->hgenerator; | |
212 | } | |
213 | ||
214 | err = basic_set_parms(tp, f, base, tb, tca[TCA_RATE-1]); | |
215 | if (err < 0) | |
216 | goto errout; | |
217 | ||
218 | tcf_tree_lock(tp); | |
219 | list_add(&f->link, &head->flist); | |
220 | tcf_tree_unlock(tp); | |
221 | *arg = (unsigned long) f; | |
222 | ||
223 | return 0; | |
224 | errout: | |
225 | if (*arg == 0UL && f) | |
226 | kfree(f); | |
227 | ||
228 | return err; | |
229 | } | |
230 | ||
231 | static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg) | |
232 | { | |
233 | struct basic_head *head = (struct basic_head *) tp->root; | |
234 | struct basic_filter *f; | |
235 | ||
236 | list_for_each_entry(f, &head->flist, link) { | |
237 | if (arg->count < arg->skip) | |
238 | goto skip; | |
239 | ||
240 | if (arg->fn(tp, (unsigned long) f, arg) < 0) { | |
241 | arg->stop = 1; | |
242 | break; | |
243 | } | |
244 | skip: | |
245 | arg->count++; | |
246 | } | |
247 | } | |
248 | ||
249 | static int basic_dump(struct tcf_proto *tp, unsigned long fh, | |
250 | struct sk_buff *skb, struct tcmsg *t) | |
251 | { | |
252 | struct basic_filter *f = (struct basic_filter *) fh; | |
253 | unsigned char *b = skb->tail; | |
254 | struct rtattr *rta; | |
255 | ||
256 | if (f == NULL) | |
257 | return skb->len; | |
258 | ||
259 | t->tcm_handle = f->handle; | |
260 | ||
261 | rta = (struct rtattr *) b; | |
262 | RTA_PUT(skb, TCA_OPTIONS, 0, NULL); | |
263 | ||
e1e284a4 TG |
264 | if (f->res.classid) |
265 | RTA_PUT(skb, TCA_BASIC_CLASSID, sizeof(u32), &f->res.classid); | |
266 | ||
1da177e4 LT |
267 | if (tcf_exts_dump(skb, &f->exts, &basic_ext_map) < 0 || |
268 | tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0) | |
269 | goto rtattr_failure; | |
270 | ||
271 | rta->rta_len = (skb->tail - b); | |
272 | return skb->len; | |
273 | ||
274 | rtattr_failure: | |
275 | skb_trim(skb, b - skb->data); | |
276 | return -1; | |
277 | } | |
278 | ||
279 | static struct tcf_proto_ops cls_basic_ops = { | |
280 | .kind = "basic", | |
281 | .classify = basic_classify, | |
282 | .init = basic_init, | |
283 | .destroy = basic_destroy, | |
284 | .get = basic_get, | |
285 | .put = basic_put, | |
286 | .change = basic_change, | |
287 | .delete = basic_delete, | |
288 | .walk = basic_walk, | |
289 | .dump = basic_dump, | |
290 | .owner = THIS_MODULE, | |
291 | }; | |
292 | ||
293 | static int __init init_basic(void) | |
294 | { | |
295 | return register_tcf_proto_ops(&cls_basic_ops); | |
296 | } | |
297 | ||
298 | static void __exit exit_basic(void) | |
299 | { | |
300 | unregister_tcf_proto_ops(&cls_basic_ops); | |
301 | } | |
302 | ||
303 | module_init(init_basic) | |
304 | module_exit(exit_basic) | |
305 | MODULE_LICENSE("GPL"); | |
306 |