Root/
1 | /* |
2 | * Berkeley Packet Filter based traffic classifier |
3 | * |
4 | * Might be used to classify traffic through flexible, user-defined and |
5 | * possibly JIT-ed BPF filters for traffic control as an alternative to |
6 | * ematches. |
7 | * |
8 | * (C) 2013 Daniel Borkmann <dborkman@redhat.com> |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. |
13 | */ |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/types.h> |
17 | #include <linux/skbuff.h> |
18 | #include <linux/filter.h> |
19 | #include <net/rtnetlink.h> |
20 | #include <net/pkt_cls.h> |
21 | #include <net/sock.h> |
22 | |
23 | MODULE_LICENSE("GPL"); |
24 | MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>"); |
25 | MODULE_DESCRIPTION("TC BPF based classifier"); |
26 | |
27 | struct cls_bpf_head { |
28 | struct list_head plist; |
29 | u32 hgen; |
30 | }; |
31 | |
32 | struct cls_bpf_prog { |
33 | struct sk_filter *filter; |
34 | struct sock_filter *bpf_ops; |
35 | struct tcf_exts exts; |
36 | struct tcf_result res; |
37 | struct list_head link; |
38 | u32 handle; |
39 | u16 bpf_len; |
40 | }; |
41 | |
42 | static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { |
43 | [TCA_BPF_CLASSID] = { .type = NLA_U32 }, |
44 | [TCA_BPF_OPS_LEN] = { .type = NLA_U16 }, |
45 | [TCA_BPF_OPS] = { .type = NLA_BINARY, |
46 | .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, |
47 | }; |
48 | |
49 | static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
50 | struct tcf_result *res) |
51 | { |
52 | struct cls_bpf_head *head = tp->root; |
53 | struct cls_bpf_prog *prog; |
54 | int ret; |
55 | |
56 | list_for_each_entry(prog, &head->plist, link) { |
57 | int filter_res = SK_RUN_FILTER(prog->filter, skb); |
58 | |
59 | if (filter_res == 0) |
60 | continue; |
61 | |
62 | *res = prog->res; |
63 | if (filter_res != -1) |
64 | res->classid = filter_res; |
65 | |
66 | ret = tcf_exts_exec(skb, &prog->exts, res); |
67 | if (ret < 0) |
68 | continue; |
69 | |
70 | return ret; |
71 | } |
72 | |
73 | return -1; |
74 | } |
75 | |
76 | static int cls_bpf_init(struct tcf_proto *tp) |
77 | { |
78 | struct cls_bpf_head *head; |
79 | |
80 | head = kzalloc(sizeof(*head), GFP_KERNEL); |
81 | if (head == NULL) |
82 | return -ENOBUFS; |
83 | |
84 | INIT_LIST_HEAD(&head->plist); |
85 | tp->root = head; |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog) |
91 | { |
92 | tcf_unbind_filter(tp, &prog->res); |
93 | tcf_exts_destroy(tp, &prog->exts); |
94 | |
95 | sk_unattached_filter_destroy(prog->filter); |
96 | |
97 | kfree(prog->bpf_ops); |
98 | kfree(prog); |
99 | } |
100 | |
101 | static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) |
102 | { |
103 | struct cls_bpf_head *head = tp->root; |
104 | struct cls_bpf_prog *prog, *todel = (struct cls_bpf_prog *) arg; |
105 | |
106 | list_for_each_entry(prog, &head->plist, link) { |
107 | if (prog == todel) { |
108 | tcf_tree_lock(tp); |
109 | list_del(&prog->link); |
110 | tcf_tree_unlock(tp); |
111 | |
112 | cls_bpf_delete_prog(tp, prog); |
113 | return 0; |
114 | } |
115 | } |
116 | |
117 | return -ENOENT; |
118 | } |
119 | |
120 | static void cls_bpf_destroy(struct tcf_proto *tp) |
121 | { |
122 | struct cls_bpf_head *head = tp->root; |
123 | struct cls_bpf_prog *prog, *tmp; |
124 | |
125 | list_for_each_entry_safe(prog, tmp, &head->plist, link) { |
126 | list_del(&prog->link); |
127 | cls_bpf_delete_prog(tp, prog); |
128 | } |
129 | |
130 | kfree(head); |
131 | } |
132 | |
133 | static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) |
134 | { |
135 | struct cls_bpf_head *head = tp->root; |
136 | struct cls_bpf_prog *prog; |
137 | unsigned long ret = 0UL; |
138 | |
139 | if (head == NULL) |
140 | return 0UL; |
141 | |
142 | list_for_each_entry(prog, &head->plist, link) { |
143 | if (prog->handle == handle) { |
144 | ret = (unsigned long) prog; |
145 | break; |
146 | } |
147 | } |
148 | |
149 | return ret; |
150 | } |
151 | |
152 | static void cls_bpf_put(struct tcf_proto *tp, unsigned long f) |
153 | { |
154 | } |
155 | |
156 | static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, |
157 | struct cls_bpf_prog *prog, |
158 | unsigned long base, struct nlattr **tb, |
159 | struct nlattr *est) |
160 | { |
161 | struct sock_filter *bpf_ops, *bpf_old; |
162 | struct tcf_exts exts; |
163 | struct sock_fprog tmp; |
164 | struct sk_filter *fp, *fp_old; |
165 | u16 bpf_size, bpf_len; |
166 | u32 classid; |
167 | int ret; |
168 | |
169 | if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID]) |
170 | return -EINVAL; |
171 | |
172 | tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE); |
173 | ret = tcf_exts_validate(net, tp, tb, est, &exts); |
174 | if (ret < 0) |
175 | return ret; |
176 | |
177 | classid = nla_get_u32(tb[TCA_BPF_CLASSID]); |
178 | bpf_len = nla_get_u16(tb[TCA_BPF_OPS_LEN]); |
179 | if (bpf_len > BPF_MAXINSNS || bpf_len == 0) { |
180 | ret = -EINVAL; |
181 | goto errout; |
182 | } |
183 | |
184 | bpf_size = bpf_len * sizeof(*bpf_ops); |
185 | bpf_ops = kzalloc(bpf_size, GFP_KERNEL); |
186 | if (bpf_ops == NULL) { |
187 | ret = -ENOMEM; |
188 | goto errout; |
189 | } |
190 | |
191 | memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size); |
192 | |
193 | tmp.len = bpf_len; |
194 | tmp.filter = (struct sock_filter __user *) bpf_ops; |
195 | |
196 | ret = sk_unattached_filter_create(&fp, &tmp); |
197 | if (ret) |
198 | goto errout_free; |
199 | |
200 | tcf_tree_lock(tp); |
201 | fp_old = prog->filter; |
202 | bpf_old = prog->bpf_ops; |
203 | |
204 | prog->bpf_len = bpf_len; |
205 | prog->bpf_ops = bpf_ops; |
206 | prog->filter = fp; |
207 | prog->res.classid = classid; |
208 | tcf_tree_unlock(tp); |
209 | |
210 | tcf_bind_filter(tp, &prog->res, base); |
211 | tcf_exts_change(tp, &prog->exts, &exts); |
212 | |
213 | if (fp_old) |
214 | sk_unattached_filter_destroy(fp_old); |
215 | if (bpf_old) |
216 | kfree(bpf_old); |
217 | |
218 | return 0; |
219 | |
220 | errout_free: |
221 | kfree(bpf_ops); |
222 | errout: |
223 | tcf_exts_destroy(tp, &exts); |
224 | return ret; |
225 | } |
226 | |
227 | static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp, |
228 | struct cls_bpf_head *head) |
229 | { |
230 | unsigned int i = 0x80000000; |
231 | |
232 | do { |
233 | if (++head->hgen == 0x7FFFFFFF) |
234 | head->hgen = 1; |
235 | } while (--i > 0 && cls_bpf_get(tp, head->hgen)); |
236 | if (i == 0) |
237 | pr_err("Insufficient number of handles\n"); |
238 | |
239 | return i; |
240 | } |
241 | |
242 | static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, |
243 | struct tcf_proto *tp, unsigned long base, |
244 | u32 handle, struct nlattr **tca, |
245 | unsigned long *arg) |
246 | { |
247 | struct cls_bpf_head *head = tp->root; |
248 | struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg; |
249 | struct nlattr *tb[TCA_BPF_MAX + 1]; |
250 | int ret; |
251 | |
252 | if (tca[TCA_OPTIONS] == NULL) |
253 | return -EINVAL; |
254 | |
255 | ret = nla_parse_nested(tb, TCA_BPF_MAX, tca[TCA_OPTIONS], bpf_policy); |
256 | if (ret < 0) |
257 | return ret; |
258 | |
259 | if (prog != NULL) { |
260 | if (handle && prog->handle != handle) |
261 | return -EINVAL; |
262 | return cls_bpf_modify_existing(net, tp, prog, base, tb, |
263 | tca[TCA_RATE]); |
264 | } |
265 | |
266 | prog = kzalloc(sizeof(*prog), GFP_KERNEL); |
267 | if (prog == NULL) |
268 | return -ENOBUFS; |
269 | |
270 | tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE); |
271 | if (handle == 0) |
272 | prog->handle = cls_bpf_grab_new_handle(tp, head); |
273 | else |
274 | prog->handle = handle; |
275 | if (prog->handle == 0) { |
276 | ret = -EINVAL; |
277 | goto errout; |
278 | } |
279 | |
280 | ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]); |
281 | if (ret < 0) |
282 | goto errout; |
283 | |
284 | tcf_tree_lock(tp); |
285 | list_add(&prog->link, &head->plist); |
286 | tcf_tree_unlock(tp); |
287 | |
288 | *arg = (unsigned long) prog; |
289 | |
290 | return 0; |
291 | errout: |
292 | if (*arg == 0UL && prog) |
293 | kfree(prog); |
294 | |
295 | return ret; |
296 | } |
297 | |
298 | static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, |
299 | struct sk_buff *skb, struct tcmsg *tm) |
300 | { |
301 | struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh; |
302 | struct nlattr *nest, *nla; |
303 | |
304 | if (prog == NULL) |
305 | return skb->len; |
306 | |
307 | tm->tcm_handle = prog->handle; |
308 | |
309 | nest = nla_nest_start(skb, TCA_OPTIONS); |
310 | if (nest == NULL) |
311 | goto nla_put_failure; |
312 | |
313 | if (nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid)) |
314 | goto nla_put_failure; |
315 | if (nla_put_u16(skb, TCA_BPF_OPS_LEN, prog->bpf_len)) |
316 | goto nla_put_failure; |
317 | |
318 | nla = nla_reserve(skb, TCA_BPF_OPS, prog->bpf_len * |
319 | sizeof(struct sock_filter)); |
320 | if (nla == NULL) |
321 | goto nla_put_failure; |
322 | |
323 | memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla)); |
324 | |
325 | if (tcf_exts_dump(skb, &prog->exts) < 0) |
326 | goto nla_put_failure; |
327 | |
328 | nla_nest_end(skb, nest); |
329 | |
330 | if (tcf_exts_dump_stats(skb, &prog->exts) < 0) |
331 | goto nla_put_failure; |
332 | |
333 | return skb->len; |
334 | |
335 | nla_put_failure: |
336 | nla_nest_cancel(skb, nest); |
337 | return -1; |
338 | } |
339 | |
340 | static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) |
341 | { |
342 | struct cls_bpf_head *head = tp->root; |
343 | struct cls_bpf_prog *prog; |
344 | |
345 | list_for_each_entry(prog, &head->plist, link) { |
346 | if (arg->count < arg->skip) |
347 | goto skip; |
348 | if (arg->fn(tp, (unsigned long) prog, arg) < 0) { |
349 | arg->stop = 1; |
350 | break; |
351 | } |
352 | skip: |
353 | arg->count++; |
354 | } |
355 | } |
356 | |
357 | static struct tcf_proto_ops cls_bpf_ops __read_mostly = { |
358 | .kind = "bpf", |
359 | .owner = THIS_MODULE, |
360 | .classify = cls_bpf_classify, |
361 | .init = cls_bpf_init, |
362 | .destroy = cls_bpf_destroy, |
363 | .get = cls_bpf_get, |
364 | .put = cls_bpf_put, |
365 | .change = cls_bpf_change, |
366 | .delete = cls_bpf_delete, |
367 | .walk = cls_bpf_walk, |
368 | .dump = cls_bpf_dump, |
369 | }; |
370 | |
371 | static int __init cls_bpf_init_mod(void) |
372 | { |
373 | return register_tcf_proto_ops(&cls_bpf_ops); |
374 | } |
375 | |
376 | static void __exit cls_bpf_exit_mod(void) |
377 | { |
378 | unregister_tcf_proto_ops(&cls_bpf_ops); |
379 | } |
380 | |
381 | module_init(cls_bpf_init_mod); |
382 | module_exit(cls_bpf_exit_mod); |
383 |
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