Blame view

net/sched/cls_fw.c 8.35 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * net/sched/cls_fw.c	Classifier mapping ipchains' fwmark to traffic class.
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   * Changes:
   * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_walk off by one
   * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_delete killed all the filter (and kernel).
   * Alex <alex@pilotsoft.com> : 2004xxyy: Added Action extension
   *
   * JHS: We should remove the CONFIG_NET_CLS_IND from here
   * eventually when the meta match extension is made available
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  #include <linux/skbuff.h>
0ba480538   Patrick McHardy   [NET_SCHED]: Remo...
27
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
  #include <net/act_api.h>
  #include <net/pkt_cls.h>
c5c13fafd   Thomas Graf   [PKT_SCHED]: impr...
30
  #define HTSIZE (PAGE_SIZE/sizeof(struct fw_filter *))
cc7ec456f   Eric Dumazet   net_sched: cleanups
31
  struct fw_head {
c5c13fafd   Thomas Graf   [PKT_SCHED]: impr...
32
  	struct fw_filter *ht[HTSIZE];
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
33
  	u32 mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
35
  struct fw_filter {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
  	struct fw_filter	*next;
  	u32			id;
  	struct tcf_result	res;
  #ifdef CONFIG_NET_CLS_IND
  	char			indev[IFNAMSIZ];
  #endif /* CONFIG_NET_CLS_IND */
  	struct tcf_exts		exts;
  };
5239008b0   Patrick McHardy   [NET_SCHED]: Cons...
44
  static const struct tcf_ext_map fw_ext_map = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
  	.action = TCA_FW_ACT,
  	.police = TCA_FW_POLICE
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
48
  static inline int fw_hash(u32 handle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  {
c5c13fafd   Thomas Graf   [PKT_SCHED]: impr...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  	if (HTSIZE == 4096)
  		return ((handle >> 24) & 0xFFF) ^
  		       ((handle >> 12) & 0xFFF) ^
  		       (handle & 0xFFF);
  	else if (HTSIZE == 2048)
  		return ((handle >> 22) & 0x7FF) ^
  		       ((handle >> 11) & 0x7FF) ^
  		       (handle & 0x7FF);
  	else if (HTSIZE == 1024)
  		return ((handle >> 20) & 0x3FF) ^
  		       ((handle >> 10) & 0x3FF) ^
  		       (handle & 0x3FF);
  	else if (HTSIZE == 512)
  		return (handle >> 27) ^
  		       ((handle >> 18) & 0x1FF) ^
  		       ((handle >> 9) & 0x1FF) ^
  		       (handle & 0x1FF);
  	else if (HTSIZE == 256) {
  		u8 *t = (u8 *) &handle;
  		return t[0] ^ t[1] ^ t[2] ^ t[3];
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
70
  	} else
c5c13fafd   Thomas Graf   [PKT_SCHED]: impr...
71
  		return handle & (HTSIZE - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  }
dc7f9f6e8   Eric Dumazet   net: sched: const...
73
  static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  			  struct tcf_result *res)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
76
  	struct fw_head *head = (struct fw_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  	struct fw_filter *f;
  	int r;
5c804bfdc   Patrick McHardy   [NET_SCHED]: cls_...
79
  	u32 id = skb->mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  
  	if (head != NULL) {
5c804bfdc   Patrick McHardy   [NET_SCHED]: cls_...
82
  		id &= head->mask;
cc7ec456f   Eric Dumazet   net_sched: cleanups
83
  		for (f = head->ht[fw_hash(id)]; f; f = f->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  			if (f->id == id) {
  				*res = f->res;
  #ifdef CONFIG_NET_CLS_IND
  				if (!tcf_match_indev(skb, f->indev))
  					continue;
  #endif /* CONFIG_NET_CLS_IND */
  				r = tcf_exts_exec(skb, &f->exts, res);
  				if (r < 0)
  					continue;
  
  				return r;
  			}
  		}
  	} else {
  		/* old method */
cc7ec456f   Eric Dumazet   net_sched: cleanups
99
100
  		if (id && (TC_H_MAJ(id) == 0 ||
  			   !(TC_H_MAJ(id ^ tp->q->handle)))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
107
108
109
110
111
  			res->classid = id;
  			res->class = 0;
  			return 0;
  		}
  	}
  
  	return -1;
  }
  
  static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
112
  	struct fw_head *head = (struct fw_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
  	struct fw_filter *f;
  
  	if (head == NULL)
  		return 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
117
  	for (f = head->ht[fw_hash(handle)]; f; f = f->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  		if (f->id == handle)
  			return (unsigned long)f;
  	}
  	return 0;
  }
  
  static void fw_put(struct tcf_proto *tp, unsigned long f)
  {
  }
  
  static int fw_init(struct tcf_proto *tp)
  {
  	return 0;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
132
  static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
139
140
  {
  	tcf_unbind_filter(tp, &f->res);
  	tcf_exts_destroy(tp, &f->exts);
  	kfree(f);
  }
  
  static void fw_destroy(struct tcf_proto *tp)
  {
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
141
  	struct fw_head *head = tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
  	struct fw_filter *f;
  	int h;
  
  	if (head == NULL)
  		return;
cc7ec456f   Eric Dumazet   net_sched: cleanups
147
148
  	for (h = 0; h < HTSIZE; h++) {
  		while ((f = head->ht[h]) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
156
157
  			head->ht[h] = f->next;
  			fw_delete_filter(tp, f);
  		}
  	}
  	kfree(head);
  }
  
  static int fw_delete(struct tcf_proto *tp, unsigned long arg)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
158
159
  	struct fw_head *head = (struct fw_head *)tp->root;
  	struct fw_filter *f = (struct fw_filter *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
  	struct fw_filter **fp;
  
  	if (head == NULL || f == NULL)
  		goto out;
cc7ec456f   Eric Dumazet   net_sched: cleanups
164
  	for (fp = &head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
169
170
171
172
173
174
175
  		if (*fp == f) {
  			tcf_tree_lock(tp);
  			*fp = f->next;
  			tcf_tree_unlock(tp);
  			fw_delete_filter(tp, f);
  			return 0;
  		}
  	}
  out:
  	return -EINVAL;
  }
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
176
177
178
179
180
  static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = {
  	[TCA_FW_CLASSID]	= { .type = NLA_U32 },
  	[TCA_FW_INDEV]		= { .type = NLA_STRING, .len = IFNAMSIZ },
  	[TCA_FW_MASK]		= { .type = NLA_U32 },
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  static int
  fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
183
  	struct nlattr **tb, struct nlattr **tca, unsigned long base)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  {
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
185
  	struct fw_head *head = (struct fw_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	struct tcf_exts e;
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
187
  	u32 mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  	int err;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
189
  	err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &fw_ext_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
  	if (err < 0)
  		return err;
  
  	err = -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
194
  	if (tb[TCA_FW_CLASSID]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
195
  		f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
  		tcf_bind_filter(tp, &f->res, base);
  	}
  
  #ifdef CONFIG_NET_CLS_IND
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
200
201
  	if (tb[TCA_FW_INDEV]) {
  		err = tcf_change_indev(tp, f->indev, tb[TCA_FW_INDEV]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
  		if (err < 0)
  			goto errout;
  	}
  #endif /* CONFIG_NET_CLS_IND */
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
206
  	if (tb[TCA_FW_MASK]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
207
  		mask = nla_get_u32(tb[TCA_FW_MASK]);
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
208
209
210
211
  		if (mask != head->mask)
  			goto errout;
  	} else if (head->mask != 0xFFFFFFFF)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
214
215
216
217
218
219
220
221
  	tcf_exts_change(tp, &f->exts, &e);
  
  	return 0;
  errout:
  	tcf_exts_destroy(tp, &e);
  	return err;
  }
  
  static int fw_change(struct tcf_proto *tp, unsigned long base,
  		     u32 handle,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
222
  		     struct nlattr **tca,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  		     unsigned long *arg)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
225
  	struct fw_head *head = (struct fw_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  	struct fw_filter *f = (struct fw_filter *) *arg;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
227
228
  	struct nlattr *opt = tca[TCA_OPTIONS];
  	struct nlattr *tb[TCA_FW_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
  	int err;
  
  	if (!opt)
  		return handle ? -EINVAL : 0;
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
233
  	err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
234
235
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
241
242
243
244
245
246
  
  	if (f != NULL) {
  		if (f->id != handle && handle)
  			return -EINVAL;
  		return fw_change_attrs(tp, f, tb, tca, base);
  	}
  
  	if (!handle)
  		return -EINVAL;
  
  	if (head == NULL) {
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
247
  		u32 mask = 0xFFFFFFFF;
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
248
  		if (tb[TCA_FW_MASK])
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
249
  			mask = nla_get_u32(tb[TCA_FW_MASK]);
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
250

0da974f4f   Panagiotis Issaris   [NET]: Conversion...
251
  		head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
  		if (head == NULL)
  			return -ENOBUFS;
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
254
  		head->mask = mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
  
  		tcf_tree_lock(tp);
  		tp->root = head;
  		tcf_tree_unlock(tp);
  	}
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
260
  	f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
  	if (f == NULL)
  		return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  
  	f->id = handle;
  
  	err = fw_change_attrs(tp, f, tb, tca, base);
  	if (err < 0)
  		goto errout;
  
  	f->next = head->ht[fw_hash(handle)];
  	tcf_tree_lock(tp);
  	head->ht[fw_hash(handle)] = f;
  	tcf_tree_unlock(tp);
  
  	*arg = (unsigned long)f;
  	return 0;
  
  errout:
a51482bde   Jesper Juhl   [NET]: kfree cleanup
279
  	kfree(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
  	return err;
  }
  
  static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
285
  	struct fw_head *head = (struct fw_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
291
292
  	int h;
  
  	if (head == NULL)
  		arg->stop = 1;
  
  	if (arg->stop)
  		return;
c5c13fafd   Thomas Graf   [PKT_SCHED]: impr...
293
  	for (h = 0; h < HTSIZE; h++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
  		struct fw_filter *f;
  
  		for (f = head->ht[h]; f; f = f->next) {
  			if (arg->count < arg->skip) {
  				arg->count++;
  				continue;
  			}
  			if (arg->fn(tp, (unsigned long)f, arg) < 0) {
  				arg->stop = 1;
  				return;
  			}
  			arg->count++;
  		}
  	}
  }
  
  static int fw_dump(struct tcf_proto *tp, unsigned long fh,
  		   struct sk_buff *skb, struct tcmsg *t)
  {
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
313
  	struct fw_head *head = (struct fw_head *)tp->root;
cc7ec456f   Eric Dumazet   net_sched: cleanups
314
  	struct fw_filter *f = (struct fw_filter *)fh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
315
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
316
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
321
322
323
324
  
  	if (f == NULL)
  		return skb->len;
  
  	t->tcm_handle = f->id;
  
  	if (!f->res.classid && !tcf_exts_is_available(&f->exts))
  		return skb->len;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
325
326
327
  	nest = nla_nest_start(skb, TCA_OPTIONS);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
  
  	if (f->res.classid)
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
330
  		NLA_PUT_U32(skb, TCA_FW_CLASSID, f->res.classid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  #ifdef CONFIG_NET_CLS_IND
  	if (strlen(f->indev))
57e1c487a   Patrick McHardy   [NET_SCHED]: Use ...
333
  		NLA_PUT_STRING(skb, TCA_FW_INDEV, f->indev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
  #endif /* CONFIG_NET_CLS_IND */
b4e9b520c   Patrick McHardy   [NET_SCHED]: Add ...
335
  	if (head->mask != 0xFFFFFFFF)
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
336
  		NLA_PUT_U32(skb, TCA_FW_MASK, head->mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
  
  	if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
339
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
341
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
  
  	if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
344
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
  
  	return skb->len;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
347
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
348
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
  	return -1;
  }
2eb9d75c7   Patrick McHardy   [NET_SCHED]: mark...
351
  static struct tcf_proto_ops cls_fw_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
  	.kind		=	"fw",
  	.classify	=	fw_classify,
  	.init		=	fw_init,
  	.destroy	=	fw_destroy,
  	.get		=	fw_get,
  	.put		=	fw_put,
  	.change		=	fw_change,
  	.delete		=	fw_delete,
  	.walk		=	fw_walk,
  	.dump		=	fw_dump,
  	.owner		=	THIS_MODULE,
  };
  
  static int __init init_fw(void)
  {
  	return register_tcf_proto_ops(&cls_fw_ops);
  }
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
369
  static void __exit exit_fw(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
372
373
374
375
376
  {
  	unregister_tcf_proto_ops(&cls_fw_ops);
  }
  
  module_init(init_fw)
  module_exit(exit_fw)
  MODULE_LICENSE("GPL");