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