1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/module.h>
17#include <linux/slab.h>
18#include <linux/types.h>
19#include <linux/kernel.h>
20#include <linux/string.h>
21#include <linux/errno.h>
22#include <linux/skbuff.h>
23#include <net/netlink.h>
24#include <net/act_api.h>
25#include <net/pkt_cls.h>
26#include <net/sch_generic.h>
27
28#define HTSIZE 256
29
30struct fw_head {
31 u32 mask;
32 struct fw_filter __rcu *ht[HTSIZE];
33 struct rcu_head rcu;
34};
35
36struct fw_filter {
37 struct fw_filter __rcu *next;
38 u32 id;
39 struct tcf_result res;
40#ifdef CONFIG_NET_CLS_IND
41 int ifindex;
42#endif
43 struct tcf_exts exts;
44 struct tcf_proto *tp;
45 struct rcu_work rwork;
46};
47
48static u32 fw_hash(u32 handle)
49{
50 handle ^= (handle >> 16);
51 handle ^= (handle >> 8);
52 return handle % HTSIZE;
53}
54
55static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp,
56 struct tcf_result *res)
57{
58 struct fw_head *head = rcu_dereference_bh(tp->root);
59 struct fw_filter *f;
60 int r;
61 u32 id = skb->mark;
62
63 if (head != NULL) {
64 id &= head->mask;
65
66 for (f = rcu_dereference_bh(head->ht[fw_hash(id)]); f;
67 f = rcu_dereference_bh(f->next)) {
68 if (f->id == id) {
69 *res = f->res;
70#ifdef CONFIG_NET_CLS_IND
71 if (!tcf_match_indev(skb, f->ifindex))
72 continue;
73#endif
74 r = tcf_exts_exec(skb, &f->exts, res);
75 if (r < 0)
76 continue;
77
78 return r;
79 }
80 }
81 } else {
82 struct Qdisc *q = tcf_block_q(tp->chain->block);
83
84
85 if (id && (TC_H_MAJ(id) == 0 ||
86 !(TC_H_MAJ(id ^ q->handle)))) {
87 res->classid = id;
88 res->class = 0;
89 return 0;
90 }
91 }
92
93 return -1;
94}
95
96static void *fw_get(struct tcf_proto *tp, u32 handle)
97{
98 struct fw_head *head = rtnl_dereference(tp->root);
99 struct fw_filter *f;
100
101 if (head == NULL)
102 return NULL;
103
104 f = rtnl_dereference(head->ht[fw_hash(handle)]);
105 for (; f; f = rtnl_dereference(f->next)) {
106 if (f->id == handle)
107 return f;
108 }
109 return NULL;
110}
111
112static int fw_init(struct tcf_proto *tp)
113{
114
115
116
117 return 0;
118}
119
120static void __fw_delete_filter(struct fw_filter *f)
121{
122 tcf_exts_destroy(&f->exts);
123 tcf_exts_put_net(&f->exts);
124 kfree(f);
125}
126
127static void fw_delete_filter_work(struct work_struct *work)
128{
129 struct fw_filter *f = container_of(to_rcu_work(work),
130 struct fw_filter,
131 rwork);
132 rtnl_lock();
133 __fw_delete_filter(f);
134 rtnl_unlock();
135}
136
137static void fw_destroy(struct tcf_proto *tp, bool rtnl_held,
138 struct netlink_ext_ack *extack)
139{
140 struct fw_head *head = rtnl_dereference(tp->root);
141 struct fw_filter *f;
142 int h;
143
144 if (head == NULL)
145 return;
146
147 for (h = 0; h < HTSIZE; h++) {
148 while ((f = rtnl_dereference(head->ht[h])) != NULL) {
149 RCU_INIT_POINTER(head->ht[h],
150 rtnl_dereference(f->next));
151 tcf_unbind_filter(tp, &f->res);
152 if (tcf_exts_get_net(&f->exts))
153 tcf_queue_work(&f->rwork, fw_delete_filter_work);
154 else
155 __fw_delete_filter(f);
156 }
157 }
158 kfree_rcu(head, rcu);
159}
160
161static int fw_delete(struct tcf_proto *tp, void *arg, bool *last,
162 bool rtnl_held, struct netlink_ext_ack *extack)
163{
164 struct fw_head *head = rtnl_dereference(tp->root);
165 struct fw_filter *f = arg;
166 struct fw_filter __rcu **fp;
167 struct fw_filter *pfp;
168 int ret = -EINVAL;
169 int h;
170
171 if (head == NULL || f == NULL)
172 goto out;
173
174 fp = &head->ht[fw_hash(f->id)];
175
176 for (pfp = rtnl_dereference(*fp); pfp;
177 fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
178 if (pfp == f) {
179 RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
180 tcf_unbind_filter(tp, &f->res);
181 tcf_exts_get_net(&f->exts);
182 tcf_queue_work(&f->rwork, fw_delete_filter_work);
183 ret = 0;
184 break;
185 }
186 }
187
188 *last = true;
189 for (h = 0; h < HTSIZE; h++) {
190 if (rcu_access_pointer(head->ht[h])) {
191 *last = false;
192 break;
193 }
194 }
195
196out:
197 return ret;
198}
199
200static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = {
201 [TCA_FW_CLASSID] = { .type = NLA_U32 },
202 [TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ },
203 [TCA_FW_MASK] = { .type = NLA_U32 },
204};
205
206static int fw_set_parms(struct net *net, struct tcf_proto *tp,
207 struct fw_filter *f, struct nlattr **tb,
208 struct nlattr **tca, unsigned long base, bool ovr,
209 struct netlink_ext_ack *extack)
210{
211 struct fw_head *head = rtnl_dereference(tp->root);
212 u32 mask;
213 int err;
214
215 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &f->exts, ovr,
216 true, extack);
217 if (err < 0)
218 return err;
219
220 if (tb[TCA_FW_CLASSID]) {
221 f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]);
222 tcf_bind_filter(tp, &f->res, base);
223 }
224
225#ifdef CONFIG_NET_CLS_IND
226 if (tb[TCA_FW_INDEV]) {
227 int ret;
228 ret = tcf_change_indev(net, tb[TCA_FW_INDEV], extack);
229 if (ret < 0)
230 return ret;
231 f->ifindex = ret;
232 }
233#endif
234
235 err = -EINVAL;
236 if (tb[TCA_FW_MASK]) {
237 mask = nla_get_u32(tb[TCA_FW_MASK]);
238 if (mask != head->mask)
239 return err;
240 } else if (head->mask != 0xFFFFFFFF)
241 return err;
242
243 return 0;
244}
245
246static int fw_change(struct net *net, struct sk_buff *in_skb,
247 struct tcf_proto *tp, unsigned long base,
248 u32 handle, struct nlattr **tca, void **arg,
249 bool ovr, bool rtnl_held,
250 struct netlink_ext_ack *extack)
251{
252 struct fw_head *head = rtnl_dereference(tp->root);
253 struct fw_filter *f = *arg;
254 struct nlattr *opt = tca[TCA_OPTIONS];
255 struct nlattr *tb[TCA_FW_MAX + 1];
256 int err;
257
258 if (!opt)
259 return handle ? -EINVAL : 0;
260
261 err = nla_parse_nested_deprecated(tb, TCA_FW_MAX, opt, fw_policy,
262 NULL);
263 if (err < 0)
264 return err;
265
266 if (f) {
267 struct fw_filter *pfp, *fnew;
268 struct fw_filter __rcu **fp;
269
270 if (f->id != handle && handle)
271 return -EINVAL;
272
273 fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
274 if (!fnew)
275 return -ENOBUFS;
276
277 fnew->id = f->id;
278 fnew->res = f->res;
279#ifdef CONFIG_NET_CLS_IND
280 fnew->ifindex = f->ifindex;
281#endif
282 fnew->tp = f->tp;
283
284 err = tcf_exts_init(&fnew->exts, net, TCA_FW_ACT,
285 TCA_FW_POLICE);
286 if (err < 0) {
287 kfree(fnew);
288 return err;
289 }
290
291 err = fw_set_parms(net, tp, fnew, tb, tca, base, ovr, extack);
292 if (err < 0) {
293 tcf_exts_destroy(&fnew->exts);
294 kfree(fnew);
295 return err;
296 }
297
298 fp = &head->ht[fw_hash(fnew->id)];
299 for (pfp = rtnl_dereference(*fp); pfp;
300 fp = &pfp->next, pfp = rtnl_dereference(*fp))
301 if (pfp == f)
302 break;
303
304 RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next));
305 rcu_assign_pointer(*fp, fnew);
306 tcf_unbind_filter(tp, &f->res);
307 tcf_exts_get_net(&f->exts);
308 tcf_queue_work(&f->rwork, fw_delete_filter_work);
309
310 *arg = fnew;
311 return err;
312 }
313
314 if (!handle)
315 return -EINVAL;
316
317 if (!head) {
318 u32 mask = 0xFFFFFFFF;
319 if (tb[TCA_FW_MASK])
320 mask = nla_get_u32(tb[TCA_FW_MASK]);
321
322 head = kzalloc(sizeof(*head), GFP_KERNEL);
323 if (!head)
324 return -ENOBUFS;
325 head->mask = mask;
326
327 rcu_assign_pointer(tp->root, head);
328 }
329
330 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
331 if (f == NULL)
332 return -ENOBUFS;
333
334 err = tcf_exts_init(&f->exts, net, TCA_FW_ACT, TCA_FW_POLICE);
335 if (err < 0)
336 goto errout;
337 f->id = handle;
338 f->tp = tp;
339
340 err = fw_set_parms(net, tp, f, tb, tca, base, ovr, extack);
341 if (err < 0)
342 goto errout;
343
344 RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]);
345 rcu_assign_pointer(head->ht[fw_hash(handle)], f);
346
347 *arg = f;
348 return 0;
349
350errout:
351 tcf_exts_destroy(&f->exts);
352 kfree(f);
353 return err;
354}
355
356static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg,
357 bool rtnl_held)
358{
359 struct fw_head *head = rtnl_dereference(tp->root);
360 int h;
361
362 if (head == NULL)
363 arg->stop = 1;
364
365 if (arg->stop)
366 return;
367
368 for (h = 0; h < HTSIZE; h++) {
369 struct fw_filter *f;
370
371 for (f = rtnl_dereference(head->ht[h]); f;
372 f = rtnl_dereference(f->next)) {
373 if (arg->count < arg->skip) {
374 arg->count++;
375 continue;
376 }
377 if (arg->fn(tp, f, arg) < 0) {
378 arg->stop = 1;
379 return;
380 }
381 arg->count++;
382 }
383 }
384}
385
386static int fw_dump(struct net *net, struct tcf_proto *tp, void *fh,
387 struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
388{
389 struct fw_head *head = rtnl_dereference(tp->root);
390 struct fw_filter *f = fh;
391 struct nlattr *nest;
392
393 if (f == NULL)
394 return skb->len;
395
396 t->tcm_handle = f->id;
397
398 if (!f->res.classid && !tcf_exts_has_actions(&f->exts))
399 return skb->len;
400
401 nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
402 if (nest == NULL)
403 goto nla_put_failure;
404
405 if (f->res.classid &&
406 nla_put_u32(skb, TCA_FW_CLASSID, f->res.classid))
407 goto nla_put_failure;
408#ifdef CONFIG_NET_CLS_IND
409 if (f->ifindex) {
410 struct net_device *dev;
411 dev = __dev_get_by_index(net, f->ifindex);
412 if (dev && nla_put_string(skb, TCA_FW_INDEV, dev->name))
413 goto nla_put_failure;
414 }
415#endif
416 if (head->mask != 0xFFFFFFFF &&
417 nla_put_u32(skb, TCA_FW_MASK, head->mask))
418 goto nla_put_failure;
419
420 if (tcf_exts_dump(skb, &f->exts) < 0)
421 goto nla_put_failure;
422
423 nla_nest_end(skb, nest);
424
425 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
426 goto nla_put_failure;
427
428 return skb->len;
429
430nla_put_failure:
431 nla_nest_cancel(skb, nest);
432 return -1;
433}
434
435static void fw_bind_class(void *fh, u32 classid, unsigned long cl)
436{
437 struct fw_filter *f = fh;
438
439 if (f && f->res.classid == classid)
440 f->res.class = cl;
441}
442
443static struct tcf_proto_ops cls_fw_ops __read_mostly = {
444 .kind = "fw",
445 .classify = fw_classify,
446 .init = fw_init,
447 .destroy = fw_destroy,
448 .get = fw_get,
449 .change = fw_change,
450 .delete = fw_delete,
451 .walk = fw_walk,
452 .dump = fw_dump,
453 .bind_class = fw_bind_class,
454 .owner = THIS_MODULE,
455};
456
457static int __init init_fw(void)
458{
459 return register_tcf_proto_ops(&cls_fw_ops);
460}
461
462static void __exit exit_fw(void)
463{
464 unregister_tcf_proto_ops(&cls_fw_ops);
465}
466
467module_init(init_fw)
468module_exit(exit_fw)
469MODULE_LICENSE("GPL");
470