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