Root/
1 | /* |
2 | * net/sched/cls_fw.c Classifier mapping ipchains' fwmark to traffic class. |
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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
10 | * |
11 | * Changes: |
12 | * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_walk off by one |
13 | * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_delete killed all the filter (and kernel). |
14 | * Alex <alex@pilotsoft.com> : 2004xxyy: Added Action extension |
15 | * |
16 | * JHS: We should remove the CONFIG_NET_CLS_IND from here |
17 | * eventually when the meta match extension is made available |
18 | * |
19 | */ |
20 | |
21 | #include <linux/module.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/types.h> |
24 | #include <linux/kernel.h> |
25 | #include <linux/string.h> |
26 | #include <linux/errno.h> |
27 | #include <linux/skbuff.h> |
28 | #include <net/netlink.h> |
29 | #include <net/act_api.h> |
30 | #include <net/pkt_cls.h> |
31 | |
32 | #define HTSIZE 256 |
33 | |
34 | struct fw_head { |
35 | u32 mask; |
36 | struct fw_filter *ht[HTSIZE]; |
37 | }; |
38 | |
39 | struct fw_filter { |
40 | struct fw_filter *next; |
41 | u32 id; |
42 | struct tcf_result res; |
43 | #ifdef CONFIG_NET_CLS_IND |
44 | int ifindex; |
45 | #endif /* CONFIG_NET_CLS_IND */ |
46 | struct tcf_exts exts; |
47 | }; |
48 | |
49 | static u32 fw_hash(u32 handle) |
50 | { |
51 | handle ^= (handle >> 16); |
52 | handle ^= (handle >> 8); |
53 | return handle % HTSIZE; |
54 | } |
55 | |
56 | static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
57 | struct tcf_result *res) |
58 | { |
59 | struct fw_head *head = tp->root; |
60 | struct fw_filter *f; |
61 | int r; |
62 | u32 id = skb->mark; |
63 | |
64 | if (head != NULL) { |
65 | id &= head->mask; |
66 | for (f = head->ht[fw_hash(id)]; f; f = f->next) { |
67 | if (f->id == id) { |
68 | *res = f->res; |
69 | #ifdef CONFIG_NET_CLS_IND |
70 | if (!tcf_match_indev(skb, f->ifindex)) |
71 | continue; |
72 | #endif /* CONFIG_NET_CLS_IND */ |
73 | r = tcf_exts_exec(skb, &f->exts, res); |
74 | if (r < 0) |
75 | continue; |
76 | |
77 | return r; |
78 | } |
79 | } |
80 | } else { |
81 | /* old method */ |
82 | if (id && (TC_H_MAJ(id) == 0 || |
83 | !(TC_H_MAJ(id ^ tp->q->handle)))) { |
84 | res->classid = id; |
85 | res->class = 0; |
86 | return 0; |
87 | } |
88 | } |
89 | |
90 | return -1; |
91 | } |
92 | |
93 | static unsigned long fw_get(struct tcf_proto *tp, u32 handle) |
94 | { |
95 | struct fw_head *head = tp->root; |
96 | struct fw_filter *f; |
97 | |
98 | if (head == NULL) |
99 | return 0; |
100 | |
101 | for (f = head->ht[fw_hash(handle)]; f; f = f->next) { |
102 | if (f->id == handle) |
103 | return (unsigned long)f; |
104 | } |
105 | return 0; |
106 | } |
107 | |
108 | static void fw_put(struct tcf_proto *tp, unsigned long f) |
109 | { |
110 | } |
111 | |
112 | static int fw_init(struct tcf_proto *tp) |
113 | { |
114 | return 0; |
115 | } |
116 | |
117 | static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f) |
118 | { |
119 | tcf_unbind_filter(tp, &f->res); |
120 | tcf_exts_destroy(tp, &f->exts); |
121 | kfree(f); |
122 | } |
123 | |
124 | static void fw_destroy(struct tcf_proto *tp) |
125 | { |
126 | struct fw_head *head = tp->root; |
127 | struct fw_filter *f; |
128 | int h; |
129 | |
130 | if (head == NULL) |
131 | return; |
132 | |
133 | for (h = 0; h < HTSIZE; h++) { |
134 | while ((f = head->ht[h]) != NULL) { |
135 | head->ht[h] = f->next; |
136 | fw_delete_filter(tp, f); |
137 | } |
138 | } |
139 | kfree(head); |
140 | } |
141 | |
142 | static int fw_delete(struct tcf_proto *tp, unsigned long arg) |
143 | { |
144 | struct fw_head *head = tp->root; |
145 | struct fw_filter *f = (struct fw_filter *)arg; |
146 | struct fw_filter **fp; |
147 | |
148 | if (head == NULL || f == NULL) |
149 | goto out; |
150 | |
151 | for (fp = &head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) { |
152 | if (*fp == f) { |
153 | tcf_tree_lock(tp); |
154 | *fp = f->next; |
155 | tcf_tree_unlock(tp); |
156 | fw_delete_filter(tp, f); |
157 | return 0; |
158 | } |
159 | } |
160 | out: |
161 | return -EINVAL; |
162 | } |
163 | |
164 | static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { |
165 | [TCA_FW_CLASSID] = { .type = NLA_U32 }, |
166 | [TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ }, |
167 | [TCA_FW_MASK] = { .type = NLA_U32 }, |
168 | }; |
169 | |
170 | static int |
171 | fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, |
172 | struct nlattr **tb, struct nlattr **tca, unsigned long base) |
173 | { |
174 | struct fw_head *head = tp->root; |
175 | struct tcf_exts e; |
176 | u32 mask; |
177 | int err; |
178 | |
179 | tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE); |
180 | err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); |
181 | if (err < 0) |
182 | return err; |
183 | |
184 | if (tb[TCA_FW_CLASSID]) { |
185 | f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); |
186 | tcf_bind_filter(tp, &f->res, base); |
187 | } |
188 | |
189 | #ifdef CONFIG_NET_CLS_IND |
190 | if (tb[TCA_FW_INDEV]) { |
191 | int ret; |
192 | ret = tcf_change_indev(net, tb[TCA_FW_INDEV]); |
193 | if (ret < 0) { |
194 | err = ret; |
195 | goto errout; |
196 | } |
197 | f->ifindex = ret; |
198 | } |
199 | #endif /* CONFIG_NET_CLS_IND */ |
200 | |
201 | err = -EINVAL; |
202 | if (tb[TCA_FW_MASK]) { |
203 | mask = nla_get_u32(tb[TCA_FW_MASK]); |
204 | if (mask != head->mask) |
205 | goto errout; |
206 | } else if (head->mask != 0xFFFFFFFF) |
207 | goto errout; |
208 | |
209 | tcf_exts_change(tp, &f->exts, &e); |
210 | |
211 | return 0; |
212 | errout: |
213 | tcf_exts_destroy(tp, &e); |
214 | return err; |
215 | } |
216 | |
217 | static int fw_change(struct net *net, struct sk_buff *in_skb, |
218 | struct tcf_proto *tp, unsigned long base, |
219 | u32 handle, |
220 | struct nlattr **tca, |
221 | unsigned long *arg) |
222 | { |
223 | struct fw_head *head = tp->root; |
224 | struct fw_filter *f = (struct fw_filter *) *arg; |
225 | struct nlattr *opt = tca[TCA_OPTIONS]; |
226 | struct nlattr *tb[TCA_FW_MAX + 1]; |
227 | int err; |
228 | |
229 | if (!opt) |
230 | return handle ? -EINVAL : 0; |
231 | |
232 | err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); |
233 | if (err < 0) |
234 | return err; |
235 | |
236 | if (f != NULL) { |
237 | if (f->id != handle && handle) |
238 | return -EINVAL; |
239 | return fw_change_attrs(net, tp, f, tb, tca, base); |
240 | } |
241 | |
242 | if (!handle) |
243 | return -EINVAL; |
244 | |
245 | if (head == NULL) { |
246 | u32 mask = 0xFFFFFFFF; |
247 | if (tb[TCA_FW_MASK]) |
248 | mask = nla_get_u32(tb[TCA_FW_MASK]); |
249 | |
250 | head = kzalloc(sizeof(struct fw_head), GFP_KERNEL); |
251 | if (head == NULL) |
252 | return -ENOBUFS; |
253 | head->mask = mask; |
254 | |
255 | tcf_tree_lock(tp); |
256 | tp->root = head; |
257 | tcf_tree_unlock(tp); |
258 | } |
259 | |
260 | f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); |
261 | if (f == NULL) |
262 | return -ENOBUFS; |
263 | |
264 | tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); |
265 | f->id = handle; |
266 | |
267 | err = fw_change_attrs(net, tp, f, tb, tca, base); |
268 | if (err < 0) |
269 | goto errout; |
270 | |
271 | f->next = head->ht[fw_hash(handle)]; |
272 | tcf_tree_lock(tp); |
273 | head->ht[fw_hash(handle)] = f; |
274 | tcf_tree_unlock(tp); |
275 | |
276 | *arg = (unsigned long)f; |
277 | return 0; |
278 | |
279 | errout: |
280 | kfree(f); |
281 | return err; |
282 | } |
283 | |
284 | static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) |
285 | { |
286 | struct fw_head *head = tp->root; |
287 | int h; |
288 | |
289 | if (head == NULL) |
290 | arg->stop = 1; |
291 | |
292 | if (arg->stop) |
293 | return; |
294 | |
295 | for (h = 0; h < HTSIZE; h++) { |
296 | struct fw_filter *f; |
297 | |
298 | for (f = head->ht[h]; f; f = f->next) { |
299 | if (arg->count < arg->skip) { |
300 | arg->count++; |
301 | continue; |
302 | } |
303 | if (arg->fn(tp, (unsigned long)f, arg) < 0) { |
304 | arg->stop = 1; |
305 | return; |
306 | } |
307 | arg->count++; |
308 | } |
309 | } |
310 | } |
311 | |
312 | static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, |
313 | struct sk_buff *skb, struct tcmsg *t) |
314 | { |
315 | struct fw_head *head = tp->root; |
316 | struct fw_filter *f = (struct fw_filter *)fh; |
317 | unsigned char *b = skb_tail_pointer(skb); |
318 | struct nlattr *nest; |
319 | |
320 | if (f == NULL) |
321 | return skb->len; |
322 | |
323 | t->tcm_handle = f->id; |
324 | |
325 | if (!f->res.classid && !tcf_exts_is_available(&f->exts)) |
326 | return skb->len; |
327 | |
328 | nest = nla_nest_start(skb, TCA_OPTIONS); |
329 | if (nest == NULL) |
330 | goto nla_put_failure; |
331 | |
332 | if (f->res.classid && |
333 | nla_put_u32(skb, TCA_FW_CLASSID, f->res.classid)) |
334 | goto nla_put_failure; |
335 | #ifdef CONFIG_NET_CLS_IND |
336 | if (f->ifindex) { |
337 | struct net_device *dev; |
338 | dev = __dev_get_by_index(net, f->ifindex); |
339 | if (dev && nla_put_string(skb, TCA_FW_INDEV, dev->name)) |
340 | goto nla_put_failure; |
341 | } |
342 | #endif /* CONFIG_NET_CLS_IND */ |
343 | if (head->mask != 0xFFFFFFFF && |
344 | nla_put_u32(skb, TCA_FW_MASK, head->mask)) |
345 | goto nla_put_failure; |
346 | |
347 | if (tcf_exts_dump(skb, &f->exts) < 0) |
348 | goto nla_put_failure; |
349 | |
350 | nla_nest_end(skb, nest); |
351 | |
352 | if (tcf_exts_dump_stats(skb, &f->exts) < 0) |
353 | goto nla_put_failure; |
354 | |
355 | return skb->len; |
356 | |
357 | nla_put_failure: |
358 | nlmsg_trim(skb, b); |
359 | return -1; |
360 | } |
361 | |
362 | static struct tcf_proto_ops cls_fw_ops __read_mostly = { |
363 | .kind = "fw", |
364 | .classify = fw_classify, |
365 | .init = fw_init, |
366 | .destroy = fw_destroy, |
367 | .get = fw_get, |
368 | .put = fw_put, |
369 | .change = fw_change, |
370 | .delete = fw_delete, |
371 | .walk = fw_walk, |
372 | .dump = fw_dump, |
373 | .owner = THIS_MODULE, |
374 | }; |
375 | |
376 | static int __init init_fw(void) |
377 | { |
378 | return register_tcf_proto_ops(&cls_fw_ops); |
379 | } |
380 | |
381 | static void __exit exit_fw(void) |
382 | { |
383 | unregister_tcf_proto_ops(&cls_fw_ops); |
384 | } |
385 | |
386 | module_init(init_fw) |
387 | module_exit(exit_fw) |
388 | MODULE_LICENSE("GPL"); |
389 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9