Blame view

net/sched/act_api.c 23.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * net/sched/act_api.c	Packet action API.
   *
   *		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.
   *
   * Author:	Jamal Hadi Salim
   *
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/errno.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/skbuff.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  #include <linux/init.h>
  #include <linux/kmod.h>
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
21
  #include <linux/err.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
22
  #include <linux/module.h>
b854272b3   Denis V. Lunev   [NET]: Modify all...
23
24
  #include <net/net_namespace.h>
  #include <net/sock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  #include <net/sch_generic.h>
  #include <net/act_api.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
27
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
29
30
31
32
33
34
35
36
37
38
  void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
  {
  	unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
  	struct tcf_common **p1p;
  
  	for (p1p = &hinfo->htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
  		if (*p1p == p) {
  			write_lock_bh(hinfo->lock);
  			*p1p = p->tcfc_next;
  			write_unlock_bh(hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
39
40
  			gen_kill_estimator(&p->tcfc_bstats,
  					   &p->tcfc_rate_est);
c7de2cf05   Eric Dumazet   pkt_sched: gen_ki...
41
42
43
44
  			/*
  			 * gen_estimator est_timer() might access p->tcfc_lock
  			 * or bstats, wait a RCU grace period before freeing p
  			 */
f5c8593c1   Lai Jiangshan   net,rcu: convert ...
45
  			kfree_rcu(p, tcfc_rcu);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
46
47
48
  			return;
  		}
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
49
  	WARN_ON(1);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
50
51
52
53
54
55
56
57
58
59
60
61
62
  }
  EXPORT_SYMBOL(tcf_hash_destroy);
  
  int tcf_hash_release(struct tcf_common *p, int bind,
  		     struct tcf_hashinfo *hinfo)
  {
  	int ret = 0;
  
  	if (p) {
  		if (bind)
  			p->tcfc_bindcnt--;
  
  		p->tcfc_refcnt--;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
63
  		if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) {
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
64
65
66
67
68
69
70
71
72
73
74
75
  			tcf_hash_destroy(p, hinfo);
  			ret = 1;
  		}
  	}
  	return ret;
  }
  EXPORT_SYMBOL(tcf_hash_release);
  
  static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
  			   struct tc_action *a, struct tcf_hashinfo *hinfo)
  {
  	struct tcf_common *p;
cc7ec456f   Eric Dumazet   net_sched: cleanups
76
  	int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
77
  	struct nlattr *nest;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
78

e1e992e52   Jamal Hadi Salim   [NET_SCHED] prote...
79
  	read_lock_bh(hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
80
81
82
83
84
85
86
87
88
89
90
91
  
  	s_i = cb->args[0];
  
  	for (i = 0; i < (hinfo->hmask + 1); i++) {
  		p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
  
  		for (; p; p = p->tcfc_next) {
  			index++;
  			if (index < s_i)
  				continue;
  			a->priv = p;
  			a->order = n_i;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
92
93
94
95
  
  			nest = nla_nest_start(skb, a->order);
  			if (nest == NULL)
  				goto nla_put_failure;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
96
97
98
  			err = tcf_action_dump_1(skb, a, 0, 0);
  			if (err < 0) {
  				index--;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
99
  				nlmsg_trim(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
100
101
  				goto done;
  			}
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
102
  			nla_nest_end(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
103
104
105
106
107
108
  			n_i++;
  			if (n_i >= TCA_ACT_MAX_PRIO)
  				goto done;
  		}
  	}
  done:
e1e992e52   Jamal Hadi Salim   [NET_SCHED] prote...
109
  	read_unlock_bh(hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
110
111
112
  	if (n_i)
  		cb->args[0] += n_i;
  	return n_i;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
113
  nla_put_failure:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
114
  	nla_nest_cancel(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
115
116
117
118
119
120
121
  	goto done;
  }
  
  static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a,
  			  struct tcf_hashinfo *hinfo)
  {
  	struct tcf_common *p, *s_p;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
122
  	struct nlattr *nest;
cc7ec456f   Eric Dumazet   net_sched: cleanups
123
  	int i = 0, n_i = 0;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
124

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
125
126
127
  	nest = nla_nest_start(skb, a->order);
  	if (nest == NULL)
  		goto nla_put_failure;
57e1c487a   Patrick McHardy   [NET_SCHED]: Use ...
128
  	NLA_PUT_STRING(skb, TCA_KIND, a->ops->kind);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
129
130
131
132
133
134
  	for (i = 0; i < (hinfo->hmask + 1); i++) {
  		p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
  
  		while (p != NULL) {
  			s_p = p->tcfc_next;
  			if (ACT_P_DELETED == tcf_hash_release(p, 0, hinfo))
cc7ec456f   Eric Dumazet   net_sched: cleanups
135
  				module_put(a->ops->owner);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
136
137
138
139
  			n_i++;
  			p = s_p;
  		}
  	}
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
140
  	NLA_PUT_U32(skb, TCA_FCNT, n_i);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
141
  	nla_nest_end(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
142
143
  
  	return n_i;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
144
  nla_put_failure:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
145
  	nla_nest_cancel(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
146
147
148
149
150
151
152
153
154
155
156
157
158
  	return -EINVAL;
  }
  
  int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb,
  		       int type, struct tc_action *a)
  {
  	struct tcf_hashinfo *hinfo = a->ops->hinfo;
  
  	if (type == RTM_DELACTION) {
  		return tcf_del_walker(skb, a, hinfo);
  	} else if (type == RTM_GETACTION) {
  		return tcf_dump_walker(skb, cb, a, hinfo);
  	} else {
6ff9c3644   stephen hemminger   net sched: printk...
159
160
  		WARN(1, "tcf_generic_walker: unknown action %d
  ", type);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
161
162
163
164
165
166
167
168
  		return -EINVAL;
  	}
  }
  EXPORT_SYMBOL(tcf_generic_walker);
  
  struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo)
  {
  	struct tcf_common *p;
e1e992e52   Jamal Hadi Salim   [NET_SCHED] prote...
169
  	read_lock_bh(hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
170
171
172
173
174
  	for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p;
  	     p = p->tcfc_next) {
  		if (p->tcfc_index == index)
  			break;
  	}
e1e992e52   Jamal Hadi Salim   [NET_SCHED] prote...
175
  	read_unlock_bh(hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  
  	return p;
  }
  EXPORT_SYMBOL(tcf_hash_lookup);
  
  u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo)
  {
  	u32 val = *idx_gen;
  
  	do {
  		if (++val == 0)
  			val = 1;
  	} while (tcf_hash_lookup(val, hinfo));
  
  	return (*idx_gen = val);
  }
  EXPORT_SYMBOL(tcf_hash_new_index);
  
  int tcf_hash_search(struct tc_action *a, u32 index)
  {
  	struct tcf_hashinfo *hinfo = a->ops->hinfo;
  	struct tcf_common *p = tcf_hash_lookup(index, hinfo);
  
  	if (p) {
  		a->priv = p;
  		return 1;
  	}
  	return 0;
  }
  EXPORT_SYMBOL(tcf_hash_search);
  
  struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind,
  				  struct tcf_hashinfo *hinfo)
  {
  	struct tcf_common *p = NULL;
  	if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) {
76aab2c1e   Jamal Hadi Salim   pkt_sched: Fix ac...
212
  		if (bind)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
213
  			p->tcfc_bindcnt++;
76aab2c1e   Jamal Hadi Salim   pkt_sched: Fix ac...
214
  		p->tcfc_refcnt++;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
215
216
217
218
219
  		a->priv = p;
  	}
  	return p;
  }
  EXPORT_SYMBOL(tcf_hash_check);
0e991ec6a   Stephen Hemminger   tc: propogate err...
220
221
222
  struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est,
  				   struct tc_action *a, int size, int bind,
  				   u32 *idx_gen, struct tcf_hashinfo *hinfo)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
223
224
225
226
  {
  	struct tcf_common *p = kzalloc(size, GFP_KERNEL);
  
  	if (unlikely(!p))
0e991ec6a   Stephen Hemminger   tc: propogate err...
227
  		return ERR_PTR(-ENOMEM);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
228
229
230
231
232
  	p->tcfc_refcnt = 1;
  	if (bind)
  		p->tcfc_bindcnt = 1;
  
  	spin_lock_init(&p->tcfc_lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
233
234
235
  	p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo);
  	p->tcfc_tm.install = jiffies;
  	p->tcfc_tm.lastuse = jiffies;
0e991ec6a   Stephen Hemminger   tc: propogate err...
236
237
238
239
240
241
242
243
  	if (est) {
  		int err = gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
  					    &p->tcfc_lock, est);
  		if (err) {
  			kfree(p);
  			return ERR_PTR(err);
  		}
  	}
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  	a->priv = (void *) p;
  	return p;
  }
  EXPORT_SYMBOL(tcf_hash_create);
  
  void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo)
  {
  	unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
  
  	write_lock_bh(hinfo->lock);
  	p->tcfc_next = hinfo->htab[h];
  	hinfo->htab[h] = p;
  	write_unlock_bh(hinfo->lock);
  }
  EXPORT_SYMBOL(tcf_hash_insert);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  
  static struct tc_action_ops *act_base = NULL;
  static DEFINE_RWLOCK(act_mod_lock);
  
  int tcf_register_action(struct tc_action_ops *act)
  {
  	struct tc_action_ops *a, **ap;
  
  	write_lock(&act_mod_lock);
  	for (ap = &act_base; (a = *ap) != NULL; ap = &a->next) {
  		if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
  			write_unlock(&act_mod_lock);
  			return -EEXIST;
  		}
  	}
  	act->next = NULL;
  	*ap = act;
  	write_unlock(&act_mod_lock);
  	return 0;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
279
  EXPORT_SYMBOL(tcf_register_action);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  
  int tcf_unregister_action(struct tc_action_ops *act)
  {
  	struct tc_action_ops *a, **ap;
  	int err = -ENOENT;
  
  	write_lock(&act_mod_lock);
  	for (ap = &act_base; (a = *ap) != NULL; ap = &a->next)
  		if (a == act)
  			break;
  	if (a) {
  		*ap = a->next;
  		a->next = NULL;
  		err = 0;
  	}
  	write_unlock(&act_mod_lock);
  	return err;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
298
  EXPORT_SYMBOL(tcf_unregister_action);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  
  /* lookup by name */
  static struct tc_action_ops *tc_lookup_action_n(char *kind)
  {
  	struct tc_action_ops *a = NULL;
  
  	if (kind) {
  		read_lock(&act_mod_lock);
  		for (a = act_base; a; a = a->next) {
  			if (strcmp(kind, a->kind) == 0) {
  				if (!try_module_get(a->owner)) {
  					read_unlock(&act_mod_lock);
  					return NULL;
  				}
  				break;
  			}
  		}
  		read_unlock(&act_mod_lock);
  	}
  	return a;
  }
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
320
321
  /* lookup by nlattr */
  static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
  {
  	struct tc_action_ops *a = NULL;
  
  	if (kind) {
  		read_lock(&act_mod_lock);
  		for (a = act_base; a; a = a->next) {
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
328
  			if (nla_strcmp(kind, a->kind) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  				if (!try_module_get(a->owner)) {
  					read_unlock(&act_mod_lock);
  					return NULL;
  				}
  				break;
  			}
  		}
  		read_unlock(&act_mod_lock);
  	}
  	return a;
  }
  
  #if 0
  /* lookup by id */
  static struct tc_action_ops *tc_lookup_action_id(u32 type)
  {
  	struct tc_action_ops *a = NULL;
  
  	if (type) {
  		read_lock(&act_mod_lock);
  		for (a = act_base; a; a = a->next) {
  			if (a->type == type) {
  				if (!try_module_get(a->owner)) {
  					read_unlock(&act_mod_lock);
  					return NULL;
  				}
  				break;
  			}
  		}
  		read_unlock(&act_mod_lock);
  	}
  	return a;
  }
  #endif
dc7f9f6e8   Eric Dumazet   net: sched: const...
363
  int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
364
  		    struct tcf_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  {
dc7f9f6e8   Eric Dumazet   net: sched: const...
366
  	const struct tc_action *a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
  	int ret = -1;
  
  	if (skb->tc_verd & TC_NCLS) {
  		skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
374
375
376
  		ret = TC_ACT_OK;
  		goto exec_done;
  	}
  	while ((a = act) != NULL) {
  repeat:
  		if (a->ops && a->ops->act) {
f43c5a0df   Patrick McHardy   [PKT_SCHED]: Conv...
377
  			ret = a->ops->act(skb, a, res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
380
381
382
  			if (TC_MUNGED & skb->tc_verd) {
  				/* copied already, allow trampling */
  				skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
  				skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
  			if (ret == TC_ACT_REPEAT)
  				goto repeat;	/* we need a ttl - JHS */
14d50e78f   J Hadi Salim   [PKT_SCHED]: Acti...
385
386
  			if (ret != TC_ACT_PIPE)
  				goto exec_done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
  		}
  		act = a->next;
  	}
  exec_done:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
  	return ret;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
393
  EXPORT_SYMBOL(tcf_action_exec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
399
400
  
  void tcf_action_destroy(struct tc_action *act, int bind)
  {
  	struct tc_action *a;
  
  	for (a = act; a; a = act) {
  		if (a->ops && a->ops->cleanup) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
  			if (a->ops->cleanup(a, bind) == ACT_P_DELETED)
  				module_put(a->ops->owner);
  			act = act->next;
  			kfree(a);
6ff9c3644   stephen hemminger   net sched: printk...
405
406
407
408
  		} else {
  			/*FIXME: Remove later - catch insertion bugs*/
  			WARN(1, "tcf_action_destroy: BUG? destroying NULL ops
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  			act = act->next;
  			kfree(a);
  		}
  	}
  }
  
  int
  tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
  {
  	int err = -EINVAL;
  
  	if (a->ops == NULL || a->ops->dump == NULL)
  		return err;
  	return a->ops->dump(skb, a, bind, ref);
  }
  
  int
  tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
  {
  	int err = -EINVAL;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
429
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
430
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
  
  	if (a->ops == NULL || a->ops->dump == NULL)
  		return err;
57e1c487a   Patrick McHardy   [NET_SCHED]: Use ...
434
  	NLA_PUT_STRING(skb, TCA_KIND, a->ops->kind);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  	if (tcf_action_copy_stats(skb, a, 0))
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
436
  		goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
437
438
439
  	nest = nla_nest_start(skb, TCA_OPTIONS);
  	if (nest == NULL)
  		goto nla_put_failure;
cc7ec456f   Eric Dumazet   net_sched: cleanups
440
441
  	err = tcf_action_dump_old(skb, a, bind, ref);
  	if (err > 0) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
442
  		nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
  		return err;
  	}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
445
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
446
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
  	return -1;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
449
  EXPORT_SYMBOL(tcf_action_dump_1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
452
453
454
455
  
  int
  tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref)
  {
  	struct tc_action *a;
  	int err = -EINVAL;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
456
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  
  	while ((a = act) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
  		act = a->next;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
460
461
462
  		nest = nla_nest_start(skb, a->order);
  		if (nest == NULL)
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
  		err = tcf_action_dump_1(skb, a, bind, ref);
  		if (err < 0)
4fe683f50   Thomas Graf   [PKT_SCHED]: Fix ...
465
  			goto errout;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
466
  		nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
  	}
  
  	return 0;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
470
  nla_put_failure:
4fe683f50   Thomas Graf   [PKT_SCHED]: Fix ...
471
472
  	err = -EINVAL;
  errout:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
473
  	nla_nest_cancel(skb, nest);
4fe683f50   Thomas Graf   [PKT_SCHED]: Fix ...
474
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
  }
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
476
  struct tc_action *tcf_action_init_1(struct nlattr *nla, struct nlattr *est,
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
477
  				    char *name, int ovr, int bind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
481
  {
  	struct tc_action *a;
  	struct tc_action_ops *a_o;
  	char act_name[IFNAMSIZ];
cc7ec456f   Eric Dumazet   net_sched: cleanups
482
  	struct nlattr *tb[TCA_ACT_MAX + 1];
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
483
  	struct nlattr *kind;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
484
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  	if (name == NULL) {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
487
488
  		err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
  		if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  			goto err_out;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
490
  		err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
491
  		kind = tb[TCA_ACT_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
  		if (kind == NULL)
  			goto err_out;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
494
  		if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
  			goto err_out;
  	} else {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
497
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
  		if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ)
  			goto err_out;
  	}
  
  	a_o = tc_lookup_action_n(act_name);
  	if (a_o == NULL) {
95a5afca4   Johannes Berg   net: Remove CONFI...
504
  #ifdef CONFIG_MODULES
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
  		rtnl_unlock();
4bba39259   Patrick McHardy   [PKT_SCHED]: Pref...
506
  		request_module("act_%s", act_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
509
510
511
512
513
514
515
516
517
  		rtnl_lock();
  
  		a_o = tc_lookup_action_n(act_name);
  
  		/* We dropped the RTNL semaphore in order to
  		 * perform the module load.  So, even if we
  		 * succeeded in loading the module we have to
  		 * tell the caller to replay the request.  We
  		 * indicate this using -EAGAIN.
  		 */
  		if (a_o != NULL) {
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
518
  			err = -EAGAIN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
  			goto err_mod;
  		}
  #endif
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
522
  		err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
  		goto err_out;
  	}
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
525
  	err = -ENOMEM;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
526
  	a = kzalloc(sizeof(*a), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
  	if (a == NULL)
  		goto err_mod;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
  
  	/* backward compatibility for policer */
  	if (name == NULL)
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
532
  		err = a_o->init(tb[TCA_ACT_OPTIONS], est, a, ovr, bind);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  	else
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
534
535
  		err = a_o->init(nla, est, a, ovr, bind);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
538
  		goto err_free;
  
  	/* module count goes up only when brand new policy is created
cc7ec456f   Eric Dumazet   net_sched: cleanups
539
540
541
  	 * if it exists and is only bound to in a_o->init() then
  	 * ACT_P_CREATED is not returned (a zero is).
  	 */
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
542
  	if (err != ACT_P_CREATED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
  		module_put(a_o->owner);
  	a->ops = a_o;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
550
551
552
  	return a;
  
  err_free:
  	kfree(a);
  err_mod:
  	module_put(a_o->owner);
  err_out:
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
553
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
  }
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
555
  struct tc_action *tcf_action_init(struct nlattr *nla, struct nlattr *est,
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
556
  				  char *name, int ovr, int bind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
558
  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  	struct tc_action *head = NULL, *act, *act_prev = NULL;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
560
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  	int i;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
562
563
564
  	err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
  	if (err < 0)
  		return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565

7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
566
  	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
567
568
  		act = tcf_action_init_1(tb[i], est, name, ovr, bind);
  		if (IS_ERR(act))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
  			goto err;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
570
  		act->order = i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
575
576
577
578
579
580
581
582
  
  		if (head == NULL)
  			head = act;
  		else
  			act_prev->next = act;
  		act_prev = act;
  	}
  	return head;
  
  err:
  	if (head != NULL)
  		tcf_action_destroy(head, bind);
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
583
  	return act;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
  }
  
  int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
  			  int compat_mode)
  {
  	int err = 0;
  	struct gnet_dump d;
  	struct tcf_act_hdr *h = a->priv;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
592

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
  	if (h == NULL)
  		goto errout;
  
  	/* compat_mode being true specifies a call that is supposed
06fe9fb41   Dirk Hohndel   tree-wide: fix a ...
597
  	 * to add additional backward compatibility statistic TLVs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
  	 */
  	if (compat_mode) {
  		if (a->type == TCA_OLD_COMPAT)
  			err = gnet_stats_start_copy_compat(skb, 0,
4bdf39911   Patrick McHardy   [NET_SCHED]: Remo...
602
  				TCA_STATS, TCA_XSTATS, &h->tcf_lock, &d);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
  		else
  			return 0;
  	} else
  		err = gnet_stats_start_copy(skb, TCA_ACT_STATS,
4bdf39911   Patrick McHardy   [NET_SCHED]: Remo...
607
  					    &h->tcf_lock, &d);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
611
612
613
614
  
  	if (err < 0)
  		goto errout;
  
  	if (a->ops != NULL && a->ops->get_stats != NULL)
  		if (a->ops->get_stats(skb, a) < 0)
  			goto errout;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
615
  	if (gnet_stats_copy_basic(&d, &h->tcf_bstats) < 0 ||
d250a5f90   Eric Dumazet   pkt_sched: gen_es...
616
617
  	    gnet_stats_copy_rate_est(&d, &h->tcf_bstats,
  				     &h->tcf_rate_est) < 0 ||
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
618
  	    gnet_stats_copy_queue(&d, &h->tcf_qstats) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
623
624
625
626
627
628
629
630
631
  		goto errout;
  
  	if (gnet_stats_finish_copy(&d) < 0)
  		goto errout;
  
  	return 0;
  
  errout:
  	return -1;
  }
  
  static int
  tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 pid, u32 seq,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
632
  	     u16 flags, int event, int bind, int ref)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
635
  {
  	struct tcamsg *t;
  	struct nlmsghdr *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
636
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
637
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638

e431b8c00   Jamal Hadi Salim   [NETLINK]: Explic...
639
  	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
  	t = NLMSG_DATA(nlh);
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
642
643
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
644

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
645
646
647
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
  
  	if (tcf_action_dump(skb, a, bind, ref) < 0)
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
650
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
652
  	nla_nest_end(skb, nest);
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
653

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
654
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  	return skb->len;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
656
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
  nlmsg_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
658
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
661
662
  	return -1;
  }
  
  static int
7316ae88c   Tom Goff   net_sched: make t...
663
664
  act_get_notify(struct net *net, u32 pid, struct nlmsghdr *n,
  	       struct tc_action *a, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
  {
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
671
672
673
674
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
  	if (tca_get_fill(skb, a, pid, n->nlmsg_seq, 0, event, 0, 0) <= 0) {
  		kfree_skb(skb);
  		return -EINVAL;
  	}
2942e9005   Thomas Graf   [RTNETLINK]: Use ...
675

7316ae88c   Tom Goff   net_sched: make t...
676
  	return rtnl_unicast(skb, net, pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
  }
  
  static struct tc_action *
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
680
  tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 pid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
682
  	struct nlattr *tb[TCA_ACT_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
  	struct tc_action *a;
  	int index;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
685
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
687
688
  	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
  	if (err < 0)
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
689
  		goto err_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
691
  	err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
692
693
  	if (tb[TCA_ACT_INDEX] == NULL ||
  	    nla_len(tb[TCA_ACT_INDEX]) < sizeof(index))
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
694
  		goto err_out;
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
695
  	index = nla_get_u32(tb[TCA_ACT_INDEX]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696

ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
697
  	err = -ENOMEM;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
698
  	a = kzalloc(sizeof(struct tc_action), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
  	if (a == NULL)
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
700
  		goto err_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701

ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
702
  	err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
703
  	a->ops = tc_lookup_action(tb[TCA_ACT_KIND]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
707
  	if (a->ops == NULL)
  		goto err_free;
  	if (a->ops->lookup == NULL)
  		goto err_mod;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
708
  	err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
  	if (a->ops->lookup(a, index) == 0)
  		goto err_mod;
  
  	module_put(a->ops->owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  	return a;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
714

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
718
  err_mod:
  	module_put(a->ops->owner);
  err_free:
  	kfree(a);
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
719
720
  err_out:
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  }
  
  static void cleanup_a(struct tc_action *act)
  {
  	struct tc_action *a;
  
  	for (a = act; a; a = act) {
  		act = a->next;
  		kfree(a);
  	}
  }
  
  static struct tc_action *create_a(int i)
  {
  	struct tc_action *act;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
736
  	act = kzalloc(sizeof(*act), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  	if (act == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
738
739
  		pr_debug("create_a: failed to alloc!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
  		return NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
  	act->order = i;
  	return act;
  }
7316ae88c   Tom Goff   net_sched: make t...
745
746
  static int tca_action_flush(struct net *net, struct nlattr *nla,
  			    struct nlmsghdr *n, u32 pid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
  {
  	struct sk_buff *skb;
  	unsigned char *b;
  	struct nlmsghdr *nlh;
  	struct tcamsg *t;
  	struct netlink_callback dcb;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
753
  	struct nlattr *nest;
cc7ec456f   Eric Dumazet   net_sched: cleanups
754
  	struct nlattr *tb[TCA_ACT_MAX + 1];
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
755
  	struct nlattr *kind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
  	struct tc_action *a = create_a(0);
36723873b   Jamal Hadi Salim   net-sched: fix Ac...
757
  	int err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
  
  	if (a == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
760
761
  		pr_debug("tca_action_flush: couldnt create tc_action
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
763
764
765
766
  		return err;
  	}
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb) {
6ff9c3644   stephen hemminger   net sched: printk...
767
768
  		pr_debug("tca_action_flush: failed skb alloc
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
  		kfree(a);
36723873b   Jamal Hadi Salim   net-sched: fix Ac...
770
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
  	}
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
772
  	b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
773

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
774
775
  	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
  		goto err_out;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
777
  	err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
778
  	kind = tb[TCA_ACT_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
785
  	a->ops = tc_lookup_action(kind);
  	if (a->ops == NULL)
  		goto err_out;
  
  	nlh = NLMSG_PUT(skb, pid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t));
  	t = NLMSG_DATA(nlh);
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
786
787
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
789
790
791
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
  
  	err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
  	if (err < 0)
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
795
  		goto nla_put_failure;
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
796
797
  	if (err == 0)
  		goto noflush_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
799
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
801
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
804
  	nlh->nlmsg_flags |= NLM_F_ROOT;
  	module_put(a->ops->owner);
  	kfree(a);
cc7ec456f   Eric Dumazet   net_sched: cleanups
805
806
  	err = rtnetlink_send(skb, net, pid, RTNLGRP_TC,
  			     n->nlmsg_flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
808
809
810
  	if (err > 0)
  		return 0;
  
  	return err;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
811
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
  nlmsg_failure:
ebbaeab18   Thomas Graf   [PKT_SCHED]: act_...
813
  	module_put(a->ops->owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
  err_out:
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
815
  noflush_out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
818
819
820
821
  	kfree_skb(skb);
  	kfree(a);
  	return err;
  }
  
  static int
7316ae88c   Tom Goff   net_sched: make t...
822
823
  tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
  	      u32 pid, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
  {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
825
  	int i, ret;
cc7ec456f   Eric Dumazet   net_sched: cleanups
826
  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
  	struct tc_action *head = NULL, *act, *act_prev = NULL;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
828
829
830
  	ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
  	if (ret < 0)
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831

cc7ec456f   Eric Dumazet   net_sched: cleanups
832
  	if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) {
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
833
  		if (tb[1] != NULL)
7316ae88c   Tom Goff   net_sched: make t...
834
  			return tca_action_flush(net, tb[1], n, pid);
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
835
836
  		else
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  	}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
838
  	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
839
840
841
  		act = tcf_action_get_1(tb[i], n, pid);
  		if (IS_ERR(act)) {
  			ret = PTR_ERR(act);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
  			goto err;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
843
  		}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
844
  		act->order = i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
847
848
849
850
851
852
853
  
  		if (head == NULL)
  			head = act;
  		else
  			act_prev->next = act;
  		act_prev = act;
  	}
  
  	if (event == RTM_GETACTION)
7316ae88c   Tom Goff   net_sched: make t...
854
  		ret = act_get_notify(net, pid, n, head, event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
856
857
858
859
860
861
862
863
864
  	else { /* delete */
  		struct sk_buff *skb;
  
  		skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  		if (!skb) {
  			ret = -ENOBUFS;
  			goto err;
  		}
  
  		if (tca_get_fill(skb, head, pid, n->nlmsg_seq, 0, event,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
865
  				 0, 1) <= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
871
872
  			kfree_skb(skb);
  			ret = -EINVAL;
  			goto err;
  		}
  
  		/* now do the delete */
  		tcf_action_destroy(head, 0);
7316ae88c   Tom Goff   net_sched: make t...
873
  		ret = rtnetlink_send(skb, net, pid, RTNLGRP_TC,
cc7ec456f   Eric Dumazet   net_sched: cleanups
874
  				     n->nlmsg_flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
877
878
879
880
881
882
  		if (ret > 0)
  			return 0;
  		return ret;
  	}
  err:
  	cleanup_a(head);
  	return ret;
  }
7316ae88c   Tom Goff   net_sched: make t...
883
884
  static int tcf_add_notify(struct net *net, struct tc_action *a,
  			  u32 pid, u32 seq, int event, u16 flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
  {
  	struct tcamsg *t;
  	struct nlmsghdr *nlh;
  	struct sk_buff *skb;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
889
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
892
893
894
895
  	unsigned char *b;
  	int err = 0;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
896
  	b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897

e431b8c00   Jamal Hadi Salim   [NETLINK]: Explic...
898
  	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
  	t = NLMSG_DATA(nlh);
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
901
902
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
903
904
905
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
907
  
  	if (tcf_action_dump(skb, a, 0, 0) < 0)
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
908
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
910
  	nla_nest_end(skb, nest);
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
911

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
912
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
ac6d439d2   Patrick McHardy   [NETLINK]: Conver...
913
  	NETLINK_CB(skb).dst_group = RTNLGRP_TC;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
914

cc7ec456f   Eric Dumazet   net_sched: cleanups
915
  	err = rtnetlink_send(skb, net, pid, RTNLGRP_TC, flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
917
918
  	if (err > 0)
  		err = 0;
  	return err;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
919
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
  nlmsg_failure:
f6e57464d   Patrick McHardy   [NET_SCHED]: act_...
921
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
923
  	return -1;
  }
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
924

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
  static int
7316ae88c   Tom Goff   net_sched: make t...
926
927
  tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
  	       u32 pid, int ovr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
931
932
  {
  	int ret = 0;
  	struct tc_action *act;
  	struct tc_action *a;
  	u32 seq = n->nlmsg_seq;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
933
  	act = tcf_action_init(nla, NULL, NULL, ovr, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
935
  	if (act == NULL)
  		goto done;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
936
937
938
939
  	if (IS_ERR(act)) {
  		ret = PTR_ERR(act);
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
941
942
  
  	/* dump then free all the actions after update; inserted policy
  	 * stays intact
cc7ec456f   Eric Dumazet   net_sched: cleanups
943
  	 */
7316ae88c   Tom Goff   net_sched: make t...
944
  	ret = tcf_add_notify(net, act, pid, seq, RTM_NEWACTION, n->nlmsg_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
947
948
949
950
951
952
953
954
  	for (a = act; a; a = act) {
  		act = a->next;
  		kfree(a);
  	}
  done:
  	return ret;
  }
  
  static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
955
  	struct net *net = sock_net(skb->sk);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
956
  	struct nlattr *tca[TCA_ACT_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
958
  	u32 pid = skb ? NETLINK_CB(skb).pid : 0;
  	int ret = 0, ovr = 0;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
959
960
961
962
963
  	ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
  	if (ret < 0)
  		return ret;
  
  	if (tca[TCA_ACT_TAB] == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
964
965
  		pr_notice("tc_ctl_action: received NO action attribs
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
967
  		return -EINVAL;
  	}
cc7ec456f   Eric Dumazet   net_sched: cleanups
968
  	/* n->nlmsg_flags & NLM_F_CREATE */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
971
  	switch (n->nlmsg_type) {
  	case RTM_NEWACTION:
  		/* we are going to assume all other flags
25985edce   Lucas De Marchi   Fix common misspe...
972
  		 * imply create only if it doesn't exist
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
973
974
975
976
  		 * Note that CREATE | EXCL implies that
  		 * but since we want avoid ambiguity (eg when flags
  		 * is zero) then just set this
  		 */
cc7ec456f   Eric Dumazet   net_sched: cleanups
977
  		if (n->nlmsg_flags & NLM_F_REPLACE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
979
  			ovr = 1;
  replay:
7316ae88c   Tom Goff   net_sched: make t...
980
  		ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, pid, ovr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
984
  		if (ret == -EAGAIN)
  			goto replay;
  		break;
  	case RTM_DELACTION:
7316ae88c   Tom Goff   net_sched: make t...
985
986
  		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
  				    pid, RTM_DELACTION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987
988
  		break;
  	case RTM_GETACTION:
7316ae88c   Tom Goff   net_sched: make t...
989
990
  		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
  				    pid, RTM_GETACTION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
991
992
993
994
995
996
997
  		break;
  	default:
  		BUG();
  	}
  
  	return ret;
  }
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
998
  static struct nlattr *
3a6c2b419   Patrick McHardy   netlink: constify...
999
  find_dump_kind(const struct nlmsghdr *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1001
  	struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1];
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1002
1003
1004
  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
  	struct nlattr *nla[TCAA_MAX + 1];
  	struct nlattr *kind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005

c96c9471d   Patrick McHardy   [NET_SCHED]: act_...
1006
  	if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, NULL) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
  		return NULL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1008
  	tb1 = nla[TCA_ACT_TAB];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
1010
  	if (tb1 == NULL)
  		return NULL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1011
1012
  	if (nla_parse(tb, TCA_ACT_MAX_PRIO, nla_data(tb1),
  		      NLMSG_ALIGN(nla_len(tb1)), NULL) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014

6d834e04e   Patrick McHardy   [NET_SCHED]: act_...
1015
1016
1017
1018
  	if (tb[1] == NULL)
  		return NULL;
  	if (nla_parse(tb2, TCA_ACT_MAX, nla_data(tb[1]),
  		      nla_len(tb[1]), NULL) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
  		return NULL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1020
  	kind = tb2[TCA_ACT_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021

26dab8930   Thomas Graf   [PKT_SCHED]: Fix ...
1022
  	return kind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
1025
1026
1027
1028
  }
  
  static int
  tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
  {
  	struct nlmsghdr *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1029
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1030
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
1034
  	struct tc_action_ops *a_o;
  	struct tc_action a;
  	int ret = 0;
  	struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1035
  	struct nlattr *kind = find_dump_kind(cb->nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036
1037
  
  	if (kind == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
1038
1039
  		pr_info("tc_dump_action: action bad kind
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
1041
  		return 0;
  	}
26dab8930   Thomas Graf   [PKT_SCHED]: Fix ...
1042
  	a_o = tc_lookup_action(kind);
cc7ec456f   Eric Dumazet   net_sched: cleanups
1043
  	if (a_o == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
1046
1047
1048
1049
  
  	memset(&a, 0, sizeof(struct tc_action));
  	a.ops = a_o;
  
  	if (a_o->walk == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
1050
1051
1052
  		WARN(1, "tc_dump_action: %s !capable of dumping table
  ",
  		     a_o->kind);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1053
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
1055
1056
  	}
  
  	nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1057
  			cb->nlh->nlmsg_type, sizeof(*t));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
1059
  	t = NLMSG_DATA(nlh);
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
1060
1061
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1063
1064
1065
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066
1067
1068
  
  	ret = a_o->walk(skb, cb, RTM_GETACTION, &a);
  	if (ret < 0)
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1069
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
  
  	if (ret > 0) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1072
  		nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073
1074
  		ret = skb->len;
  	} else
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1075
  		nla_nest_cancel(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1077
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
  	if (NETLINK_CB(cb->skb).pid && ret)
  		nlh->nlmsg_flags |= NLM_F_MULTI;
  	module_put(a_o->owner);
  	return skb->len;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
1082
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
1084
  nlmsg_failure:
  	module_put(a_o->owner);
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1085
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
1088
1089
1090
  	return skb->len;
  }
  
  static int __init tc_action_init(void)
  {
c7ac8679b   Greg Rose   rtnetlink: Comput...
1091
1092
1093
1094
  	rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL);
  	rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL);
  	rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
  		      NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1096
1097
1098
1099
  	return 0;
  }
  
  subsys_initcall(tc_action_init);