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 
fw_hash(u32 handle)58 static __inline__ int fw_hash(u32 handle)
59 {
60 	return handle&0xFF;
61 }
62 
fw_classify(struct sk_buff * skb,struct tcf_proto * tp,struct tcf_result * res)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 
fw_get(struct tcf_proto * tp,u32 handle)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 
fw_put(struct tcf_proto * tp,unsigned long f)114 static void fw_put(struct tcf_proto *tp, unsigned long f)
115 {
116 }
117 
fw_init(struct tcf_proto * tp)118 static int fw_init(struct tcf_proto *tp)
119 {
120 	MOD_INC_USE_COUNT;
121 	return 0;
122 }
123 
fw_destroy(struct tcf_proto * tp)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 
fw_delete(struct tcf_proto * tp,unsigned long arg)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 
fw_change(struct tcf_proto * tp,unsigned long base,u32 handle,struct rtattr ** tca,unsigned long * arg)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 
fw_walk(struct tcf_proto * tp,struct tcf_walker * arg)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 				return;
296 			}
297 			arg->count++;
298 		}
299 	}
300 }
301 
fw_dump(struct tcf_proto * tp,unsigned long fh,struct sk_buff * skb,struct tcmsg * t)302 static int fw_dump(struct tcf_proto *tp, unsigned long fh,
303 		   struct sk_buff *skb, struct tcmsg *t)
304 {
305 	struct fw_filter *f = (struct fw_filter*)fh;
306 	unsigned char	 *b = skb->tail;
307 	struct rtattr *rta;
308 
309 	if (f == NULL)
310 		return skb->len;
311 
312 	t->tcm_handle = f->id;
313 
314        if (!f->res.classid
315 #ifdef CONFIG_NET_CLS_POLICE
316            && !f->police
317 #endif
318            )
319 		return skb->len;
320 
321 	rta = (struct rtattr*)b;
322 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
323 
324 	if (f->res.classid)
325 		RTA_PUT(skb, TCA_FW_CLASSID, 4, &f->res.classid);
326 #ifdef CONFIG_NET_CLS_POLICE
327 	if (f->police) {
328 		struct rtattr * p_rta = (struct rtattr*)skb->tail;
329 
330 		RTA_PUT(skb, TCA_FW_POLICE, 0, NULL);
331 
332 		if (tcf_police_dump(skb, f->police) < 0)
333 			goto rtattr_failure;
334 
335 		p_rta->rta_len = skb->tail - (u8*)p_rta;
336 	}
337 #endif
338 
339 	rta->rta_len = skb->tail - b;
340 #ifdef CONFIG_NET_CLS_POLICE
341 	if (f->police) {
342 		if (qdisc_copy_stats(skb, &f->police->stats))
343 			goto rtattr_failure;
344 	}
345 #endif
346 	return skb->len;
347 
348 rtattr_failure:
349 	skb_trim(skb, b - skb->data);
350 	return -1;
351 }
352 
353 struct tcf_proto_ops cls_fw_ops = {
354 	NULL,
355 	"fw",
356 	fw_classify,
357 	fw_init,
358 	fw_destroy,
359 
360 	fw_get,
361 	fw_put,
362 	fw_change,
363 	fw_delete,
364 	fw_walk,
365 	fw_dump
366 };
367 
368 #ifdef MODULE
init_module(void)369 int init_module(void)
370 {
371 	return register_tcf_proto_ops(&cls_fw_ops);
372 }
373 
cleanup_module(void)374 void cleanup_module(void)
375 {
376 	unregister_tcf_proto_ops(&cls_fw_ops);
377 }
378 #endif
379 MODULE_LICENSE("GPL");
380