Blame view

net/sched/cls_api.c 13.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * net/sched/cls_api.c	Packet classifier 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.
   *
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   * Changes:
   *
   * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <linux/skbuff.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  #include <linux/init.h>
  #include <linux/kmod.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
24
  #include <linux/netlink.h>
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
25
  #include <linux/err.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
26
  #include <linux/slab.h>
b854272b3   Denis V. Lunev   [NET]: Modify all...
27
28
  #include <net/net_namespace.h>
  #include <net/sock.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
29
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  #include <net/pkt_sched.h>
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  /* The list of all installed classifier types */
2eb9d75c7   Patrick McHardy   [NET_SCHED]: mark...
33
  static struct tcf_proto_ops *tcf_proto_base __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
  
  /* Protects list of registered TC modules. It is pure SMP lock. */
  static DEFINE_RWLOCK(cls_mod_lock);
  
  /* Find classifier type by string name */
dc7f9f6e8   Eric Dumazet   net: sched: const...
39
  static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  {
dc7f9f6e8   Eric Dumazet   net: sched: const...
41
  	const struct tcf_proto_ops *t = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
  
  	if (kind) {
  		read_lock(&cls_mod_lock);
  		for (t = tcf_proto_base; t; t = t->next) {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
46
  			if (nla_strcmp(kind, t->kind) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  				if (!try_module_get(t->owner))
  					t = NULL;
  				break;
  			}
  		}
  		read_unlock(&cls_mod_lock);
  	}
  	return t;
  }
  
  /* Register(unregister) new classifier type */
  
  int register_tcf_proto_ops(struct tcf_proto_ops *ops)
  {
  	struct tcf_proto_ops *t, **tp;
  	int rc = -EEXIST;
  
  	write_lock(&cls_mod_lock);
  	for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)
  		if (!strcmp(ops->kind, t->kind))
  			goto out;
  
  	ops->next = NULL;
  	*tp = ops;
  	rc = 0;
  out:
  	write_unlock(&cls_mod_lock);
  	return rc;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
76
  EXPORT_SYMBOL(register_tcf_proto_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
  
  int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
  {
  	struct tcf_proto_ops *t, **tp;
  	int rc = -ENOENT;
  
  	write_lock(&cls_mod_lock);
cc7ec456f   Eric Dumazet   net_sched: cleanups
84
  	for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
90
91
92
93
94
95
  		if (t == ops)
  			break;
  
  	if (!t)
  		goto out;
  	*tp = t->next;
  	rc = 0;
  out:
  	write_unlock(&cls_mod_lock);
  	return rc;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
96
  EXPORT_SYMBOL(unregister_tcf_proto_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97

7316ae88c   Tom Goff   net_sched: make t...
98
99
100
  static int tfilter_notify(struct net *net, struct sk_buff *oskb,
  			  struct nlmsghdr *n, struct tcf_proto *tp,
  			  unsigned long fh, int event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
  
  
  /* Select new prio value from the range, managed by kernel. */
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
104
  static inline u32 tcf_auto_prio(struct tcf_proto *tp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  {
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
106
  	u32 first = TC_H_MAKE(0xC0000000U, 0U);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  
  	if (tp)
cc7ec456f   Eric Dumazet   net_sched: cleanups
109
  		first = tp->prio - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
116
117
  
  	return first;
  }
  
  /* Add/change/delete/get a filter node */
  
  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
118
  	struct net *net = sock_net(skb->sk);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
119
  	struct nlattr *tca[TCA_MAX + 1];
55dbc640c   David S. Miller   pkt_sched: Remove...
120
  	spinlock_t *root_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
126
127
128
129
  	struct tcmsg *t;
  	u32 protocol;
  	u32 prio;
  	u32 nprio;
  	u32 parent;
  	struct net_device *dev;
  	struct Qdisc  *q;
  	struct tcf_proto **back, **chain;
  	struct tcf_proto *tp;
dc7f9f6e8   Eric Dumazet   net: sched: const...
130
  	const struct tcf_proto_ops *tp_ops;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
131
  	const struct Qdisc_class_ops *cops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
  	unsigned long cl;
  	unsigned long fh;
  	int err;
12186be7d   Minoru Usui   net_cls: fix unco...
135
  	int tp_created = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
  
  replay:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
144
145
146
  	t = NLMSG_DATA(n);
  	protocol = TC_H_MIN(t->tcm_info);
  	prio = TC_H_MAJ(t->tcm_info);
  	nprio = prio;
  	parent = t->tcm_parent;
  	cl = 0;
  
  	if (prio == 0) {
  		/* If no priority is given, user wants we allocated it. */
cc7ec456f   Eric Dumazet   net_sched: cleanups
147
148
  		if (n->nlmsg_type != RTM_NEWTFILTER ||
  		    !(n->nlmsg_flags & NLM_F_CREATE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  			return -ENOENT;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
150
  		prio = TC_H_MAKE(0x80000000U, 0U);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
  	}
  
  	/* Find head of filter chain. */
  
  	/* Find link */
7316ae88c   Tom Goff   net_sched: make t...
156
  	dev = __dev_get_by_index(net, t->tcm_ifindex);
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
157
  	if (dev == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  		return -ENODEV;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
159
160
161
  	err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
  	/* Find qdisc */
  	if (!parent) {
af356afa0   Patrick McHardy   net_sched: reintr...
164
  		q = dev->qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  		parent = q->handle;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
166
167
168
169
170
  	} else {
  		q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
  		if (q == NULL)
  			return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
  
  	/* Is it classful? */
cc7ec456f   Eric Dumazet   net_sched: cleanups
173
174
  	cops = q->ops->cl_ops;
  	if (!cops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  		return -EINVAL;
71ebe5e91   Patrick McHardy   net_sched: make c...
176
177
  	if (cops->tcf_chain == NULL)
  		return -EOPNOTSUPP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  	/* Do we search for filter, attached to class? */
  	if (TC_H_MIN(parent)) {
  		cl = cops->get(q, parent);
  		if (cl == 0)
  			return -ENOENT;
  	}
  
  	/* And the last stroke */
  	chain = cops->tcf_chain(q, cl);
  	err = -EINVAL;
  	if (chain == NULL)
  		goto errout;
  
  	/* Check the chain for existence of proto-tcf with this priority */
cc7ec456f   Eric Dumazet   net_sched: cleanups
192
  	for (back = chain; (tp = *back) != NULL; back = &tp->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
  		if (tp->prio >= prio) {
  			if (tp->prio == prio) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
195
196
  				if (!nprio ||
  				    (tp->protocol != protocol && protocol))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
  					goto errout;
  			} else
  				tp = NULL;
  			break;
  		}
  	}
102396ae6   Jarek Poplawski   pkt_sched: Fix lo...
203
  	root_lock = qdisc_root_sleeping_lock(q);
55dbc640c   David S. Miller   pkt_sched: Remove...
204

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
  	if (tp == NULL) {
  		/* Proto-tcf does not exist, create new one */
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
207
  		if (tca[TCA_KIND] == NULL || !protocol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
  			goto errout;
  
  		err = -ENOENT;
cc7ec456f   Eric Dumazet   net_sched: cleanups
211
212
  		if (n->nlmsg_type != RTM_NEWTFILTER ||
  		    !(n->nlmsg_flags & NLM_F_CREATE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
218
  			goto errout;
  
  
  		/* Create new proto tcf */
  
  		err = -ENOBUFS;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
219
220
  		tp = kzalloc(sizeof(*tp), GFP_KERNEL);
  		if (tp == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  			goto errout;
f2df82494   Patrick McHardy   net_sched: cls_ap...
222
  		err = -ENOENT;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
223
  		tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  		if (tp_ops == NULL) {
95a5afca4   Johannes Berg   net: Remove CONFI...
225
  #ifdef CONFIG_MODULES
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
226
  			struct nlattr *kind = tca[TCA_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
  			char name[IFNAMSIZ];
  
  			if (kind != NULL &&
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
230
  			    nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  				rtnl_unlock();
  				request_module("cls_%s", name);
  				rtnl_lock();
  				tp_ops = tcf_proto_lookup_ops(kind);
  				/* We dropped the RTNL semaphore in order to
  				 * perform the module load.  So, even if we
  				 * succeeded in loading the module we have to
  				 * replay the request.  We indicate this using
  				 * -EAGAIN.
  				 */
  				if (tp_ops != NULL) {
  					module_put(tp_ops->owner);
  					err = -EAGAIN;
  				}
  			}
  #endif
  			kfree(tp);
  			goto errout;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
  		tp->ops = tp_ops;
  		tp->protocol = protocol;
d0ab8ff81   Robert Love   net: Only store h...
252
  		tp->prio = nprio ? : TC_H_MAJ(tcf_auto_prio(*back));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
  		tp->q = q;
  		tp->classify = tp_ops->classify;
  		tp->classid = parent;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
256
257
258
  
  		err = tp_ops->init(tp);
  		if (err != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
  			module_put(tp_ops->owner);
  			kfree(tp);
  			goto errout;
  		}
12186be7d   Minoru Usui   net_cls: fix unco...
263
  		tp_created = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264

add93b610   Patrick McHardy   [NET_SCHED]: Conv...
265
  	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
270
271
  		goto errout;
  
  	fh = tp->ops->get(tp, t->tcm_handle);
  
  	if (fh == 0) {
  		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
55dbc640c   David S. Miller   pkt_sched: Remove...
272
  			spin_lock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  			*back = tp->next;
323c04883   Jarek Poplawski   pkt_sched: Fix un...
274
  			spin_unlock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

7316ae88c   Tom Goff   net_sched: make t...
276
  			tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
281
282
  			tcf_destroy(tp);
  			err = 0;
  			goto errout;
  		}
  
  		err = -ENOENT;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
283
284
  		if (n->nlmsg_type != RTM_NEWTFILTER ||
  		    !(n->nlmsg_flags & NLM_F_CREATE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
  			goto errout;
  	} else {
  		switch (n->nlmsg_type) {
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
288
  		case RTM_NEWTFILTER:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  			err = -EEXIST;
12186be7d   Minoru Usui   net_cls: fix unco...
290
291
292
  			if (n->nlmsg_flags & NLM_F_EXCL) {
  				if (tp_created)
  					tcf_destroy(tp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  				goto errout;
12186be7d   Minoru Usui   net_cls: fix unco...
294
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
  			break;
  		case RTM_DELTFILTER:
  			err = tp->ops->delete(tp, fh);
  			if (err == 0)
7316ae88c   Tom Goff   net_sched: make t...
299
  				tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  			goto errout;
  		case RTM_GETTFILTER:
7316ae88c   Tom Goff   net_sched: make t...
302
  			err = tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
309
310
  			goto errout;
  		default:
  			err = -EINVAL;
  			goto errout;
  		}
  	}
  
  	err = tp->ops->change(tp, cl, t->tcm_handle, tca, &fh);
12186be7d   Minoru Usui   net_cls: fix unco...
311
312
313
314
315
316
317
  	if (err == 0) {
  		if (tp_created) {
  			spin_lock_bh(root_lock);
  			tp->next = *back;
  			*back = tp;
  			spin_unlock_bh(root_lock);
  		}
7316ae88c   Tom Goff   net_sched: make t...
318
  		tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
12186be7d   Minoru Usui   net_cls: fix unco...
319
320
321
322
  	} else {
  		if (tp_created)
  			tcf_destroy(tp);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
329
330
331
  
  errout:
  	if (cl)
  		cops->put(q, cl);
  	if (err == -EAGAIN)
  		/* Replay the request. */
  		goto replay;
  	return err;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
332
333
  static int tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp,
  			 unsigned long fh, u32 pid, u32 seq, u16 flags, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
  {
  	struct tcmsg *tcm;
  	struct nlmsghdr  *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
337
  	unsigned char *b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338

e431b8c00   Jamal Hadi Salim   [NETLINK]: Explic...
339
  	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  	tcm = NLMSG_DATA(nlh);
  	tcm->tcm_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
342
  	tcm->tcm__pad1 = 0;
ad61df918   Jiri Pirko   netlink: fix typo...
343
  	tcm->tcm__pad2 = 0;
5ce2d488f   David S. Miller   pkt_sched: Remove...
344
  	tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
  	tcm->tcm_parent = tp->classid;
  	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
57e1c487a   Patrick McHardy   [NET_SCHED]: Use ...
347
  	NLA_PUT_STRING(skb, TCA_KIND, tp->ops->kind);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
  	tcm->tcm_handle = fh;
  	if (RTM_DELTFILTER != event) {
  		tcm->tcm_handle = 0;
  		if (tp->ops->dump && tp->ops->dump(tp, fh, skb, tcm) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
352
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  	}
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
354
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
357
  	return skb->len;
  
  nlmsg_failure:
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
358
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
359
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
  	return -1;
  }
7316ae88c   Tom Goff   net_sched: make t...
362
363
364
  static int tfilter_notify(struct net *net, struct sk_buff *oskb,
  			  struct nlmsghdr *n, struct tcf_proto *tp,
  			  unsigned long fh, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
372
373
374
375
376
  {
  	struct sk_buff *skb;
  	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
  
  	if (tcf_fill_node(skb, tp, fh, pid, n->nlmsg_seq, 0, event) <= 0) {
  		kfree_skb(skb);
  		return -EINVAL;
  	}
7316ae88c   Tom Goff   net_sched: make t...
377
  	return rtnetlink_send(skb, net, pid, RTNLGRP_TC,
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
378
  			      n->nlmsg_flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
380
  struct tcf_dump_args {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
  	struct tcf_walker w;
  	struct sk_buff *skb;
  	struct netlink_callback *cb;
  };
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
385
386
  static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
  			 struct tcf_walker *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  {
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
388
  	struct tcf_dump_args *a = (void *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
  
  	return tcf_fill_node(a->skb, tp, n, NETLINK_CB(a->cb->skb).pid,
  			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER);
  }
bd27a8750   Eric Dumazet   net_cls: Use __de...
393
  /* called with RTNL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
  static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
396
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
401
  	int t;
  	int s_t;
  	struct net_device *dev;
  	struct Qdisc *q;
  	struct tcf_proto *tp, **chain;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
402
  	struct tcmsg *tcm = (struct tcmsg *)NLMSG_DATA(cb->nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  	unsigned long cl = 0;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
404
  	const struct Qdisc_class_ops *cops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
  	struct tcf_dump_args arg;
  
  	if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
  		return skb->len;
cc7ec456f   Eric Dumazet   net_sched: cleanups
409
410
  	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
  	if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  		return skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  	if (!tcm->tcm_parent)
af356afa0   Patrick McHardy   net_sched: reintr...
413
  		q = dev->qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
  	else
  		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
  	if (!q)
  		goto out;
cc7ec456f   Eric Dumazet   net_sched: cleanups
418
419
  	cops = q->ops->cl_ops;
  	if (!cops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  		goto errout;
71ebe5e91   Patrick McHardy   net_sched: make c...
421
422
  	if (cops->tcf_chain == NULL)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
426
427
428
429
430
431
432
  	if (TC_H_MIN(tcm->tcm_parent)) {
  		cl = cops->get(q, tcm->tcm_parent);
  		if (cl == 0)
  			goto errout;
  	}
  	chain = cops->tcf_chain(q, cl);
  	if (chain == NULL)
  		goto errout;
  
  	s_t = cb->args[0];
cc7ec456f   Eric Dumazet   net_sched: cleanups
433
434
435
  	for (tp = *chain, t = 0; tp; tp = tp->next, t++) {
  		if (t < s_t)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
437
438
439
440
441
442
443
444
445
  		if (TC_H_MAJ(tcm->tcm_info) &&
  		    TC_H_MAJ(tcm->tcm_info) != tp->prio)
  			continue;
  		if (TC_H_MIN(tcm->tcm_info) &&
  		    TC_H_MIN(tcm->tcm_info) != tp->protocol)
  			continue;
  		if (t > s_t)
  			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
  		if (cb->args[1] == 0) {
  			if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).pid,
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
446
447
  					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
  					  RTM_NEWTFILTER) <= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
  				break;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
449

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
452
453
454
455
456
457
  			cb->args[1] = 1;
  		}
  		if (tp->ops->walk == NULL)
  			continue;
  		arg.w.fn = tcf_node_dump;
  		arg.skb = skb;
  		arg.cb = cb;
  		arg.w.stop = 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
458
  		arg.w.skip = cb->args[1] - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
  		arg.w.count = 0;
  		tp->ops->walk(tp, &arg.w);
cc7ec456f   Eric Dumazet   net_sched: cleanups
461
  		cb->args[1] = arg.w.count + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
466
467
468
469
470
471
  		if (arg.w.stop)
  			break;
  	}
  
  	cb->args[0] = t;
  
  errout:
  	if (cl)
  		cops->put(q, cl);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
  	return skb->len;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
474
  void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
480
  {
  #ifdef CONFIG_NET_CLS_ACT
  	if (exts->action) {
  		tcf_action_destroy(exts->action, TCA_ACT_UNBIND);
  		exts->action = NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
  #endif
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
483
  EXPORT_SYMBOL(tcf_exts_destroy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484

add93b610   Patrick McHardy   [NET_SCHED]: Conv...
485
486
  int tcf_exts_validate(struct tcf_proto *tp, struct nlattr **tb,
  		  struct nlattr *rate_tlv, struct tcf_exts *exts,
5239008b0   Patrick McHardy   [NET_SCHED]: Cons...
487
  		  const struct tcf_ext_map *map)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
  {
  	memset(exts, 0, sizeof(*exts));
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
490

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
  #ifdef CONFIG_NET_CLS_ACT
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
  		struct tc_action *act;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
494
  		if (map->police && tb[map->police]) {
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
495
  			act = tcf_action_init_1(tb[map->police], rate_tlv,
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
496
  						"police", TCA_ACT_NOREPLACE,
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
497
498
499
  						TCA_ACT_BIND);
  			if (IS_ERR(act))
  				return PTR_ERR(act);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
  
  			act->type = TCA_OLD_COMPAT;
  			exts->action = act;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
503
  		} else if (map->action && tb[map->action]) {
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
504
  			act = tcf_action_init(tb[map->action], rate_tlv, NULL,
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
505
506
507
  					      TCA_ACT_NOREPLACE, TCA_ACT_BIND);
  			if (IS_ERR(act))
  				return PTR_ERR(act);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
  
  			exts->action = act;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  #else
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
513
514
  	if ((map->action && tb[map->action]) ||
  	    (map->police && tb[map->police]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
  		return -EOPNOTSUPP;
  #endif
  
  	return 0;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
520
  EXPORT_SYMBOL(tcf_exts_validate);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521

aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
522
523
  void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
  		     struct tcf_exts *src)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
528
  {
  #ifdef CONFIG_NET_CLS_ACT
  	if (src->action) {
  		struct tc_action *act;
  		tcf_tree_lock(tp);
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
529
530
  		act = dst->action;
  		dst->action = src->action;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
  		tcf_tree_unlock(tp);
  		if (act)
  			tcf_action_destroy(act, TCA_ACT_UNBIND);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
  #endif
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
537
  EXPORT_SYMBOL(tcf_exts_change);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
539
  int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
5239008b0   Patrick McHardy   [NET_SCHED]: Cons...
540
  		  const struct tcf_ext_map *map)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
544
545
546
547
548
  {
  #ifdef CONFIG_NET_CLS_ACT
  	if (map->action && exts->action) {
  		/*
  		 * again for backward compatible mode - we want
  		 * to work with both old and new modes of entering
  		 * tc data even if iproute2  was newer - jhs
  		 */
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
549
  		struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
  
  		if (exts->action->type != TCA_OLD_COMPAT) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
552
553
554
  			nest = nla_nest_start(skb, map->action);
  			if (nest == NULL)
  				goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  			if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
556
  				goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
557
  			nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  		} else if (map->police) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
559
560
561
  			nest = nla_nest_start(skb, map->police);
  			if (nest == NULL)
  				goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  			if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
563
  				goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
564
  			nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
566
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
  #endif
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
569
  nla_put_failure: __attribute__ ((unused))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
  	return -1;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
572
  EXPORT_SYMBOL(tcf_exts_dump);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573

aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
574
575
  
  int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
5239008b0   Patrick McHardy   [NET_SCHED]: Cons...
576
  			const struct tcf_ext_map *map)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
  {
  #ifdef CONFIG_NET_CLS_ACT
  	if (exts->action)
  		if (tcf_action_copy_stats(skb, exts->action, 1) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
581
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
583
  #endif
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
584
  nla_put_failure: __attribute__ ((unused))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
  	return -1;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
587
  EXPORT_SYMBOL(tcf_exts_dump_stats);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
  
  static int __init tc_filter_init(void)
  {
c7ac8679b   Greg Rose   rtnetlink: Comput...
591
592
  	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL);
  	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL);
82623c0d7   Thomas Graf   [PKT_SCHED] cls: ...
593
  	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
c7ac8679b   Greg Rose   rtnetlink: Comput...
594
  		      tc_dump_tfilter, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
  	return 0;
  }
  
  subsys_initcall(tc_filter_init);