File: /usr/src/linux/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     	MOD_INC_USE_COUNT;
121     	return 0;
122     }
123     
124     static void fw_destroy(struct tcf_proto *tp)
125     {
126     	struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
127     	struct fw_filter *f;
128     	int h;
129     
130     	if (head == NULL) {
131     		MOD_DEC_USE_COUNT;
132     		return;
133     	}
134     
135     	for (h=0; h<256; h++) {
136     		while ((f=head->ht[h]) != NULL) {
137     			unsigned long cl;
138     			head->ht[h] = f->next;
139     
140     			if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
141     				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
142     #ifdef CONFIG_NET_CLS_POLICE
143     			tcf_police_release(f->police);
144     #endif
145     			kfree(f);
146     		}
147     	}
148     	kfree(head);
149     	MOD_DEC_USE_COUNT;
150     }
151     
152     static int fw_delete(struct tcf_proto *tp, unsigned long arg)
153     {
154     	struct fw_head *head = (struct fw_head*)tp->root;
155     	struct fw_filter *f = (struct fw_filter*)arg;
156     	struct fw_filter **fp;
157     
158     	if (head == NULL || f == NULL)
159     		return -EINVAL;
160     
161     	for (fp=&head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
162     		if (*fp == f) {
163     			unsigned long cl;
164     
165     			tcf_tree_lock(tp);
166     			*fp = f->next;
167     			tcf_tree_unlock(tp);
168     
169     			if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
170     				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
171     #ifdef CONFIG_NET_CLS_POLICE
172     			tcf_police_release(f->police);
173     #endif
174     			kfree(f);
175     			return 0;
176     		}
177     	}
178     	return -EINVAL;
179     }
180     
181     static int fw_change(struct tcf_proto *tp, unsigned long base,
182     		     u32 handle,
183     		     struct rtattr **tca,
184     		     unsigned long *arg)
185     {
186     	struct fw_head *head = (struct fw_head*)tp->root;
187     	struct fw_filter *f;
188     	struct rtattr *opt = tca[TCA_OPTIONS-1];
189     	struct rtattr *tb[TCA_FW_MAX];
190     	int err;
191     
192     	if (!opt)
193     		return handle ? -EINVAL : 0;
194     
195     	if (rtattr_parse(tb, TCA_FW_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)) < 0)
196     		return -EINVAL;
197     
198     	if ((f = (struct fw_filter*)*arg) != NULL) {
199     		/* Node exists: adjust only classid */
200     
201     		if (f->id != handle && handle)
202     			return -EINVAL;
203     		if (tb[TCA_FW_CLASSID-1]) {
204     			unsigned long cl;
205     
206     			f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
207     			cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid);
208     			cl = cls_set_class(tp, &f->res.class, cl);
209     			if (cl)
210     				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
211     		}
212     #ifdef CONFIG_NET_CLS_POLICE
213     		if (tb[TCA_FW_POLICE-1]) {
214     			struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
215     
216     			tcf_tree_lock(tp);
217     			police = xchg(&f->police, police);
218     			tcf_tree_unlock(tp);
219     
220     			tcf_police_release(police);
221     		}
222     #endif
223     		return 0;
224     	}
225     
226     	if (!handle)
227     		return -EINVAL;
228     
229     	if (head == NULL) {
230     		head = kmalloc(sizeof(struct fw_head), GFP_KERNEL);
231     		if (head == NULL)
232     			return -ENOBUFS;
233     		memset(head, 0, sizeof(*head));
234     
235     		tcf_tree_lock(tp);
236     		tp->root = head;
237     		tcf_tree_unlock(tp);
238     	}
239     
240     	f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
241     	if (f == NULL)
242     		return -ENOBUFS;
243     	memset(f, 0, sizeof(*f));
244     
245     	f->id = handle;
246     
247     	if (tb[TCA_FW_CLASSID-1]) {
248     		err = -EINVAL;
249     		if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != 4)
250     			goto errout;
251     		f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
252     		cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
253     	}
254     
255     #ifdef CONFIG_NET_CLS_POLICE
256     	if (tb[TCA_FW_POLICE-1])
257     		f->police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
258     #endif
259     
260     	f->next = head->ht[fw_hash(handle)];
261     	tcf_tree_lock(tp);
262     	head->ht[fw_hash(handle)] = f;
263     	tcf_tree_unlock(tp);
264     
265     	*arg = (unsigned long)f;
266     	return 0;
267     
268     errout:
269     	if (f)
270     		kfree(f);
271     	return err;
272     }
273     
274     static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
275     {
276     	struct fw_head *head = (struct fw_head*)tp->root;
277     	int h;
278     
279     	if (head == NULL)
280     		arg->stop = 1;
281     
282     	if (arg->stop)
283     		return;
284     
285     	for (h = 0; h < 256; h++) {
286     		struct fw_filter *f;
287     
288     		for (f = head->ht[h]; f; f = f->next) {
289     			if (arg->count < arg->skip) {
290     				arg->count++;
291     				continue;
292     			}
293     			if (arg->fn(tp, (unsigned long)f, arg) < 0) {
294     				arg->stop = 1;
295     				break;
296     			}
297     			arg->count++;
298     		}
299     	}
300     }
301     
302     #ifdef CONFIG_RTNETLINK
303     static int fw_dump(struct tcf_proto *tp, unsigned long fh,
304     		   struct sk_buff *skb, struct tcmsg *t)
305     {
306     	struct fw_filter *f = (struct fw_filter*)fh;
307     	unsigned char	 *b = skb->tail;
308     	struct rtattr *rta;
309     
310     	if (f == NULL)
311     		return skb->len;
312     
313     	t->tcm_handle = f->id;
314     
315            if (!f->res.classid
316     #ifdef CONFIG_NET_CLS_POLICE
317                && !f->police
318     #endif
319                )
320     		return skb->len;
321     
322     	rta = (struct rtattr*)b;
323     	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
324     
325     	if (f->res.classid)
326     		RTA_PUT(skb, TCA_FW_CLASSID, 4, &f->res.classid);
327     #ifdef CONFIG_NET_CLS_POLICE
328     	if (f->police) {
329     		struct rtattr * p_rta = (struct rtattr*)skb->tail;
330     
331     		RTA_PUT(skb, TCA_FW_POLICE, 0, NULL);
332     
333     		if (tcf_police_dump(skb, f->police) < 0)
334     			goto rtattr_failure;
335     
336     		p_rta->rta_len = skb->tail - (u8*)p_rta;
337     	}
338     #endif
339     
340     	rta->rta_len = skb->tail - b;
341     #ifdef CONFIG_NET_CLS_POLICE
342     	if (f->police) {
343     		if (qdisc_copy_stats(skb, &f->police->stats))
344     			goto rtattr_failure;
345     	}
346     #endif
347     	return skb->len;
348     
349     rtattr_failure:
350     	skb_trim(skb, b - skb->data);
351     	return -1;
352     }
353     #endif
354     
355     
356     struct tcf_proto_ops cls_fw_ops = {
357     	NULL,
358     	"fw",
359     	fw_classify,
360     	fw_init,
361     	fw_destroy,
362     
363     	fw_get,
364     	fw_put,
365     	fw_change,
366     	fw_delete,
367     	fw_walk,
368     #ifdef CONFIG_RTNETLINK
369     	fw_dump
370     #else
371     	NULL
372     #endif
373     };
374     
375     #ifdef MODULE
376     int init_module(void)
377     {
378     	return register_tcf_proto_ops(&cls_fw_ops);
379     }
380     
381     void cleanup_module(void) 
382     {
383     	unregister_tcf_proto_ops(&cls_fw_ops);
384     }
385     #endif
386