vserver 1.9.3
[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 <asm/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/pkt_sched.h>
47
48 struct fw_head
49 {
50         struct fw_filter *ht[256];
51 };
52
53 struct fw_filter
54 {
55         struct fw_filter        *next;
56         u32                     id;
57         struct tcf_result       res;
58 #ifdef CONFIG_NET_CLS_ACT
59        struct tc_action        *action;
60 #ifdef CONFIG_NET_CLS_IND
61        char                     indev[IFNAMSIZ];
62 #endif
63 #else
64 #ifdef CONFIG_NET_CLS_POLICE
65         struct tcf_police       *police;
66 #endif
67 #endif
68 };
69
70 static __inline__ int fw_hash(u32 handle)
71 {
72         return handle&0xFF;
73 }
74
75 static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
76                           struct tcf_result *res)
77 {
78         struct fw_head *head = (struct fw_head*)tp->root;
79         struct fw_filter *f;
80 #ifdef CONFIG_NETFILTER
81         u32 id = skb->nfmark;
82 #else
83         u32 id = 0;
84 #endif
85
86         if (head == NULL)
87                 goto old_method;
88
89         for (f=head->ht[fw_hash(id)]; f; f=f->next) {
90                 if (f->id == id) {
91                         *res = f->res;
92 #ifdef CONFIG_NET_CLS_ACT
93 #ifdef CONFIG_NET_CLS_IND
94                         if (0 != f->indev[0]) {
95                                 if  (NULL == skb->input_dev) {
96                                         continue;
97                                 } else {
98                                         if (0 != strcmp(f->indev, skb->input_dev->name)) {
99                                                 continue;
100                                         }
101                                 }
102                         }
103 #endif
104                                if (f->action) {
105                                        int pol_res = tcf_action_exec(skb, f->action, res);
106                                        if (pol_res >= 0)
107                                                return pol_res;
108                                } else
109 #else
110 #ifdef CONFIG_NET_CLS_POLICE
111                         if (f->police)
112                                 return tcf_police(skb, f->police);
113 #endif
114 #endif
115                         return 0;
116                 }
117         }
118         return -1;
119
120 old_method:
121         if (id && (TC_H_MAJ(id) == 0 ||
122                      !(TC_H_MAJ(id^tp->q->handle)))) {
123                 res->classid = id;
124                 res->class = 0;
125                 return 0;
126         }
127         return -1;
128 }
129
130 static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
131 {
132         struct fw_head *head = (struct fw_head*)tp->root;
133         struct fw_filter *f;
134
135         if (head == NULL)
136                 return 0;
137
138         for (f=head->ht[fw_hash(handle)]; f; f=f->next) {
139                 if (f->id == handle)
140                         return (unsigned long)f;
141         }
142         return 0;
143 }
144
145 static void fw_put(struct tcf_proto *tp, unsigned long f)
146 {
147 }
148
149 static int fw_init(struct tcf_proto *tp)
150 {
151         return 0;
152 }
153
154 static void fw_destroy(struct tcf_proto *tp)
155 {
156         struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
157         struct fw_filter *f;
158         int h;
159
160         if (head == NULL)
161                 return;
162
163         for (h=0; h<256; h++) {
164                 while ((f=head->ht[h]) != NULL) {
165                         unsigned long cl;
166                         head->ht[h] = f->next;
167
168                         if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
169                                 tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
170 #ifdef CONFIG_NET_CLS_ACT
171        if (f->action) {
172                tcf_action_destroy(f->action,TCA_ACT_UNBIND);
173        }
174 #else
175 #ifdef CONFIG_NET_CLS_POLICE
176                         tcf_police_release(f->police,TCA_ACT_UNBIND);
177 #endif
178 #endif
179
180                         kfree(f);
181                 }
182         }
183         kfree(head);
184 }
185
186 static int fw_delete(struct tcf_proto *tp, unsigned long arg)
187 {
188         struct fw_head *head = (struct fw_head*)tp->root;
189         struct fw_filter *f = (struct fw_filter*)arg;
190         struct fw_filter **fp;
191
192         if (head == NULL || f == NULL)
193                 goto out;
194
195         for (fp=&head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
196                 if (*fp == f) {
197                         unsigned long cl;
198
199                         tcf_tree_lock(tp);
200                         *fp = f->next;
201                         tcf_tree_unlock(tp);
202
203                         if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
204                                 tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
205 #ifdef CONFIG_NET_CLS_ACT
206        if (f->action) {
207                tcf_action_destroy(f->action,TCA_ACT_UNBIND);
208        }
209 #else
210 #ifdef CONFIG_NET_CLS_POLICE
211                         tcf_police_release(f->police,TCA_ACT_UNBIND);
212 #endif
213 #endif
214                         kfree(f);
215                         return 0;
216                 }
217         }
218 out:
219         return -EINVAL;
220 }
221
222 static int fw_change(struct tcf_proto *tp, unsigned long base,
223                      u32 handle,
224                      struct rtattr **tca,
225                      unsigned long *arg)
226 {
227         struct fw_head *head = (struct fw_head*)tp->root;
228         struct fw_filter *f;
229         struct rtattr *opt = tca[TCA_OPTIONS-1];
230         struct rtattr *tb[TCA_FW_MAX];
231         int err;
232 #ifdef CONFIG_NET_CLS_ACT
233        struct tc_action *act = NULL;
234        int ret;
235 #endif
236
237
238         if (!opt)
239                 return handle ? -EINVAL : 0;
240
241         if (rtattr_parse(tb, TCA_FW_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)) < 0)
242                 return -EINVAL;
243
244         if ((f = (struct fw_filter*)*arg) != NULL) {
245                 /* Node exists: adjust only classid */
246
247                 if (f->id != handle && handle)
248                         return -EINVAL;
249                 if (tb[TCA_FW_CLASSID-1]) {
250                         unsigned long cl;
251
252                         f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
253                         cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid);
254                         cl = cls_set_class(tp, &f->res.class, cl);
255                         if (cl)
256                                 tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
257                 }
258 #ifdef CONFIG_NET_CLS_ACT
259                 if (tb[TCA_FW_POLICE-1]) {
260                         act = kmalloc(sizeof(*act),GFP_KERNEL);
261                         if (NULL == act)
262                                 return -ENOMEM;
263
264                         memset(act,0,sizeof(*act));
265                         ret = tcf_action_init_1(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1] ,act,"police",TCA_ACT_NOREPLACE,TCA_ACT_BIND);
266                         if (0 > ret){
267                                 tcf_action_destroy(act,TCA_ACT_UNBIND);
268                                 return ret;
269                         }
270                         act->type = TCA_OLD_COMPAT;
271
272                         sch_tree_lock(tp->q);
273                         act = xchg(&f->action, act);
274                         sch_tree_unlock(tp->q);
275
276                         tcf_action_destroy(act,TCA_ACT_UNBIND);
277
278                 }
279
280                 if(tb[TCA_FW_ACT-1]) {
281                         act = kmalloc(sizeof(*act),GFP_KERNEL);
282                         if (NULL == act)
283                                 return -ENOMEM;
284                         memset(act,0,sizeof(*act));
285                         ret = tcf_action_init(tb[TCA_FW_ACT-1], tca[TCA_RATE-1],act,NULL, TCA_ACT_NOREPLACE,TCA_ACT_BIND);
286                         if (0 > ret) {
287                                 tcf_action_destroy(act,TCA_ACT_UNBIND);
288                                 return ret;
289                         }
290
291                         sch_tree_lock(tp->q);
292                         act = xchg(&f->action, act);
293                         sch_tree_unlock(tp->q);
294
295                         tcf_action_destroy(act,TCA_ACT_UNBIND);
296                 }
297 #ifdef CONFIG_NET_CLS_IND
298                 if(tb[TCA_FW_INDEV-1]) {
299                         struct rtattr *idev = tb[TCA_FW_INDEV-1];
300                         if (RTA_PAYLOAD(idev) >= IFNAMSIZ) {
301                                 printk("cls_fw: bad indev name %s\n",(char*)RTA_DATA(idev));
302                                 err = -EINVAL;
303                                 goto errout;
304                         }
305                         memset(f->indev,0,IFNAMSIZ);
306                         sprintf(f->indev, "%s", (char*)RTA_DATA(idev));
307                 }
308 #endif
309 #else /* only POLICE defined */
310 #ifdef CONFIG_NET_CLS_POLICE
311                 if (tb[TCA_FW_POLICE-1]) {
312                         struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
313
314                         tcf_tree_lock(tp);
315                         police = xchg(&f->police, police);
316                         tcf_tree_unlock(tp);
317
318                         tcf_police_release(police,TCA_ACT_UNBIND);
319                 }
320 #endif
321 #endif
322                 return 0;
323         }
324
325         if (!handle)
326                 return -EINVAL;
327
328         if (head == NULL) {
329                 head = kmalloc(sizeof(struct fw_head), GFP_KERNEL);
330                 if (head == NULL)
331                         return -ENOBUFS;
332                 memset(head, 0, sizeof(*head));
333
334                 tcf_tree_lock(tp);
335                 tp->root = head;
336                 tcf_tree_unlock(tp);
337         }
338
339         f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
340         if (f == NULL)
341                 return -ENOBUFS;
342         memset(f, 0, sizeof(*f));
343
344         f->id = handle;
345
346         if (tb[TCA_FW_CLASSID-1]) {
347                 err = -EINVAL;
348                 if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != 4)
349                         goto errout;
350                 f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
351                 cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
352         }
353
354 #ifdef CONFIG_NET_CLS_ACT
355         if(tb[TCA_FW_ACT-1]) {
356                 act = kmalloc(sizeof(*act),GFP_KERNEL);
357                 if (NULL == act)
358                         return -ENOMEM;
359                 memset(act,0,sizeof(*act));
360                 ret = tcf_action_init(tb[TCA_FW_ACT-1], tca[TCA_RATE-1],act,NULL,TCA_ACT_NOREPLACE,TCA_ACT_BIND);
361                 if (0 > ret) {
362                         tcf_action_destroy(act,TCA_ACT_UNBIND);
363                         return ret;
364                 }
365                 f->action= act;
366         }
367 #ifdef CONFIG_NET_CLS_IND
368                 if(tb[TCA_FW_INDEV-1]) {
369                         struct rtattr *idev = tb[TCA_FW_INDEV-1];
370                         if (RTA_PAYLOAD(idev) >= IFNAMSIZ) {
371                                 printk("cls_fw: bad indev name %s\n",(char*)RTA_DATA(idev));
372                                 err = -EINVAL;
373                                 goto errout;
374                         }
375                         memset(f->indev,0,IFNAMSIZ);
376                         sprintf(f->indev, "%s", (char*)RTA_DATA(idev));
377                 }
378 #endif
379 #else
380 #ifdef CONFIG_NET_CLS_POLICE
381         if (tb[TCA_FW_POLICE-1])
382                 f->police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
383 #endif
384 #endif
385
386         f->next = head->ht[fw_hash(handle)];
387         tcf_tree_lock(tp);
388         head->ht[fw_hash(handle)] = f;
389         tcf_tree_unlock(tp);
390
391         *arg = (unsigned long)f;
392         return 0;
393
394 errout:
395         if (f)
396                 kfree(f);
397         return err;
398 }
399
400 static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
401 {
402         struct fw_head *head = (struct fw_head*)tp->root;
403         int h;
404
405         if (head == NULL)
406                 arg->stop = 1;
407
408         if (arg->stop)
409                 return;
410
411         for (h = 0; h < 256; h++) {
412                 struct fw_filter *f;
413
414                 for (f = head->ht[h]; f; f = f->next) {
415                         if (arg->count < arg->skip) {
416                                 arg->count++;
417                                 continue;
418                         }
419                         if (arg->fn(tp, (unsigned long)f, arg) < 0) {
420                                 arg->stop = 1;
421                                 break;
422                         }
423                         arg->count++;
424                 }
425         }
426 }
427
428 static int fw_dump(struct tcf_proto *tp, unsigned long fh,
429                    struct sk_buff *skb, struct tcmsg *t)
430 {
431         struct fw_filter *f = (struct fw_filter*)fh;
432         unsigned char    *b = skb->tail;
433         struct rtattr *rta;
434
435         if (f == NULL)
436                 return skb->len;
437
438         t->tcm_handle = f->id;
439
440        if (!f->res.classid
441 #ifdef CONFIG_NET_CLS_ACT
442            && !f->action
443 #else
444 #ifdef CONFIG_NET_CLS_POLICE
445            && !f->police
446 #endif
447 #endif
448            )
449                 return skb->len;
450
451         rta = (struct rtattr*)b;
452         RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
453
454         if (f->res.classid)
455                 RTA_PUT(skb, TCA_FW_CLASSID, 4, &f->res.classid);
456 #ifdef CONFIG_NET_CLS_ACT
457                /* again for backward compatible mode - we want
458                *  to work with both old and new modes of entering
459                *  tc data even if iproute2  was newer - jhs
460                */
461         if (f->action) {
462                 struct rtattr * p_rta = (struct rtattr*)skb->tail;
463
464                 if (f->action->type != TCA_OLD_COMPAT) {
465                         RTA_PUT(skb, TCA_FW_ACT, 0, NULL);
466                         if (tcf_action_dump(skb,f->action,0,0) < 0) {
467                                 goto rtattr_failure;
468                         }
469                 } else {
470                         RTA_PUT(skb, TCA_FW_POLICE, 0, NULL);
471                         if (tcf_action_dump_old(skb,f->action,0,0) < 0) {
472                                 goto rtattr_failure;
473                         }
474                 }
475
476                 p_rta->rta_len = skb->tail - (u8*)p_rta;
477         }
478 #ifdef CONFIG_NET_CLS_IND
479         if(strlen(f->indev)) {
480                 struct rtattr * p_rta = (struct rtattr*)skb->tail;
481                 RTA_PUT(skb, TCA_FW_INDEV, IFNAMSIZ, f->indev);
482                 p_rta->rta_len = skb->tail - (u8*)p_rta;
483         }
484 #endif
485 #else
486 #ifdef CONFIG_NET_CLS_POLICE
487         if (f->police) {
488                 struct rtattr * p_rta = (struct rtattr*)skb->tail;
489
490                 RTA_PUT(skb, TCA_FW_POLICE, 0, NULL);
491
492                 if (tcf_police_dump(skb, f->police) < 0)
493                         goto rtattr_failure;
494
495                 p_rta->rta_len = skb->tail - (u8*)p_rta;
496         }
497 #endif
498 #endif
499
500         rta->rta_len = skb->tail - b;
501 #ifdef CONFIG_NET_CLS_ACT
502        if (f->action && f->action->type == TCA_OLD_COMPAT) {
503                if (tcf_action_copy_stats(skb,f->action))
504                        goto rtattr_failure;
505        }
506 #else
507 #ifdef CONFIG_NET_CLS_POLICE
508         if (f->police) {
509                 if (qdisc_copy_stats(skb, &f->police->stats,
510                                      f->police->stats_lock))
511                         goto rtattr_failure;
512         }
513 #endif
514 #endif
515         return skb->len;
516
517 rtattr_failure:
518         skb_trim(skb, b - skb->data);
519         return -1;
520 }
521
522 static struct tcf_proto_ops cls_fw_ops = {
523         .next           =       NULL,
524         .kind           =       "fw",
525         .classify       =       fw_classify,
526         .init           =       fw_init,
527         .destroy        =       fw_destroy,
528         .get            =       fw_get,
529         .put            =       fw_put,
530         .change         =       fw_change,
531         .delete         =       fw_delete,
532         .walk           =       fw_walk,
533         .dump           =       fw_dump,
534         .owner          =       THIS_MODULE,
535 };
536
537 static int __init init_fw(void)
538 {
539         return register_tcf_proto_ops(&cls_fw_ops);
540 }
541
542 static void __exit exit_fw(void) 
543 {
544         unregister_tcf_proto_ops(&cls_fw_ops);
545 }
546
547 module_init(init_fw)
548 module_exit(exit_fw)
549 MODULE_LICENSE("GPL");