upgrade to linux 2.6.10-1.12_FC2
[linux-2.6.git] / net / sched / cls_fw.c
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/config.h>
22 #include <linux/module.h>
23 #include <asm/uaccess.h>
24 #include <asm/system.h>
25 #include <linux/bitops.h>
26 #include <linux/types.h>
27 #include <linux/kernel.h>
28 #include <linux/sched.h>
29 #include <linux/string.h>
30 #include <linux/mm.h>
31 #include <linux/socket.h>
32 #include <linux/sockios.h>
33 #include <linux/in.h>
34 #include <linux/errno.h>
35 #include <linux/interrupt.h>
36 #include <linux/if_ether.h>
37 #include <linux/inet.h>
38 #include <linux/netdevice.h>
39 #include <linux/etherdevice.h>
40 #include <linux/notifier.h>
41 #include <linux/netfilter.h>
42 #include <net/ip.h>
43 #include <net/route.h>
44 #include <linux/skbuff.h>
45 #include <net/sock.h>
46 #include <net/act_api.h>
47 #include <net/pkt_cls.h>
48
49 struct fw_head
50 {
51         struct fw_filter *ht[256];
52 };
53
54 struct fw_filter
55 {
56         struct fw_filter        *next;
57         u32                     id;
58         struct tcf_result       res;
59 #ifdef CONFIG_NET_CLS_IND
60         char                    indev[IFNAMSIZ];
61 #endif /* CONFIG_NET_CLS_IND */
62 #ifdef CONFIG_NET_CLS_ACT
63         struct tc_action        *action;
64 #else /* CONFIG_NET_CLS_ACT */
65 #ifdef CONFIG_NET_CLS_POLICE
66         struct tcf_police       *police;
67 #endif /* CONFIG_NET_CLS_POLICE */
68 #endif /* CONFIG_NET_CLS_ACT */
69 };
70
71 static __inline__ int fw_hash(u32 handle)
72 {
73         return handle&0xFF;
74 }
75
76 static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
77                           struct tcf_result *res)
78 {
79         struct fw_head *head = (struct fw_head*)tp->root;
80         struct fw_filter *f;
81 #ifdef CONFIG_NETFILTER
82         u32 id = skb->nfmark;
83 #else
84         u32 id = 0;
85 #endif
86
87         if (head != NULL) {
88                 for (f=head->ht[fw_hash(id)]; f; f=f->next) {
89                         if (f->id == id) {
90                                 *res = f->res;
91 #ifdef CONFIG_NET_CLS_IND
92                                 if (!tcf_match_indev(skb, f->indev))
93                                         continue;
94 #endif /* CONFIG_NET_CLS_IND */
95 #ifdef CONFIG_NET_CLS_ACT
96                                 if (f->action) {
97                                         int act_res = tcf_action_exec(skb, f->action, res);
98                                         if (act_res >= 0)
99                                                 return act_res;
100                                         continue;
101                                 }
102 #else /* CONFIG_NET_CLS_ACT */
103 #ifdef CONFIG_NET_CLS_POLICE
104                                 if (f->police)
105                                         return tcf_police(skb, f->police);
106 #endif /* CONFIG_NET_CLS_POLICE */
107 #endif /* CONFIG_NET_CLS_ACT */
108                                 return 0;
109                         }
110                 }
111         } else {
112                 /* old method */
113                 if (id && (TC_H_MAJ(id) == 0 || !(TC_H_MAJ(id^tp->q->handle)))) {
114                         res->classid = id;
115                         res->class = 0;
116                         return 0;
117                 }
118         }
119
120         return -1;
121 }
122
123 static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
124 {
125         struct fw_head *head = (struct fw_head*)tp->root;
126         struct fw_filter *f;
127
128         if (head == NULL)
129                 return 0;
130
131         for (f=head->ht[fw_hash(handle)]; f; f=f->next) {
132                 if (f->id == handle)
133                         return (unsigned long)f;
134         }
135         return 0;
136 }
137
138 static void fw_put(struct tcf_proto *tp, unsigned long f)
139 {
140 }
141
142 static int fw_init(struct tcf_proto *tp)
143 {
144         return 0;
145 }
146
147 static void fw_destroy(struct tcf_proto *tp)
148 {
149         struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
150         struct fw_filter *f;
151         int h;
152
153         if (head == NULL)
154                 return;
155
156         for (h=0; h<256; h++) {
157                 while ((f=head->ht[h]) != NULL) {
158                         head->ht[h] = f->next;
159                         tcf_unbind_filter(tp, &f->res);
160 #ifdef CONFIG_NET_CLS_ACT
161                         if (f->action)
162                                 tcf_action_destroy(f->action, TCA_ACT_UNBIND);
163 #else /* CONFIG_NET_CLS_ACT */
164 #ifdef CONFIG_NET_CLS_POLICE
165                         if (f->police)
166                                 tcf_police_release(f->police, TCA_ACT_UNBIND);
167 #endif /* CONFIG_NET_CLS_POLICE */
168 #endif /* CONFIG_NET_CLS_ACT */
169
170                         kfree(f);
171                 }
172         }
173         kfree(head);
174 }
175
176 static int fw_delete(struct tcf_proto *tp, unsigned long arg)
177 {
178         struct fw_head *head = (struct fw_head*)tp->root;
179         struct fw_filter *f = (struct fw_filter*)arg;
180         struct fw_filter **fp;
181
182         if (head == NULL || f == NULL)
183                 goto out;
184
185         for (fp=&head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
186                 if (*fp == f) {
187                         tcf_tree_lock(tp);
188                         *fp = f->next;
189                         tcf_tree_unlock(tp);
190                         tcf_unbind_filter(tp, &f->res);
191 #ifdef CONFIG_NET_CLS_ACT
192                         if (f->action)
193                                 tcf_action_destroy(f->action,TCA_ACT_UNBIND);
194 #else /* CONFIG_NET_CLS_ACT */
195 #ifdef CONFIG_NET_CLS_POLICE
196                         tcf_police_release(f->police,TCA_ACT_UNBIND);
197 #endif /* CONFIG_NET_CLS_POLICE */
198 #endif /* CONFIG_NET_CLS_ACT */
199                         kfree(f);
200                         return 0;
201                 }
202         }
203 out:
204         return -EINVAL;
205 }
206
207 static int
208 fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f,
209         struct rtattr **tb, struct rtattr **tca, unsigned long base)
210 {
211         int err = -EINVAL;
212
213         if (tb[TCA_FW_CLASSID-1]) {
214                 if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != sizeof(u32))
215                         goto errout;
216                 f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
217                 tcf_bind_filter(tp, &f->res, base);
218         }
219
220 #ifdef CONFIG_NET_CLS_IND
221         if (tb[TCA_FW_INDEV-1]) {
222                 err = tcf_change_indev(tp, f->indev, tb[TCA_FW_INDEV-1]);
223                 if (err < 0)
224                         goto errout;
225         }
226 #endif /* CONFIG_NET_CLS_IND */
227
228 #ifdef CONFIG_NET_CLS_ACT
229         if (tb[TCA_FW_POLICE-1]) {
230                 err = tcf_change_act_police(tp, &f->action, tb[TCA_FW_POLICE-1],
231                         tca[TCA_RATE-1]);
232                 if (err < 0)
233                         goto errout;
234         }
235
236         if (tb[TCA_FW_ACT-1]) {
237                 err = tcf_change_act(tp, &f->action, tb[TCA_FW_ACT-1],
238                         tca[TCA_RATE-1]);
239                 if (err < 0)
240                         goto errout;
241         }
242 #else /* CONFIG_NET_CLS_ACT */
243 #ifdef CONFIG_NET_CLS_POLICE
244         if (tb[TCA_FW_POLICE-1]) {
245                 err = tcf_change_police(tp, &f->police, tb[TCA_FW_POLICE-1],
246                         tca[TCA_RATE-1]);
247                 if (err < 0)
248                         goto errout;
249         }
250 #endif /* CONFIG_NET_CLS_POLICE */
251 #endif /* CONFIG_NET_CLS_ACT */
252
253         err = 0;
254 errout:
255         return err;
256 }
257
258 static int fw_change(struct tcf_proto *tp, unsigned long base,
259                      u32 handle,
260                      struct rtattr **tca,
261                      unsigned long *arg)
262 {
263         struct fw_head *head = (struct fw_head*)tp->root;
264         struct fw_filter *f = (struct fw_filter *) *arg;
265         struct rtattr *opt = tca[TCA_OPTIONS-1];
266         struct rtattr *tb[TCA_FW_MAX];
267         int err;
268
269         if (!opt)
270                 return handle ? -EINVAL : 0;
271
272         if (rtattr_parse(tb, TCA_FW_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)) < 0)
273                 return -EINVAL;
274
275         if (f != NULL) {
276                 if (f->id != handle && handle)
277                         return -EINVAL;
278                 return fw_change_attrs(tp, f, tb, tca, base);
279         }
280
281         if (!handle)
282                 return -EINVAL;
283
284         if (head == NULL) {
285                 head = kmalloc(sizeof(struct fw_head), GFP_KERNEL);
286                 if (head == NULL)
287                         return -ENOBUFS;
288                 memset(head, 0, sizeof(*head));
289
290                 tcf_tree_lock(tp);
291                 tp->root = head;
292                 tcf_tree_unlock(tp);
293         }
294
295         f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
296         if (f == NULL)
297                 return -ENOBUFS;
298         memset(f, 0, sizeof(*f));
299
300         f->id = handle;
301
302         err = fw_change_attrs(tp, f, tb, tca, base);
303         if (err < 0)
304                 goto errout;
305
306         f->next = head->ht[fw_hash(handle)];
307         tcf_tree_lock(tp);
308         head->ht[fw_hash(handle)] = f;
309         tcf_tree_unlock(tp);
310
311         *arg = (unsigned long)f;
312         return 0;
313
314 errout:
315         if (f)
316                 kfree(f);
317         return err;
318 }
319
320 static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
321 {
322         struct fw_head *head = (struct fw_head*)tp->root;
323         int h;
324
325         if (head == NULL)
326                 arg->stop = 1;
327
328         if (arg->stop)
329                 return;
330
331         for (h = 0; h < 256; h++) {
332                 struct fw_filter *f;
333
334                 for (f = head->ht[h]; f; f = f->next) {
335                         if (arg->count < arg->skip) {
336                                 arg->count++;
337                                 continue;
338                         }
339                         if (arg->fn(tp, (unsigned long)f, arg) < 0) {
340                                 arg->stop = 1;
341                                 return;
342                         }
343                         arg->count++;
344                 }
345         }
346 }
347
348 static int fw_dump(struct tcf_proto *tp, unsigned long fh,
349                    struct sk_buff *skb, struct tcmsg *t)
350 {
351         struct fw_filter *f = (struct fw_filter*)fh;
352         unsigned char    *b = skb->tail;
353         struct rtattr *rta;
354
355         if (f == NULL)
356                 return skb->len;
357
358         t->tcm_handle = f->id;
359
360         if (!f->res.classid
361 #ifdef CONFIG_NET_CLS_ACT
362                 && !f->action
363 #else
364 #ifdef CONFIG_NET_CLS_POLICE
365                 && !f->police
366 #endif
367 #endif
368                 )
369                 return skb->len;
370
371         rta = (struct rtattr*)b;
372         RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
373
374         if (f->res.classid)
375                 RTA_PUT(skb, TCA_FW_CLASSID, 4, &f->res.classid);
376 #ifdef CONFIG_NET_CLS_IND
377         if (strlen(f->indev))
378                 RTA_PUT(skb, TCA_FW_INDEV, IFNAMSIZ, f->indev);
379 #endif /* CONFIG_NET_CLS_IND */
380 #ifdef CONFIG_NET_CLS_ACT
381         if (tcf_dump_act(skb, f->action, TCA_FW_ACT, TCA_FW_POLICE) < 0)
382                 goto rtattr_failure;
383 #else /* CONFIG_NET_CLS_ACT */
384 #ifdef CONFIG_NET_CLS_POLICE
385         if (tcf_dump_police(skb, f->police, TCA_FW_POLICE) < 0)
386                 goto rtattr_failure;
387 #endif /* CONFIG_NET_CLS_POLICE */
388 #endif /* CONFIG_NET_CLS_ACT */
389
390         rta->rta_len = skb->tail - b;
391 #ifdef CONFIG_NET_CLS_ACT
392         if (f->action && f->action->type == TCA_OLD_COMPAT) {
393                 if (tcf_action_copy_stats(skb,f->action))
394                         goto rtattr_failure;
395         }
396 #else /* CONFIG_NET_CLS_ACT */
397 #ifdef CONFIG_NET_CLS_POLICE
398         if (f->police)
399                 if (tcf_police_dump_stats(skb, f->police) < 0)
400                         goto rtattr_failure;
401 #endif /* CONFIG_NET_CLS_POLICE */
402 #endif /* CONFIG_NET_CLS_ACT */
403         return skb->len;
404
405 rtattr_failure:
406         skb_trim(skb, b - skb->data);
407         return -1;
408 }
409
410 static struct tcf_proto_ops cls_fw_ops = {
411         .next           =       NULL,
412         .kind           =       "fw",
413         .classify       =       fw_classify,
414         .init           =       fw_init,
415         .destroy        =       fw_destroy,
416         .get            =       fw_get,
417         .put            =       fw_put,
418         .change         =       fw_change,
419         .delete         =       fw_delete,
420         .walk           =       fw_walk,
421         .dump           =       fw_dump,
422         .owner          =       THIS_MODULE,
423 };
424
425 static int __init init_fw(void)
426 {
427         return register_tcf_proto_ops(&cls_fw_ops);
428 }
429
430 static void __exit exit_fw(void) 
431 {
432         unregister_tcf_proto_ops(&cls_fw_ops);
433 }
434
435 module_init(init_fw)
436 module_exit(exit_fw)
437 MODULE_LICENSE("GPL");