Blame view

net/sched/ematch.c 14.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  /*
   * net/sched/ematch.c		Extended Match 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:	Thomas Graf <tgraf@suug.ch>
   *
   * ==========================================================================
   *
   * An extended match (ematch) is a small classification tool not worth
   * writing a full classifier for. Ematches can be interconnected to form
   * a logic expression and get attached to classifiers to extend their
   * functionatlity.
   *
   * The userspace part transforms the logic expressions into an array
   * consisting of multiple sequences of interconnected ematches separated
   * by markers. Precedence is implemented by a special ematch kind
   * referencing a sequence beyond the marker of the current sequence
   * causing the current position in the sequence to be pushed onto a stack
   * to allow the current position to be overwritten by the position referenced
   * in the special ematch. Matching continues in the new sequence until a
   * marker is reached causing the position to be restored from the stack.
   *
   * Example:
   *          A AND (B1 OR B2) AND C AND D
   *
   *              ------->-PUSH-------
   *    -->--    /         -->--      \   -->--
   *   /     \  /         /     \      \ /     \
   * +-------+-------+-------+-------+-------+--------+
   * | A AND | B AND | C AND | D END | B1 OR | B2 END |
   * +-------+-------+-------+-------+-------+--------+
   *                    \                      /
   *                     --------<-POP---------
   *
   * where B is a virtual ematch referencing to sequence starting with B1.
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
40
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
   * ==========================================================================
   *
   * How to write an ematch in 60 seconds
   * ------------------------------------
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
45
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
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
   *   1) Provide a matcher function:
   *      static int my_match(struct sk_buff *skb, struct tcf_ematch *m,
   *                          struct tcf_pkt_info *info)
   *      {
   *      	struct mydata *d = (struct mydata *) m->data;
   *
   *      	if (...matching goes here...)
   *      		return 1;
   *      	else
   *      		return 0;
   *      }
   *
   *   2) Fill out a struct tcf_ematch_ops:
   *      static struct tcf_ematch_ops my_ops = {
   *      	.kind = unique id,
   *      	.datalen = sizeof(struct mydata),
   *      	.match = my_match,
   *      	.owner = THIS_MODULE,
   *      };
   *
   *   3) Register/Unregister your ematch:
   *      static int __init init_my_ematch(void)
   *      {
   *      	return tcf_em_register(&my_ops);
   *      }
   *
   *      static void __exit exit_my_ematch(void)
   *      {
4d24b52ac   Alexey Dobriyan   ematch: simpler t...
74
   *      	tcf_em_unregister(&my_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
82
   *      }
   *
   *      module_init(init_my_ematch);
   *      module_exit(exit_my_ematch);
   *
   *   4) By now you should have two more seconds left, barely enough to
   *      open up a beer to watch the compilation going.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
84
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
  #include <linux/rtnetlink.h>
  #include <linux/skbuff.h>
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
  
  static LIST_HEAD(ematch_ops);
  static DEFINE_RWLOCK(ematch_mod_lock);
cc7ec456f   Eric Dumazet   net_sched: cleanups
94
  static struct tcf_ematch_ops *tcf_em_lookup(u16 kind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  {
  	struct tcf_ematch_ops *e = NULL;
  
  	read_lock(&ematch_mod_lock);
  	list_for_each_entry(e, &ematch_ops, link) {
  		if (kind == e->kind) {
  			if (!try_module_get(e->owner))
  				e = NULL;
  			read_unlock(&ematch_mod_lock);
  			return e;
  		}
  	}
  	read_unlock(&ematch_mod_lock);
  
  	return NULL;
  }
  
  /**
   * tcf_em_register - register an extended match
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
114
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
   * @ops: ematch operations lookup table
   *
   * This function must be called by ematches to announce their presence.
   * The given @ops must have kind set to a unique identifier and the
   * callback match() must be implemented. All other callbacks are optional
   * and a fallback implementation is used instead.
   *
   * Returns -EEXISTS if an ematch of the same kind has already registered.
   */
  int tcf_em_register(struct tcf_ematch_ops *ops)
  {
  	int err = -EEXIST;
  	struct tcf_ematch_ops *e;
  
  	if (ops->match == NULL)
  		return -EINVAL;
  
  	write_lock(&ematch_mod_lock);
  	list_for_each_entry(e, &ematch_ops, link)
  		if (ops->kind == e->kind)
  			goto errout;
  
  	list_add_tail(&ops->link, &ematch_ops);
  	err = 0;
  errout:
  	write_unlock(&ematch_mod_lock);
  	return err;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
143
  EXPORT_SYMBOL(tcf_em_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
149
150
151
152
153
154
155
  
  /**
   * tcf_em_unregister - unregster and extended match
   *
   * @ops: ematch operations lookup table
   *
   * This function must be called by ematches to announce their disappearance
   * for examples when the module gets unloaded. The @ops parameter must be
   * the same as the one used for registration.
   *
   * Returns -ENOENT if no matching ematch was found.
   */
4d24b52ac   Alexey Dobriyan   ematch: simpler t...
156
  void tcf_em_unregister(struct tcf_ematch_ops *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  	write_lock(&ematch_mod_lock);
4d24b52ac   Alexey Dobriyan   ematch: simpler t...
159
  	list_del(&ops->link);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  	write_unlock(&ematch_mod_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
162
  EXPORT_SYMBOL(tcf_em_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163

cc7ec456f   Eric Dumazet   net_sched: cleanups
164
165
  static inline struct tcf_ematch *tcf_em_get_match(struct tcf_ematch_tree *tree,
  						  int index)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
  {
  	return &tree->matches[index];
  }
  
  
  static int tcf_em_validate(struct tcf_proto *tp,
  			   struct tcf_ematch_tree_hdr *tree_hdr,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
173
  			   struct tcf_ematch *em, struct nlattr *nla, int idx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
  {
  	int err = -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
176
177
  	struct tcf_ematch_hdr *em_hdr = nla_data(nla);
  	int data_len = nla_len(nla) - sizeof(*em_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  	void *data = (void *) em_hdr + sizeof(*em_hdr);
82a470f11   John Fastabend   net: sched: remov...
179
  	struct net *net = dev_net(qdisc_dev(tp->q));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
184
185
  
  	if (!TCF_EM_REL_VALID(em_hdr->flags))
  		goto errout;
  
  	if (em_hdr->kind == TCF_EM_CONTAINER) {
  		/* Special ematch called "container", carries an index
cc7ec456f   Eric Dumazet   net_sched: cleanups
186
187
  		 * referencing an external ematch sequence.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
197
  		u32 ref;
  
  		if (data_len < sizeof(ref))
  			goto errout;
  		ref = *(u32 *) data;
  
  		if (ref >= tree_hdr->nmatches)
  			goto errout;
  
  		/* We do not allow backward jumps to avoid loops and jumps
cc7ec456f   Eric Dumazet   net_sched: cleanups
198
199
  		 * to our own position are of course illegal.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
  		if (ref <= idx)
  			goto errout;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
202

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
207
208
209
210
  		em->data = ref;
  	} else {
  		/* Note: This lookup will increase the module refcnt
  		 * of the ematch module referenced. In case of a failure,
  		 * a destroy function is called by the underlying layer
  		 * which automatically releases the reference again, therefore
  		 * the module MUST not be given back under any circumstances
  		 * here. Be aware, the destroy function assumes that the
cc7ec456f   Eric Dumazet   net_sched: cleanups
211
212
  		 * module is held if the ops field is non zero.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
  		em->ops = tcf_em_lookup(em_hdr->kind);
  
  		if (em->ops == NULL) {
  			err = -ENOENT;
95a5afca4   Johannes Berg   net: Remove CONFI...
217
  #ifdef CONFIG_MODULES
db3d99c09   Patrick McHardy   [NET_SCHED]: emat...
218
219
220
221
222
223
224
  			__rtnl_unlock();
  			request_module("ematch-kind-%u", em_hdr->kind);
  			rtnl_lock();
  			em->ops = tcf_em_lookup(em_hdr->kind);
  			if (em->ops) {
  				/* We dropped the RTNL mutex in order to
  				 * perform the module load. Tell the caller
cc7ec456f   Eric Dumazet   net_sched: cleanups
225
226
  				 * to replay the request.
  				 */
db3d99c09   Patrick McHardy   [NET_SCHED]: emat...
227
  				module_put(em->ops->owner);
34eea79e2   Ignacy Gawędzki   ematch: Fix auto-...
228
  				em->ops = NULL;
db3d99c09   Patrick McHardy   [NET_SCHED]: emat...
229
230
231
  				err = -EAGAIN;
  			}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
  			goto errout;
  		}
  
  		/* ematch module provides expected length of data, so we
cc7ec456f   Eric Dumazet   net_sched: cleanups
236
237
  		 * can do a basic sanity check.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
  		if (em->ops->datalen && data_len < em->ops->datalen)
  			goto errout;
  
  		if (em->ops->change) {
82a470f11   John Fastabend   net: sched: remov...
242
  			err = em->ops->change(net, data, data_len, em);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
  			if (err < 0)
  				goto errout;
  		} else if (data_len > 0) {
  			/* ematch module doesn't provide an own change
  			 * procedure and expects us to allocate and copy
  			 * the ematch data.
  			 *
  			 * TCF_EM_SIMPLE may be specified stating that the
  			 * data only consists of a u32 integer and the module
  			 * does not expected a memory reference but rather
cc7ec456f   Eric Dumazet   net_sched: cleanups
253
254
  			 * the value carried.
  			 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
  			if (em_hdr->flags & TCF_EM_SIMPLE) {
  				if (data_len < sizeof(u32))
  					goto errout;
  				em->data = *(u32 *) data;
  			} else {
c7b1b2497   Arnaldo Carvalho de Melo   [SCHED]: Use kmem...
260
  				void *v = kmemdup(data, data_len, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
  				if (v == NULL) {
  					err = -ENOBUFS;
  					goto errout;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
  				em->data = (unsigned long) v;
  			}
  		}
  	}
  
  	em->matchid = em_hdr->matchid;
  	em->flags = em_hdr->flags;
  	em->datalen = data_len;
82a470f11   John Fastabend   net: sched: remov...
273
  	em->net = net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
  
  	err = 0;
  errout:
  	return err;
  }
7a9c1bd40   Patrick McHardy   [NET_SCHED]: Use ...
279
280
281
282
  static const struct nla_policy em_policy[TCA_EMATCH_TREE_MAX + 1] = {
  	[TCA_EMATCH_TREE_HDR]	= { .len = sizeof(struct tcf_ematch_tree_hdr) },
  	[TCA_EMATCH_TREE_LIST]	= { .type = NLA_NESTED },
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
  /**
   * tcf_em_tree_validate - validate ematch config TLV and build ematch tree
   *
   * @tp: classifier kind handle
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
287
   * @nla: ematch tree configuration TLV
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
   * @tree: destination ematch tree variable to store the resulting
   *        ematch tree.
   *
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
291
   * This function validates the given configuration TLV @nla and builds an
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
297
298
   * ematch tree in @tree. The resulting tree must later be copied into
   * the private classifier data using tcf_em_tree_change(). You MUST NOT
   * provide the ematch tree variable of the private classifier data directly,
   * the changes would not be locked properly.
   *
   * Returns a negative error code if the configuration TLV contains errors.
   */
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
299
  int tcf_em_tree_validate(struct tcf_proto *tp, struct nlattr *nla,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  			 struct tcf_ematch_tree *tree)
  {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
302
  	int idx, list_len, matches_len, err;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
303
304
  	struct nlattr *tb[TCA_EMATCH_TREE_MAX + 1];
  	struct nlattr *rt_match, *rt_hdr, *rt_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
306
  	struct tcf_ematch_tree_hdr *tree_hdr;
  	struct tcf_ematch *em;
268bcca1e   Stephen Hemminger   [PKT_SCHED] ematc...
307
308
  	memset(tree, 0, sizeof(*tree));
  	if (!nla)
b541ca2c5   Thomas Graf   [PKT_SCHED]: Corr...
309
  		return 0;
b541ca2c5   Thomas Graf   [PKT_SCHED]: Corr...
310

7a9c1bd40   Patrick McHardy   [NET_SCHED]: Use ...
311
  	err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, nla, em_policy);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
312
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  		goto errout;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
314
  	err = -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
315
316
  	rt_hdr = tb[TCA_EMATCH_TREE_HDR];
  	rt_list = tb[TCA_EMATCH_TREE_LIST];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
  
  	if (rt_hdr == NULL || rt_list == NULL)
  		goto errout;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
320
  	tree_hdr = nla_data(rt_hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
  	memcpy(&tree->hdr, tree_hdr, sizeof(*tree_hdr));
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
322
323
  	rt_match = nla_data(rt_list);
  	list_len = nla_len(rt_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
  	matches_len = tree_hdr->nmatches * sizeof(*em);
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
325
  	tree->matches = kzalloc(matches_len, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
  	if (tree->matches == NULL)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328

add93b610   Patrick McHardy   [NET_SCHED]: Conv...
329
  	/* We do not use nla_parse_nested here because the maximum
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  	 * number of attributes is unknown. This saves us the allocation
  	 * for a tb buffer which would serve no purpose at all.
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
332
  	 *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
  	 * The array of rt attributes is parsed in the order as they are
  	 * provided, their type must be incremental from 1 to n. Even
  	 * if it does not serve any real purpose, a failure of sticking
cc7ec456f   Eric Dumazet   net_sched: cleanups
336
337
  	 * to this policy will result in parsing failure.
  	 */
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
338
  	for (idx = 0; nla_ok(rt_match, list_len); idx++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  		err = -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
340
  		if (rt_match->nla_type != (idx + 1))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
  			goto errout_abort;
  
  		if (idx >= tree_hdr->nmatches)
  			goto errout_abort;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
345
  		if (nla_len(rt_match) < sizeof(struct tcf_ematch_hdr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
350
351
352
  			goto errout_abort;
  
  		em = tcf_em_get_match(tree, idx);
  
  		err = tcf_em_validate(tp, tree_hdr, em, rt_match, idx);
  		if (err < 0)
  			goto errout_abort;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
353
  		rt_match = nla_next(rt_match, &list_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
  	}
  
  	/* Check if the number of matches provided by userspace actually
  	 * complies with the array of matches. The number was used for
  	 * the validation of references and a mismatch could lead to
cc7ec456f   Eric Dumazet   net_sched: cleanups
359
360
  	 * undefined references during the matching process.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
368
369
370
  	if (idx != tree_hdr->nmatches) {
  		err = -EINVAL;
  		goto errout_abort;
  	}
  
  	err = 0;
  errout:
  	return err;
  
  errout_abort:
82a470f11   John Fastabend   net: sched: remov...
371
  	tcf_em_tree_destroy(tree);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  	return err;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
374
  EXPORT_SYMBOL(tcf_em_tree_validate);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
380
381
382
383
384
385
  
  /**
   * tcf_em_tree_destroy - destroy an ematch tree
   *
   * @tp: classifier kind handle
   * @tree: ematch tree to be deleted
   *
   * This functions destroys an ematch tree previously created by
   * tcf_em_tree_validate()/tcf_em_tree_change(). You must ensure that
   * the ematch tree is not in use before calling this function.
   */
82a470f11   John Fastabend   net: sched: remov...
386
  void tcf_em_tree_destroy(struct tcf_ematch_tree *tree)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
391
392
393
394
395
396
397
  {
  	int i;
  
  	if (tree->matches == NULL)
  		return;
  
  	for (i = 0; i < tree->hdr.nmatches; i++) {
  		struct tcf_ematch *em = tcf_em_get_match(tree, i);
  
  		if (em->ops) {
  			if (em->ops->destroy)
82a470f11   John Fastabend   net: sched: remov...
398
  				em->ops->destroy(em);
954415e33   Stephen Hemminger   [PKT_SCHED] ematc...
399
  			else if (!tcf_em_is_simple(em))
30ddb159f   David S. Miller   [PKT_SCHED] ematc...
400
  				kfree((void *) em->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
  			module_put(em->ops->owner);
  		}
  	}
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
404

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
  	tree->hdr.nmatches = 0;
  	kfree(tree->matches);
954415e33   Stephen Hemminger   [PKT_SCHED] ematc...
407
  	tree->matches = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
409
  EXPORT_SYMBOL(tcf_em_tree_destroy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  
  /**
   * tcf_em_tree_dump - dump ematch tree into a rtnl message
   *
   * @skb: skb holding the rtnl message
   * @t: ematch tree to be dumped
   * @tlv: TLV type to be used to encapsulate the tree
   *
   * This function dumps a ematch tree into a rtnl message. It is valid to
   * call this function while the ematch tree is in use.
   *
   * Returns -1 if the skb tailroom is insufficient.
   */
  int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv)
  {
  	int i;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
426
  	u8 *tail;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
427
  	struct nlattr *top_start;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
428
  	struct nlattr *list_start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
430
431
432
  	top_start = nla_nest_start(skb, tlv);
  	if (top_start == NULL)
  		goto nla_put_failure;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
433
434
  	if (nla_put(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
436
437
438
  	list_start = nla_nest_start(skb, TCA_EMATCH_TREE_LIST);
  	if (list_start == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
440
  	tail = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  	for (i = 0; i < tree->hdr.nmatches; i++) {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
442
  		struct nlattr *match_start = (struct nlattr *)tail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
446
447
448
  		struct tcf_ematch *em = tcf_em_get_match(tree, i);
  		struct tcf_ematch_hdr em_hdr = {
  			.kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER,
  			.matchid = em->matchid,
  			.flags = em->flags
  		};
1b34ec43c   David S. Miller   pkt_sched: Stop u...
449
450
  		if (nla_put(skb, i + 1, sizeof(em_hdr), &em_hdr))
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
  
  		if (em->ops && em->ops->dump) {
  			if (em->ops->dump(skb, em) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
454
  				goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
  		} else if (tcf_em_is_container(em) || tcf_em_is_simple(em)) {
  			u32 u = em->data;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
457
  			nla_put_nohdr(skb, sizeof(u), &u);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  		} else if (em->datalen > 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
459
  			nla_put_nohdr(skb, em->datalen, (void *) em->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
461
  		tail = skb_tail_pointer(skb);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
462
  		match_start->nla_len = tail - (u8 *)match_start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  	}
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
464
465
  	nla_nest_end(skb, list_start);
  	nla_nest_end(skb, top_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
  
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
468
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
  	return -1;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
471
  EXPORT_SYMBOL(tcf_em_tree_dump);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
  
  static inline int tcf_em_match(struct sk_buff *skb, struct tcf_ematch *em,
  			       struct tcf_pkt_info *info)
  {
  	int r = em->ops->match(skb, em, info);
cc7ec456f   Eric Dumazet   net_sched: cleanups
477

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
  	return tcf_em_is_inverted(em) ? !r : r;
  }
  
  /* Do not use this function directly, use tcf_em_tree_match instead */
  int __tcf_em_tree_match(struct sk_buff *skb, struct tcf_ematch_tree *tree,
  			struct tcf_pkt_info *info)
  {
  	int stackp = 0, match_idx = 0, res = 0;
  	struct tcf_ematch *cur_match;
  	int stack[CONFIG_NET_EMATCH_STACK];
  
  proceed:
  	while (match_idx < tree->hdr.nmatches) {
  		cur_match = tcf_em_get_match(tree, match_idx);
  
  		if (tcf_em_is_container(cur_match)) {
  			if (unlikely(stackp >= CONFIG_NET_EMATCH_STACK))
  				goto stack_overflow;
  
  			stack[stackp++] = match_idx;
  			match_idx = cur_match->data;
  			goto proceed;
  		}
  
  		res = tcf_em_match(skb, cur_match, info);
  
  		if (tcf_em_early_end(cur_match, res))
  			break;
  
  		match_idx++;
  	}
  
  pop_stack:
  	if (stackp > 0) {
  		match_idx = stack[--stackp];
  		cur_match = tcf_em_get_match(tree, match_idx);
34a419d4e   Ignacy Gawędzki   ematch: Fix early...
514
515
  		if (tcf_em_is_inverted(cur_match))
  			res = !res;
17c9c8232   Ignacy Gawędzki   ematch: Fix match...
516
  		if (tcf_em_early_end(cur_match, res)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
  			goto pop_stack;
17c9c8232   Ignacy Gawędzki   ematch: Fix match...
518
  		} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
524
525
526
  			match_idx++;
  			goto proceed;
  		}
  	}
  
  	return res;
  
  stack_overflow:
e87cc4728   Joe Perches   net: Convert net_...
527
528
  	net_warn_ratelimited("tc ematch: local stack overflow, increase NET_EMATCH_STACK
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
  	return -1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  EXPORT_SYMBOL(__tcf_em_tree_match);