Blame view

net/sched/cls_route.c 12.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * net/sched/cls_route.c	ROUTE4 classifier.
   *
   *		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>
   */
  
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/skbuff.h>
0ba480538   Patrick McHardy   [NET_SCHED]: Remo...
19
20
21
  #include <net/dst.h>
  #include <net/route.h>
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
  #include <net/act_api.h>
  #include <net/pkt_cls.h>
  
  /*
cc7ec456f   Eric Dumazet   net_sched: cleanups
26
27
28
29
30
   * 1. For now we assume that route tags < 256.
   *    It allows to use direct table lookups, instead of hash tables.
   * 2. For now we assume that "from TAG" and "fromdev DEV" statements
   *    are mutually  exclusive.
   * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
   */
cc7ec456f   Eric Dumazet   net_sched: cleanups
32
  struct route4_fastmap {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
  	struct route4_filter	*filter;
  	u32			id;
  	int			iif;
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
37
  struct route4_head {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  	struct route4_fastmap	fastmap[16];
cc7ec456f   Eric Dumazet   net_sched: cleanups
39
  	struct route4_bucket	*table[256 + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
41
  struct route4_bucket {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
  	/* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */
cc7ec456f   Eric Dumazet   net_sched: cleanups
43
  	struct route4_filter	*ht[16 + 16 + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
45
  struct route4_filter {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
  	struct route4_filter	*next;
  	u32			id;
  	int			iif;
  
  	struct tcf_result	res;
  	struct tcf_exts		exts;
  	u32			handle;
  	struct route4_bucket	*bkt;
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
55
  #define ROUTE4_FAILURE ((struct route4_filter *)(-1L))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56

5239008b0   Patrick McHardy   [NET_SCHED]: Cons...
57
  static const struct tcf_ext_map route_ext_map = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
  	.police = TCA_ROUTE4_POLICE,
  	.action = TCA_ROUTE4_ACT
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
61
  static inline int route4_fastmap_hash(u32 id, int iif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
63
  	return id & 0xF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
65
66
  static void
  route4_reset_fastmap(struct Qdisc *q, struct route4_head *head, u32 id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  {
102396ae6   Jarek Poplawski   pkt_sched: Fix lo...
68
  	spinlock_t *root_lock = qdisc_root_sleeping_lock(q);
15b458fa6   David S. Miller   pkt_sched: Kill q...
69
70
  
  	spin_lock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  	memset(head->fastmap, 0, sizeof(head->fastmap));
15b458fa6   David S. Miller   pkt_sched: Kill q...
72
  	spin_unlock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
74
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
  route4_set_fastmap(struct route4_head *head, u32 id, int iif,
  		   struct route4_filter *f)
  {
  	int h = route4_fastmap_hash(id, iif);
cc7ec456f   Eric Dumazet   net_sched: cleanups
79

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
  	head->fastmap[h].id = id;
  	head->fastmap[h].iif = iif;
  	head->fastmap[h].filter = f;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
84
  static inline int route4_hash_to(u32 id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
86
  	return id & 0xFF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
88
  static inline int route4_hash_from(u32 id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
90
  	return (id >> 16) & 0xF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
92
  static inline int route4_hash_iif(int iif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
94
  	return 16 + ((iif >> 16) & 0xF);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
96
  static inline int route4_hash_wild(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  {
  	return 32;
  }
  
  #define ROUTE4_APPLY_RESULT()					\
  {								\
  	*res = f->res;						\
  	if (tcf_exts_is_available(&f->exts)) {			\
  		int r = tcf_exts_exec(skb, &f->exts, res);	\
  		if (r < 0) {					\
  			dont_cache = 1;				\
  			continue;				\
  		}						\
  		return r;					\
  	} else if (!dont_cache)					\
  		route4_set_fastmap(head, id, iif, f);		\
  	return 0;						\
  }
dc7f9f6e8   Eric Dumazet   net: sched: const...
115
  static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
  			   struct tcf_result *res)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
118
  	struct route4_head *head = (struct route4_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
122
123
  	struct dst_entry *dst;
  	struct route4_bucket *b;
  	struct route4_filter *f;
  	u32 id, h;
  	int iif, dont_cache = 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
124
125
  	dst = skb_dst(skb);
  	if (!dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
  		goto failure;
  
  	id = dst->tclassid;
  	if (head == NULL)
  		goto old_method;
5e2b61f78   David S. Miller   ipv4: Remove flow...
131
  	iif = ((struct rtable *)dst)->rt_iif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  
  	h = route4_fastmap_hash(id, iif);
  	if (id == head->fastmap[h].id &&
  	    iif == head->fastmap[h].iif &&
  	    (f = head->fastmap[h].filter) != NULL) {
  		if (f == ROUTE4_FAILURE)
  			goto failure;
  
  		*res = f->res;
  		return 0;
  	}
  
  	h = route4_hash_to(id);
  
  restart:
cc7ec456f   Eric Dumazet   net_sched: cleanups
147
148
  	b = head->table[h];
  	if (b) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  		for (f = b->ht[route4_hash_from(id)]; f; f = f->next)
  			if (f->id == id)
  				ROUTE4_APPLY_RESULT();
  
  		for (f = b->ht[route4_hash_iif(iif)]; f; f = f->next)
  			if (f->iif == iif)
  				ROUTE4_APPLY_RESULT();
  
  		for (f = b->ht[route4_hash_wild()]; f; f = f->next)
  			ROUTE4_APPLY_RESULT();
  
  	}
  	if (h < 256) {
  		h = 256;
  		id &= ~0xFFFF;
  		goto restart;
  	}
  
  	if (!dont_cache)
  		route4_set_fastmap(head, id, iif, ROUTE4_FAILURE);
  failure:
  	return -1;
  
  old_method:
  	if (id && (TC_H_MAJ(id) == 0 ||
  		   !(TC_H_MAJ(id^tp->q->handle)))) {
  		res->classid = id;
  		res->class = 0;
  		return 0;
  	}
  	return -1;
  }
  
  static inline u32 to_hash(u32 id)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
184
185
186
  	u32 h = id & 0xFF;
  
  	if (id & 0x8000)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
195
196
197
198
  		h += 256;
  	return h;
  }
  
  static inline u32 from_hash(u32 id)
  {
  	id &= 0xFFFF;
  	if (id == 0xFFFF)
  		return 32;
  	if (!(id & 0x8000)) {
  		if (id > 255)
  			return 256;
cc7ec456f   Eric Dumazet   net_sched: cleanups
199
  		return id & 0xF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  	}
cc7ec456f   Eric Dumazet   net_sched: cleanups
201
  	return 16 + (id & 0xF);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
  }
  
  static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
206
  	struct route4_head *head = (struct route4_head *)tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
  	struct route4_bucket *b;
  	struct route4_filter *f;
cc7ec456f   Eric Dumazet   net_sched: cleanups
209
  	unsigned int h1, h2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
211
212
213
214
215
216
  
  	if (!head)
  		return 0;
  
  	h1 = to_hash(handle);
  	if (h1 > 256)
  		return 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
217
  	h2 = from_hash(handle >> 16);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
  	if (h2 > 32)
  		return 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
220
221
  	b = head->table[h1];
  	if (b) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  		for (f = b->ht[h2]; f; f = f->next)
  			if (f->handle == handle)
  				return (unsigned long)f;
  	}
  	return 0;
  }
  
  static void route4_put(struct tcf_proto *tp, unsigned long f)
  {
  }
  
  static int route4_init(struct tcf_proto *tp)
  {
  	return 0;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
237
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
242
243
244
245
246
  route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f)
  {
  	tcf_unbind_filter(tp, &f->res);
  	tcf_exts_destroy(tp, &f->exts);
  	kfree(f);
  }
  
  static void route4_destroy(struct tcf_proto *tp)
  {
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
247
  	struct route4_head *head = tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
  	int h1, h2;
  
  	if (head == NULL)
  		return;
cc7ec456f   Eric Dumazet   net_sched: cleanups
252
  	for (h1 = 0; h1 <= 256; h1++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  		struct route4_bucket *b;
cc7ec456f   Eric Dumazet   net_sched: cleanups
254
255
256
  		b = head->table[h1];
  		if (b) {
  			for (h2 = 0; h2 <= 32; h2++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  				struct route4_filter *f;
  
  				while ((f = b->ht[h2]) != NULL) {
  					b->ht[h2] = f->next;
  					route4_delete_filter(tp, f);
  				}
  			}
  			kfree(b);
  		}
  	}
  	kfree(head);
  }
  
  static int route4_delete(struct tcf_proto *tp, unsigned long arg)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
272
273
274
  	struct route4_head *head = (struct route4_head *)tp->root;
  	struct route4_filter **fp, *f = (struct route4_filter *)arg;
  	unsigned int h = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
  	struct route4_bucket *b;
  	int i;
  
  	if (!head || !f)
  		return -EINVAL;
  
  	h = f->handle;
  	b = f->bkt;
cc7ec456f   Eric Dumazet   net_sched: cleanups
283
  	for (fp = &b->ht[from_hash(h >> 16)]; *fp; fp = &(*fp)->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
  		if (*fp == f) {
  			tcf_tree_lock(tp);
  			*fp = f->next;
  			tcf_tree_unlock(tp);
15b458fa6   David S. Miller   pkt_sched: Kill q...
288
  			route4_reset_fastmap(tp->q, head, f->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
  			route4_delete_filter(tp, f);
  
  			/* Strip tree */
cc7ec456f   Eric Dumazet   net_sched: cleanups
292
  			for (i = 0; i <= 32; i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  				if (b->ht[i])
  					return 0;
  
  			/* OK, session has no flows */
  			tcf_tree_lock(tp);
  			head->table[to_hash(h)] = NULL;
  			tcf_tree_unlock(tp);
  
  			kfree(b);
  			return 0;
  		}
  	}
  	return 0;
  }
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
307
308
309
310
311
312
  static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {
  	[TCA_ROUTE4_CLASSID]	= { .type = NLA_U32 },
  	[TCA_ROUTE4_TO]		= { .type = NLA_U32 },
  	[TCA_ROUTE4_FROM]	= { .type = NLA_U32 },
  	[TCA_ROUTE4_IIF]	= { .type = NLA_U32 },
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
  static int route4_set_parms(struct tcf_proto *tp, unsigned long base,
  	struct route4_filter *f, u32 handle, struct route4_head *head,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
315
  	struct nlattr **tb, struct nlattr *est, int new)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
320
321
322
323
324
325
326
327
328
  {
  	int err;
  	u32 id = 0, to = 0, nhandle = 0x8000;
  	struct route4_filter *fp;
  	unsigned int h1;
  	struct route4_bucket *b;
  	struct tcf_exts e;
  
  	err = tcf_exts_validate(tp, tb, est, &e, &route_ext_map);
  	if (err < 0)
  		return err;
  
  	err = -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
329
  	if (tb[TCA_ROUTE4_TO]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  		if (new && handle & 0x8000)
  			goto errout;
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
332
  		to = nla_get_u32(tb[TCA_ROUTE4_TO]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
336
  		if (to > 0xFF)
  			goto errout;
  		nhandle = to;
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
337
338
  	if (tb[TCA_ROUTE4_FROM]) {
  		if (tb[TCA_ROUTE4_IIF])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  			goto errout;
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
340
  		id = nla_get_u32(tb[TCA_ROUTE4_FROM]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
  		if (id > 0xFF)
  			goto errout;
  		nhandle |= id << 16;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
344
  	} else if (tb[TCA_ROUTE4_IIF]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
345
  		id = nla_get_u32(tb[TCA_ROUTE4_IIF]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
350
351
352
353
354
355
356
357
358
  		if (id > 0x7FFF)
  			goto errout;
  		nhandle |= (id | 0x8000) << 16;
  	} else
  		nhandle |= 0xFFFF << 16;
  
  	if (handle && new) {
  		nhandle |= handle & 0x7F00;
  		if (nhandle != handle)
  			goto errout;
  	}
  
  	h1 = to_hash(nhandle);
cc7ec456f   Eric Dumazet   net_sched: cleanups
359
360
  	b = head->table[h1];
  	if (!b) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  		err = -ENOBUFS;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
362
  		b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  		if (b == NULL)
  			goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
  
  		tcf_tree_lock(tp);
  		head->table[h1] = b;
  		tcf_tree_unlock(tp);
  	} else {
  		unsigned int h2 = from_hash(nhandle >> 16);
cc7ec456f   Eric Dumazet   net_sched: cleanups
371

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
377
378
  		err = -EEXIST;
  		for (fp = b->ht[h2]; fp; fp = fp->next)
  			if (fp->handle == f->handle)
  				goto errout;
  	}
  
  	tcf_tree_lock(tp);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
379
  	if (tb[TCA_ROUTE4_TO])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  		f->id = to;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
381
  	if (tb[TCA_ROUTE4_FROM])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  		f->id = to | id<<16;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
383
  	else if (tb[TCA_ROUTE4_IIF])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
  		f->iif = id;
  
  	f->handle = nhandle;
  	f->bkt = b;
  	tcf_tree_unlock(tp);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
389
  	if (tb[TCA_ROUTE4_CLASSID]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
390
  		f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
399
400
401
402
403
  		tcf_bind_filter(tp, &f->res, base);
  	}
  
  	tcf_exts_change(tp, &f->exts, &e);
  
  	return 0;
  errout:
  	tcf_exts_destroy(tp, &e);
  	return err;
  }
  
  static int route4_change(struct tcf_proto *tp, unsigned long base,
  		       u32 handle,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
404
  		       struct nlattr **tca,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
  		       unsigned long *arg)
  {
  	struct route4_head *head = tp->root;
  	struct route4_filter *f, *f1, **fp;
  	struct route4_bucket *b;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
410
411
  	struct nlattr *opt = tca[TCA_OPTIONS];
  	struct nlattr *tb[TCA_ROUTE4_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
416
417
  	unsigned int h, th;
  	u32 old_handle = 0;
  	int err;
  
  	if (opt == NULL)
  		return handle ? -EINVAL : 0;
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
418
  	err = nla_parse_nested(tb, TCA_ROUTE4_MAX, opt, route4_policy);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
419
420
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421

cc7ec456f   Eric Dumazet   net_sched: cleanups
422
423
  	f = (struct route4_filter *)*arg;
  	if (f) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
427
428
429
430
  		if (f->handle != handle && handle)
  			return -EINVAL;
  
  		if (f->bkt)
  			old_handle = f->handle;
  
  		err = route4_set_parms(tp, base, f, handle, head, tb,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
431
  			tca[TCA_RATE], 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
434
435
436
437
438
439
  		if (err < 0)
  			return err;
  
  		goto reinsert;
  	}
  
  	err = -ENOBUFS;
  	if (head == NULL) {
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
440
  		head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
442
  		if (head == NULL)
  			goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
446
447
  
  		tcf_tree_lock(tp);
  		tp->root = head;
  		tcf_tree_unlock(tp);
  	}
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
448
  	f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
  	if (f == NULL)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
  
  	err = route4_set_parms(tp, base, f, handle, head, tb,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
453
  		tca[TCA_RATE], 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
  	if (err < 0)
  		goto errout;
  
  reinsert:
  	h = from_hash(f->handle >> 16);
cc7ec456f   Eric Dumazet   net_sched: cleanups
459
  	for (fp = &f->bkt->ht[h]; (f1 = *fp) != NULL; fp = &f1->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
466
467
468
469
  		if (f->handle < f1->handle)
  			break;
  
  	f->next = f1;
  	tcf_tree_lock(tp);
  	*fp = f;
  
  	if (old_handle && f->handle != old_handle) {
  		th = to_hash(old_handle);
  		h = from_hash(old_handle >> 16);
cc7ec456f   Eric Dumazet   net_sched: cleanups
470
471
  		b = head->table[th];
  		if (b) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
477
478
479
480
  			for (fp = &b->ht[h]; *fp; fp = &(*fp)->next) {
  				if (*fp == f) {
  					*fp = f->next;
  					break;
  				}
  			}
  		}
  	}
  	tcf_tree_unlock(tp);
15b458fa6   David S. Miller   pkt_sched: Kill q...
481
  	route4_reset_fastmap(tp->q, head, f->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
  	*arg = (unsigned long)f;
  	return 0;
  
  errout:
a51482bde   Jesper Juhl   [NET]: kfree cleanup
486
  	kfree(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
490
491
492
  	return err;
  }
  
  static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
  {
  	struct route4_head *head = tp->root;
cc7ec456f   Eric Dumazet   net_sched: cleanups
493
  	unsigned int h, h1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
  
  	if (head == NULL)
  		arg->stop = 1;
  
  	if (arg->stop)
  		return;
  
  	for (h = 0; h <= 256; h++) {
  		struct route4_bucket *b = head->table[h];
  
  		if (b) {
  			for (h1 = 0; h1 <= 32; h1++) {
  				struct route4_filter *f;
  
  				for (f = b->ht[h1]; 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 route4_dump(struct tcf_proto *tp, unsigned long fh,
  		       struct sk_buff *skb, struct tcmsg *t)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
527
  	struct route4_filter *f = (struct route4_filter *)fh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
528
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
529
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
533
534
535
  	u32 id;
  
  	if (f == NULL)
  		return skb->len;
  
  	t->tcm_handle = f->handle;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
536
537
538
  	nest = nla_nest_start(skb, TCA_OPTIONS);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539

cc7ec456f   Eric Dumazet   net_sched: cleanups
540
541
  	if (!(f->handle & 0x8000)) {
  		id = f->id & 0xFF;
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
542
  		NLA_PUT_U32(skb, TCA_ROUTE4_TO, id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
  	}
cc7ec456f   Eric Dumazet   net_sched: cleanups
544
545
  	if (f->handle & 0x80000000) {
  		if ((f->handle >> 16) != 0xFFFF)
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
546
  			NLA_PUT_U32(skb, TCA_ROUTE4_IIF, f->iif);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  	} else {
cc7ec456f   Eric Dumazet   net_sched: cleanups
548
  		id = f->id >> 16;
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
549
  		NLA_PUT_U32(skb, TCA_ROUTE4_FROM, id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
  	}
  	if (f->res.classid)
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
552
  		NLA_PUT_U32(skb, TCA_ROUTE4_CLASSID, f->res.classid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  
  	if (tcf_exts_dump(skb, &f->exts, &route_ext_map) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
555
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
557
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
  
  	if (tcf_exts_dump_stats(skb, &f->exts, &route_ext_map) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
560
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
  
  	return skb->len;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
563
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
564
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
566
  	return -1;
  }
2eb9d75c7   Patrick McHardy   [NET_SCHED]: mark...
567
  static struct tcf_proto_ops cls_route4_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  	.kind		=	"route",
  	.classify	=	route4_classify,
  	.init		=	route4_init,
  	.destroy	=	route4_destroy,
  	.get		=	route4_get,
  	.put		=	route4_put,
  	.change		=	route4_change,
  	.delete		=	route4_delete,
  	.walk		=	route4_walk,
  	.dump		=	route4_dump,
  	.owner		=	THIS_MODULE,
  };
  
  static int __init init_route4(void)
  {
  	return register_tcf_proto_ops(&cls_route4_ops);
  }
  
  static void __exit exit_route4(void)
  {
  	unregister_tcf_proto_ops(&cls_route4_ops);
  }
  
  module_init(init_route4)
  module_exit(exit_route4)
  MODULE_LICENSE("GPL");