Blame view

net/sched/cls_api.c 14 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>
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
24
  #include <linux/err.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
b854272b3   Denis V. Lunev   [NET]: Modify all...
26
27
  #include <net/net_namespace.h>
  #include <net/sock.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
28
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
  #include <net/pkt_sched.h>
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  /* The list of all installed classifier types */
362728746   WANG Cong   net_sched: conver...
32
  static LIST_HEAD(tcf_proto_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
  
  /* 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...
38
  static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  {
dcd760813   Eric Dumazet   net_sched: fix a ...
40
  	const struct tcf_proto_ops *t, *res = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
  
  	if (kind) {
  		read_lock(&cls_mod_lock);
362728746   WANG Cong   net_sched: conver...
44
  		list_for_each_entry(t, &tcf_proto_base, head) {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
45
  			if (nla_strcmp(kind, t->kind) == 0) {
dcd760813   Eric Dumazet   net_sched: fix a ...
46
47
  				if (try_module_get(t->owner))
  					res = t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
  				break;
  			}
  		}
  		read_unlock(&cls_mod_lock);
  	}
dcd760813   Eric Dumazet   net_sched: fix a ...
53
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
59
  }
  
  /* Register(unregister) new classifier type */
  
  int register_tcf_proto_ops(struct tcf_proto_ops *ops)
  {
362728746   WANG Cong   net_sched: conver...
60
  	struct tcf_proto_ops *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
  	int rc = -EEXIST;
  
  	write_lock(&cls_mod_lock);
362728746   WANG Cong   net_sched: conver...
64
  	list_for_each_entry(t, &tcf_proto_base, head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
  		if (!strcmp(ops->kind, t->kind))
  			goto out;
362728746   WANG Cong   net_sched: conver...
67
  	list_add_tail(&ops->head, &tcf_proto_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
  	rc = 0;
  out:
  	write_unlock(&cls_mod_lock);
  	return rc;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
73
  EXPORT_SYMBOL(register_tcf_proto_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
  
  int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
  {
362728746   WANG Cong   net_sched: conver...
77
  	struct tcf_proto_ops *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
  	int rc = -ENOENT;
  
  	write_lock(&cls_mod_lock);
dcd760813   Eric Dumazet   net_sched: fix a ...
81
82
83
84
  	list_for_each_entry(t, &tcf_proto_base, head) {
  		if (t == ops) {
  			list_del(&t->head);
  			rc = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  			break;
dcd760813   Eric Dumazet   net_sched: fix a ...
86
87
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
  	write_unlock(&cls_mod_lock);
  	return rc;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
91
  EXPORT_SYMBOL(unregister_tcf_proto_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

7316ae88c   Tom Goff   net_sched: make t...
93
94
95
  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
96
97
98
  
  
  /* Select new prio value from the range, managed by kernel. */
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
99
  static inline u32 tcf_auto_prio(struct tcf_proto *tp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  {
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
101
  	u32 first = TC_H_MAKE(0xC0000000U, 0U);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
  
  	if (tp)
cc7ec456f   Eric Dumazet   net_sched: cleanups
104
  		first = tp->prio - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
  
  	return first;
  }
  
  /* Add/change/delete/get a filter node */
661d2967b   Thomas Graf   rtnetlink: Remove...
110
  static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
112
  	struct net *net = sock_net(skb->sk);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
113
  	struct nlattr *tca[TCA_MAX + 1];
55dbc640c   David S. Miller   pkt_sched: Remove...
114
  	spinlock_t *root_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
  	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...
124
  	const struct tcf_proto_ops *tp_ops;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
125
  	const struct Qdisc_class_ops *cops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
  	unsigned long cl;
  	unsigned long fh;
  	int err;
12186be7d   Minoru Usui   net_cls: fix unco...
129
  	int tp_created = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

dfc47ef86   Eric W. Biederman   net: Push capable...
131
132
  	if ((n->nlmsg_type != RTM_GETTFILTER) && !capable(CAP_NET_ADMIN))
  		return -EPERM;
de179c8c1   Hong zhi guo   netlink: have len...
133

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  replay:
de179c8c1   Hong zhi guo   netlink: have len...
135
136
137
  	err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
  	if (err < 0)
  		return err;
942b81653   David S. Miller   pkt_sched: cls_ap...
138
  	t = nlmsg_data(n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
142
143
144
145
146
  	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
159
160
161
  		return -ENODEV;
  
  	/* Find qdisc */
  	if (!parent) {
af356afa0   Patrick McHardy   net_sched: reintr...
162
  		q = dev->qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  		parent = q->handle;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
164
165
166
167
168
  	} else {
  		q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
  		if (q == NULL)
  			return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  
  	/* Is it classful? */
cc7ec456f   Eric Dumazet   net_sched: cleanups
171
172
  	cops = q->ops->cl_ops;
  	if (!cops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  		return -EINVAL;
71ebe5e91   Patrick McHardy   net_sched: make c...
174
175
  	if (cops->tcf_chain == NULL)
  		return -EOPNOTSUPP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  	/* 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
190
  	for (back = chain; (tp = *back) != NULL; back = &tp->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  		if (tp->prio >= prio) {
  			if (tp->prio == prio) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
193
194
  				if (!nprio ||
  				    (tp->protocol != protocol && protocol))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
199
200
  					goto errout;
  			} else
  				tp = NULL;
  			break;
  		}
  	}
102396ae6   Jarek Poplawski   pkt_sched: Fix lo...
201
  	root_lock = qdisc_root_sleeping_lock(q);
55dbc640c   David S. Miller   pkt_sched: Remove...
202

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

add93b610   Patrick McHardy   [NET_SCHED]: Conv...
263
  	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
  		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...
270
  			spin_lock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  			*back = tp->next;
323c04883   Jarek Poplawski   pkt_sched: Fix un...
272
  			spin_unlock_bh(root_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273

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

15e473046   Eric W. Biederman   netlink: Rename p...
336
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
942b81653   David S. Miller   pkt_sched: cls_ap...
337
338
339
  	if (!nlh)
  		goto out_nlmsg_trim;
  	tcm = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  	tcm->tcm_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
341
  	tcm->tcm__pad1 = 0;
ad61df918   Jiri Pirko   netlink: fix typo...
342
  	tcm->tcm__pad2 = 0;
5ce2d488f   David S. Miller   pkt_sched: Remove...
343
  	tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  	tcm->tcm_parent = tp->classid;
  	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
1b34ec43c   David S. Miller   pkt_sched: Stop u...
346
347
  	if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
  	tcm->tcm_handle = fh;
  	if (RTM_DELTFILTER != event) {
  		tcm->tcm_handle = 0;
832d1d5bf   WANG Cong   net_sched: add st...
351
  		if (tp->ops->dump && tp->ops->dump(net, 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
  	return skb->len;
942b81653   David S. Miller   pkt_sched: cls_ap...
356
  out_nlmsg_trim:
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
357
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
358
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
  	return -1;
  }
7316ae88c   Tom Goff   net_sched: make t...
361
362
363
  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
364
365
  {
  	struct sk_buff *skb;
15e473046   Eric W. Biederman   netlink: Rename p...
366
  	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
832d1d5bf   WANG Cong   net_sched: add st...
371
  	if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, 0, event) <= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
  		kfree_skb(skb);
  		return -EINVAL;
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
375
  	return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
376
  			      n->nlmsg_flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
378
  struct tcf_dump_args {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
  	struct tcf_walker w;
  	struct sk_buff *skb;
  	struct netlink_callback *cb;
  };
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
383
384
  static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
  			 struct tcf_walker *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  {
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
386
  	struct tcf_dump_args *a = (void *)arg;
832d1d5bf   WANG Cong   net_sched: add st...
387
  	struct net *net = sock_net(a->skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388

832d1d5bf   WANG Cong   net_sched: add st...
389
  	return tcf_fill_node(net, a->skb, tp, n, NETLINK_CB(a->cb->skb).portid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
  			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER);
  }
bd27a8750   Eric Dumazet   net_cls: Use __de...
392
  /* called with RTNL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
  static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
395
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
  	int t;
  	int s_t;
  	struct net_device *dev;
  	struct Qdisc *q;
  	struct tcf_proto *tp, **chain;
942b81653   David S. Miller   pkt_sched: cls_ap...
401
  	struct tcmsg *tcm = nlmsg_data(cb->nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
  	unsigned long cl = 0;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
403
  	const struct Qdisc_class_ops *cops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  	struct tcf_dump_args arg;
573ce260b   Hong zhi guo   net-next: replace...
405
  	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  		return skb->len;
cc7ec456f   Eric Dumazet   net_sched: cleanups
407
408
  	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
  	if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  		return skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  	if (!tcm->tcm_parent)
af356afa0   Patrick McHardy   net_sched: reintr...
411
  		q = dev->qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
  	else
  		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
  	if (!q)
  		goto out;
cc7ec456f   Eric Dumazet   net_sched: cleanups
416
417
  	cops = q->ops->cl_ops;
  	if (!cops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
  		goto errout;
71ebe5e91   Patrick McHardy   net_sched: make c...
419
420
  	if (cops->tcf_chain == NULL)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
  	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
431
432
433
  	for (tp = *chain, t = 0; tp; tp = tp->next, t++) {
  		if (t < s_t)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
439
440
441
442
  		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) {
832d1d5bf   WANG Cong   net_sched: add st...
443
  			if (tcf_fill_node(net, skb, tp, 0, NETLINK_CB(cb->skb).portid,
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
444
445
  					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
  					  RTM_NEWTFILTER) <= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  				break;
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
447

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
  			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
456
  		arg.w.skip = cb->args[1] - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  		arg.w.count = 0;
  		tp->ops->walk(tp, &arg.w);
cc7ec456f   Eric Dumazet   net_sched: cleanups
459
  		cb->args[1] = arg.w.count + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
466
467
468
469
  		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
470
471
  	return skb->len;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
472
  void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
  {
  #ifdef CONFIG_NET_CLS_ACT
33be62715   WANG Cong   net_sched: act: u...
475
476
  	tcf_action_destroy(&exts->actions, TCA_ACT_UNBIND);
  	INIT_LIST_HEAD(&exts->actions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
  #endif
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
479
  EXPORT_SYMBOL(tcf_exts_destroy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480

c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
481
  int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
5da57f422   WANG Cong   net_sched: cls: r...
482
  		  struct nlattr *rate_tlv, struct tcf_exts *exts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
  #ifdef CONFIG_NET_CLS_ACT
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  		struct tc_action *act;
33be62715   WANG Cong   net_sched: act: u...
487
  		INIT_LIST_HEAD(&exts->actions);
5da57f422   WANG Cong   net_sched: cls: r...
488
489
  		if (exts->police && tb[exts->police]) {
  			act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
490
  						"police", TCA_ACT_NOREPLACE,
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
491
492
493
  						TCA_ACT_BIND);
  			if (IS_ERR(act))
  				return PTR_ERR(act);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494

33be62715   WANG Cong   net_sched: act: u...
495
496
  			act->type = exts->type = TCA_OLD_COMPAT;
  			list_add(&act->list, &exts->actions);
5da57f422   WANG Cong   net_sched: cls: r...
497
  		} else if (exts->action && tb[exts->action]) {
33be62715   WANG Cong   net_sched: act: u...
498
  			int err;
5da57f422   WANG Cong   net_sched: cls: r...
499
  			err = tcf_action_init(net, tb[exts->action], rate_tlv,
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
500
  					      NULL, TCA_ACT_NOREPLACE,
33be62715   WANG Cong   net_sched: act: u...
501
502
503
  					      TCA_ACT_BIND, &exts->actions);
  			if (err)
  				return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
  #else
5da57f422   WANG Cong   net_sched: cls: r...
507
508
  	if ((exts->action && tb[exts->action]) ||
  	    (exts->police && tb[exts->police]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
511
512
513
  		return -EOPNOTSUPP;
  #endif
  
  	return 0;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
514
  EXPORT_SYMBOL(tcf_exts_validate);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515

aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
516
517
  void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
  		     struct tcf_exts *src)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
  {
  #ifdef CONFIG_NET_CLS_ACT
33be62715   WANG Cong   net_sched: act: u...
520
521
  	if (!list_empty(&src->actions)) {
  		LIST_HEAD(tmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
  		tcf_tree_lock(tp);
33be62715   WANG Cong   net_sched: act: u...
523
524
  		list_splice_init(&dst->actions, &tmp);
  		list_splice(&src->actions, &dst->actions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
  		tcf_tree_unlock(tp);
33be62715   WANG Cong   net_sched: act: u...
526
  		tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
  #endif
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
530
  EXPORT_SYMBOL(tcf_exts_change);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531

33be62715   WANG Cong   net_sched: act: u...
532
533
  #define tcf_exts_first_act(ext) \
  		list_first_entry(&(exts)->actions, struct tc_action, list)
5da57f422   WANG Cong   net_sched: cls: r...
534
  int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
  {
  #ifdef CONFIG_NET_CLS_ACT
5da57f422   WANG Cong   net_sched: cls: r...
537
  	if (exts->action && !list_empty(&exts->actions)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
540
541
542
  		/*
  		 * 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 ...
543
  		struct nlattr *nest;
33be62715   WANG Cong   net_sched: act: u...
544
  		if (exts->type != TCA_OLD_COMPAT) {
5da57f422   WANG Cong   net_sched: cls: r...
545
  			nest = nla_nest_start(skb, exts->action);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
546
547
  			if (nest == NULL)
  				goto nla_put_failure;
33be62715   WANG Cong   net_sched: act: u...
548
  			if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
549
  				goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
550
  			nla_nest_end(skb, nest);
5da57f422   WANG Cong   net_sched: cls: r...
551
  		} else if (exts->police) {
33be62715   WANG Cong   net_sched: act: u...
552
  			struct tc_action *act = tcf_exts_first_act(exts);
5da57f422   WANG Cong   net_sched: cls: r...
553
  			nest = nla_nest_start(skb, exts->police);
63acd6807   Jamal Hadi Salim   net_sched: Remove...
554
  			if (nest == NULL || !act)
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
555
  				goto nla_put_failure;
33be62715   WANG Cong   net_sched: act: u...
556
  			if (tcf_action_dump_old(skb, act, 0, 0) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
557
  				goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
558
  			nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
  #endif
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
563
  nla_put_failure: __attribute__ ((unused))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
  	return -1;
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
566
  EXPORT_SYMBOL(tcf_exts_dump);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567

aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
568

5da57f422   WANG Cong   net_sched: cls: r...
569
  int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
  {
  #ifdef CONFIG_NET_CLS_ACT
33be62715   WANG Cong   net_sched: act: u...
572
573
574
  	struct tc_action *a = tcf_exts_first_act(exts);
  	if (tcf_action_copy_stats(skb, a, 1) < 0)
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
  #endif
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
  }
aa767bfea   Stephen Hemminger   [PKT_SCHED] net c...
578
  EXPORT_SYMBOL(tcf_exts_dump_stats);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
581
  
  static int __init tc_filter_init(void)
  {
c7ac8679b   Greg Rose   rtnetlink: Comput...
582
583
  	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: ...
584
  	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
c7ac8679b   Greg Rose   rtnetlink: Comput...
585
  		      tc_dump_tfilter, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
589
590
  	return 0;
  }
  
  subsys_initcall(tc_filter_init);