Blame view

net/sched/em_meta.c 21.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
  /*
   * net/sched/em_meta.c	Metadata ematch
   *
   *		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>
   *
   * ==========================================================================
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
12
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
   * 	The metadata ematch compares two meta objects where each object
   * 	represents either a meta value stored in the kernel or a static
   * 	value provided by userspace. The objects are not provided by
   * 	userspace itself but rather a definition providing the information
   * 	to build them. Every object is of a certain type which must be
   * 	equal to the object it is being compared to.
   *
   * 	The definition of a objects conists of the type (meta type), a
   * 	identifier (meta id) and additional type specific information.
   * 	The meta id is either TCF_META_TYPE_VALUE for values provided by
   * 	userspace or a index to the meta operations table consisting of
   * 	function pointers to type specific meta data collectors returning
   * 	the value of the requested meta value.
   *
   * 	         lvalue                                   rvalue
   * 	      +-----------+                           +-----------+
   * 	      | type: INT |                           | type: INT |
261688d01   David S. Miller   [PKT_SCHED]: em_m...
30
   * 	 def  | id: DEV   |                           | id: VALUE |
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
   * 	      | data:     |                           | data: 3   |
   * 	      +-----------+                           +-----------+
   * 	            |                                       |
261688d01   David S. Miller   [PKT_SCHED]: em_m...
34
   * 	            ---> meta_ops[INT][DEV](...)            |
489006296   Thomas Graf   [PKT_SCHED]: Allo...
35
   *	                      |                             |
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
   * 	            -----------                             |
   * 	            V                                       V
   * 	      +-----------+                           +-----------+
   * 	      | type: INT |                           | type: INT |
261688d01   David S. Miller   [PKT_SCHED]: em_m...
40
   * 	 obj  | id: DEV |                             | id: VALUE |
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
49
   * 	      | data: 2   |<--data got filled out     | data: 3   |
   * 	      +-----------+                           +-----------+
   * 	            |                                         |
   * 	            --------------> 2  equals 3 <--------------
   *
   * 	This is a simplified schema, the complexity varies depending
   * 	on the meta type. Obviously, the length of the data must also
   * 	be provided for non-numeric types.
   *
25985edce   Lucas De Marchi   Fix common misspe...
50
   * 	Additionally, type dependent modifiers such as shift operators
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
   * 	or mask may be applied to extend the functionaliy. As of now,
   * 	the variable length type supports shifting the byte string to
   * 	the right, eating up any number of octets and thus supporting
   * 	wildcard interface name comparisons such as "ppp%" matching
   * 	ppp0..9.
   *
   * 	NOTE: Certain meta values depend on other subsystems and are
3a4fa0a25   Robert P. J. Day   Fix misspellings ...
58
   * 	      only available if that subsystem is enabled in the kernel.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
60
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/sched.h>
  #include <linux/string.h>
  #include <linux/skbuff.h>
  #include <linux/random.h>
3113e88c3   Stephen Hemminger   [PKT_SCHED]: vlan...
68
  #include <linux/if_vlan.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
  #include <linux/tc_ematch/tc_em_meta.h>
  #include <net/dst.h>
  #include <net/route.h>
  #include <net/pkt_cls.h>
489006296   Thomas Graf   [PKT_SCHED]: Allo...
73
  #include <net/sock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74

cc7ec456f   Eric Dumazet   net_sched: cleanups
75
  struct meta_obj {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
  	unsigned long		value;
  	unsigned int		len;
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
79
  struct meta_value {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
  	struct tcf_meta_val	hdr;
  	unsigned long		val;
  	unsigned int		len;
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
84
  struct meta_match {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  	struct meta_value	lvalue;
  	struct meta_value	rvalue;
  };
  
  static inline int meta_id(struct meta_value *v)
  {
  	return TCF_META_ID(v->hdr.kind);
  }
  
  static inline int meta_type(struct meta_value *v)
  {
  	return TCF_META_TYPE(v->hdr.kind);
  }
  
  #define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \
  	struct tcf_pkt_info *info, struct meta_value *v, \
  	struct meta_obj *dst, int *err)
  
  /**************************************************************************
   * System status & misc
   **************************************************************************/
  
  META_COLLECTOR(int_random)
  {
  	get_random_bytes(&dst->value, sizeof(dst->value));
  }
  
  static inline unsigned long fixed_loadavg(int load)
  {
  	int rnd_load = load + (FIXED_1/200);
  	int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT;
  
  	return ((rnd_load >> FSHIFT) * 100) + rnd_frac;
  }
  
  META_COLLECTOR(int_loadavg_0)
  {
  	dst->value = fixed_loadavg(avenrun[0]);
  }
  
  META_COLLECTOR(int_loadavg_1)
  {
  	dst->value = fixed_loadavg(avenrun[1]);
  }
  
  META_COLLECTOR(int_loadavg_2)
  {
  	dst->value = fixed_loadavg(avenrun[2]);
  }
  
  /**************************************************************************
   * Device names & indices
   **************************************************************************/
  
  static inline int int_dev(struct net_device *dev, struct meta_obj *dst)
  {
  	if (unlikely(dev == NULL))
  		return -1;
  
  	dst->value = dev->ifindex;
  	return 0;
  }
  
  static inline int var_dev(struct net_device *dev, struct meta_obj *dst)
  {
  	if (unlikely(dev == NULL))
  		return -1;
  
  	dst->value = (unsigned long) dev->name;
  	dst->len = strlen(dev->name);
  	return 0;
  }
  
  META_COLLECTOR(int_dev)
  {
  	*err = int_dev(skb->dev, dst);
  }
  
  META_COLLECTOR(var_dev)
  {
  	*err = var_dev(skb->dev, dst);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
  /**************************************************************************
3113e88c3   Stephen Hemminger   [PKT_SCHED]: vlan...
168
169
170
171
172
   * vlan tag
   **************************************************************************/
  
  META_COLLECTOR(int_vlan_tag)
  {
1a31f2042   Stephen Hemminger   netsched: Allow m...
173
174
175
176
  	unsigned short tag;
  
  	tag = vlan_tx_tag_get(skb);
  	if (!tag && __vlan_get_tag(skb, &tag))
3113e88c3   Stephen Hemminger   [PKT_SCHED]: vlan...
177
178
179
180
181
182
183
184
  		*err = -1;
  	else
  		dst->value = tag;
  }
  
  
  
  /**************************************************************************
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
192
193
194
195
196
197
   * skb attributes
   **************************************************************************/
  
  META_COLLECTOR(int_priority)
  {
  	dst->value = skb->priority;
  }
  
  META_COLLECTOR(int_protocol)
  {
  	/* Let userspace take care of the byte ordering */
  	dst->value = skb->protocol;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  META_COLLECTOR(int_pkttype)
  {
  	dst->value = skb->pkt_type;
  }
  
  META_COLLECTOR(int_pktlen)
  {
  	dst->value = skb->len;
  }
  
  META_COLLECTOR(int_datalen)
  {
  	dst->value = skb->data_len;
  }
  
  META_COLLECTOR(int_maclen)
  {
  	dst->value = skb->mac_len;
  }
c2e3143e3   Stephen Hemminger   tc: add meta matc...
217
218
219
220
  META_COLLECTOR(int_rxhash)
  {
  	dst->value = skb_get_rxhash(skb);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
  /**************************************************************************
   * Netfilter
   **************************************************************************/
82e91ffef   Thomas Graf   [NET]: Turn nfmar...
224
  META_COLLECTOR(int_mark)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  {
82e91ffef   Thomas Graf   [NET]: Turn nfmar...
226
  	dst->value = skb->mark;
7686ee1ad   Patrick McHardy   [EMATCH]: Remove ...
227
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
231
232
233
234
235
236
  
  /**************************************************************************
   * Traffic Control
   **************************************************************************/
  
  META_COLLECTOR(int_tcindex)
  {
  	dst->value = skb->tc_index;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  /**************************************************************************
   * Routing
   **************************************************************************/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
  META_COLLECTOR(int_rtclassid)
  {
adf30907d   Eric Dumazet   net: skb->dst acc...
242
  	if (unlikely(skb_dst(skb) == NULL))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
  		*err = -1;
  	else
c7066f70d   Patrick McHardy   netfilter: fix Kc...
245
  #ifdef CONFIG_IP_ROUTE_CLASSID
adf30907d   Eric Dumazet   net: skb->dst acc...
246
  		dst->value = skb_dst(skb)->tclassid;
7686ee1ad   Patrick McHardy   [EMATCH]: Remove ...
247
248
  #else
  		dst->value = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  #endif
7686ee1ad   Patrick McHardy   [EMATCH]: Remove ...
250
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
  
  META_COLLECTOR(int_rtiif)
  {
511c3f92a   Eric Dumazet   net: skb->rtable ...
254
  	if (unlikely(skb_rtable(skb) == NULL))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
  		*err = -1;
  	else
5e2b61f78   David S. Miller   ipv4: Remove flow...
257
  		dst->value = skb_rtable(skb)->rt_iif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
  }
  
  /**************************************************************************
489006296   Thomas Graf   [PKT_SCHED]: Allo...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
   * Socket Attributes
   **************************************************************************/
  
  #define SKIP_NONLOCAL(skb)			\
  	if (unlikely(skb->sk == NULL)) {	\
  		*err = -1;			\
  		return;				\
  	}
  
  META_COLLECTOR(int_sk_family)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_family;
  }
  
  META_COLLECTOR(int_sk_state)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_state;
  }
  
  META_COLLECTOR(int_sk_reuse)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_reuse;
  }
  
  META_COLLECTOR(int_sk_bound_if)
  {
  	SKIP_NONLOCAL(skb);
  	/* No error if bound_dev_if is 0, legal userspace check */
  	dst->value = skb->sk->sk_bound_dev_if;
  }
  
  META_COLLECTOR(var_sk_bound_if)
  {
  	SKIP_NONLOCAL(skb);
d0075634c   Eric Dumazet   em_meta: avoid on...
298
  	if (skb->sk->sk_bound_dev_if == 0) {
489006296   Thomas Graf   [PKT_SCHED]: Allo...
299
300
  		dst->value = (unsigned long) "any";
  		dst->len = 3;
d0075634c   Eric Dumazet   em_meta: avoid on...
301
  	} else {
489006296   Thomas Graf   [PKT_SCHED]: Allo...
302
  		struct net_device *dev;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
303

d0075634c   Eric Dumazet   em_meta: avoid on...
304
  		rcu_read_lock();
2939e2759   Eric Dumazet   netsched: Allow v...
305
306
  		dev = dev_get_by_index_rcu(sock_net(skb->sk),
  					   skb->sk->sk_bound_dev_if);
489006296   Thomas Graf   [PKT_SCHED]: Allo...
307
  		*err = var_dev(dev, dst);
d0075634c   Eric Dumazet   em_meta: avoid on...
308
309
  		rcu_read_unlock();
  	}
489006296   Thomas Graf   [PKT_SCHED]: Allo...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  }
  
  META_COLLECTOR(int_sk_refcnt)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = atomic_read(&skb->sk->sk_refcnt);
  }
  
  META_COLLECTOR(int_sk_rcvbuf)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_rcvbuf;
  }
  
  META_COLLECTOR(int_sk_shutdown)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_shutdown;
  }
  
  META_COLLECTOR(int_sk_proto)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_protocol;
  }
  
  META_COLLECTOR(int_sk_type)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_type;
  }
  
  META_COLLECTOR(int_sk_rmem_alloc)
  {
  	SKIP_NONLOCAL(skb);
31e6d363a   Eric Dumazet   net: correct off-...
345
  	dst->value = sk_rmem_alloc_get(skb->sk);
489006296   Thomas Graf   [PKT_SCHED]: Allo...
346
347
348
349
350
  }
  
  META_COLLECTOR(int_sk_wmem_alloc)
  {
  	SKIP_NONLOCAL(skb);
31e6d363a   Eric Dumazet   net: correct off-...
351
  	dst->value = sk_wmem_alloc_get(skb->sk);
489006296   Thomas Graf   [PKT_SCHED]: Allo...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  }
  
  META_COLLECTOR(int_sk_omem_alloc)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = atomic_read(&skb->sk->sk_omem_alloc);
  }
  
  META_COLLECTOR(int_sk_rcv_qlen)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_receive_queue.qlen;
  }
  
  META_COLLECTOR(int_sk_snd_qlen)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_write_queue.qlen;
  }
  
  META_COLLECTOR(int_sk_wmem_queued)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_wmem_queued;
  }
  
  META_COLLECTOR(int_sk_fwd_alloc)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_forward_alloc;
  }
  
  META_COLLECTOR(int_sk_sndbuf)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_sndbuf;
  }
  
  META_COLLECTOR(int_sk_alloc)
  {
  	SKIP_NONLOCAL(skb);
e0c563101   stephen hemminger   em_meta: fix spar...
393
  	dst->value = (__force int) skb->sk->sk_allocation;
489006296   Thomas Graf   [PKT_SCHED]: Allo...
394
  }
81c3d5470   Eric Dumazet   [INET]: speedup i...
395
  META_COLLECTOR(int_sk_hash)
489006296   Thomas Graf   [PKT_SCHED]: Allo...
396
397
  {
  	SKIP_NONLOCAL(skb);
81c3d5470   Eric Dumazet   [INET]: speedup i...
398
  	dst->value = skb->sk->sk_hash;
489006296   Thomas Graf   [PKT_SCHED]: Allo...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  }
  
  META_COLLECTOR(int_sk_lingertime)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_lingertime / HZ;
  }
  
  META_COLLECTOR(int_sk_err_qlen)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_error_queue.qlen;
  }
  
  META_COLLECTOR(int_sk_ack_bl)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_ack_backlog;
  }
  
  META_COLLECTOR(int_sk_max_ack_bl)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_max_ack_backlog;
  }
  
  META_COLLECTOR(int_sk_prio)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_priority;
  }
  
  META_COLLECTOR(int_sk_rcvlowat)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_rcvlowat;
  }
  
  META_COLLECTOR(int_sk_rcvtimeo)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_rcvtimeo / HZ;
  }
  
  META_COLLECTOR(int_sk_sndtimeo)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_sndtimeo / HZ;
  }
  
  META_COLLECTOR(int_sk_sendmsg_off)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_sndmsg_off;
  }
  
  META_COLLECTOR(int_sk_write_pend)
  {
  	SKIP_NONLOCAL(skb);
  	dst->value = skb->sk->sk_write_pending;
  }
  
  /**************************************************************************
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
   * Meta value collectors assignment table
   **************************************************************************/
cc7ec456f   Eric Dumazet   net_sched: cleanups
464
  struct meta_ops {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
  	void		(*get)(struct sk_buff *, struct tcf_pkt_info *,
  			       struct meta_value *, struct meta_obj *, int *);
  };
489006296   Thomas Graf   [PKT_SCHED]: Allo...
468
469
  #define META_ID(name) TCF_META_ID_##name
  #define META_FUNC(name) { .get = meta_##name }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
  /* Meta value operations table listing all meta value collectors and
   * assigns them to a type and meta id. */
cc7ec456f   Eric Dumazet   net_sched: cleanups
472
  static struct meta_ops __meta_ops[TCF_META_TYPE_MAX + 1][TCF_META_ID_MAX + 1] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  	[TCF_META_TYPE_VAR] = {
489006296   Thomas Graf   [PKT_SCHED]: Allo...
474
  		[META_ID(DEV)]			= META_FUNC(var_dev),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
475
  		[META_ID(SK_BOUND_IF)] 		= META_FUNC(var_sk_bound_if),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
  	},
  	[TCF_META_TYPE_INT] = {
489006296   Thomas Graf   [PKT_SCHED]: Allo...
478
479
480
481
482
  		[META_ID(RANDOM)]		= META_FUNC(int_random),
  		[META_ID(LOADAVG_0)]		= META_FUNC(int_loadavg_0),
  		[META_ID(LOADAVG_1)]		= META_FUNC(int_loadavg_1),
  		[META_ID(LOADAVG_2)]		= META_FUNC(int_loadavg_2),
  		[META_ID(DEV)]			= META_FUNC(int_dev),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
483
484
  		[META_ID(PRIORITY)]		= META_FUNC(int_priority),
  		[META_ID(PROTOCOL)]		= META_FUNC(int_protocol),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
485
486
487
488
  		[META_ID(PKTTYPE)]		= META_FUNC(int_pkttype),
  		[META_ID(PKTLEN)]		= META_FUNC(int_pktlen),
  		[META_ID(DATALEN)]		= META_FUNC(int_datalen),
  		[META_ID(MACLEN)]		= META_FUNC(int_maclen),
82e91ffef   Thomas Graf   [NET]: Turn nfmar...
489
  		[META_ID(NFMARK)]		= META_FUNC(int_mark),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
490
  		[META_ID(TCINDEX)]		= META_FUNC(int_tcindex),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
491
  		[META_ID(RTCLASSID)]		= META_FUNC(int_rtclassid),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  		[META_ID(RTIIF)]		= META_FUNC(int_rtiif),
  		[META_ID(SK_FAMILY)]		= META_FUNC(int_sk_family),
  		[META_ID(SK_STATE)]		= META_FUNC(int_sk_state),
  		[META_ID(SK_REUSE)]		= META_FUNC(int_sk_reuse),
  		[META_ID(SK_BOUND_IF)]		= META_FUNC(int_sk_bound_if),
  		[META_ID(SK_REFCNT)]		= META_FUNC(int_sk_refcnt),
  		[META_ID(SK_RCVBUF)]		= META_FUNC(int_sk_rcvbuf),
  		[META_ID(SK_SNDBUF)]		= META_FUNC(int_sk_sndbuf),
  		[META_ID(SK_SHUTDOWN)]		= META_FUNC(int_sk_shutdown),
  		[META_ID(SK_PROTO)]		= META_FUNC(int_sk_proto),
  		[META_ID(SK_TYPE)]		= META_FUNC(int_sk_type),
  		[META_ID(SK_RMEM_ALLOC)]	= META_FUNC(int_sk_rmem_alloc),
  		[META_ID(SK_WMEM_ALLOC)]	= META_FUNC(int_sk_wmem_alloc),
  		[META_ID(SK_OMEM_ALLOC)]	= META_FUNC(int_sk_omem_alloc),
  		[META_ID(SK_WMEM_QUEUED)]	= META_FUNC(int_sk_wmem_queued),
  		[META_ID(SK_RCV_QLEN)]		= META_FUNC(int_sk_rcv_qlen),
  		[META_ID(SK_SND_QLEN)]		= META_FUNC(int_sk_snd_qlen),
  		[META_ID(SK_ERR_QLEN)]		= META_FUNC(int_sk_err_qlen),
  		[META_ID(SK_FORWARD_ALLOCS)]	= META_FUNC(int_sk_fwd_alloc),
  		[META_ID(SK_ALLOCS)]		= META_FUNC(int_sk_alloc),
81c3d5470   Eric Dumazet   [INET]: speedup i...
512
  		[META_ID(SK_HASH)]		= META_FUNC(int_sk_hash),
489006296   Thomas Graf   [PKT_SCHED]: Allo...
513
514
515
516
517
518
519
520
521
  		[META_ID(SK_LINGERTIME)]	= META_FUNC(int_sk_lingertime),
  		[META_ID(SK_ACK_BACKLOG)]	= META_FUNC(int_sk_ack_bl),
  		[META_ID(SK_MAX_ACK_BACKLOG)]	= META_FUNC(int_sk_max_ack_bl),
  		[META_ID(SK_PRIO)]		= META_FUNC(int_sk_prio),
  		[META_ID(SK_RCVLOWAT)]		= META_FUNC(int_sk_rcvlowat),
  		[META_ID(SK_RCVTIMEO)]		= META_FUNC(int_sk_rcvtimeo),
  		[META_ID(SK_SNDTIMEO)]		= META_FUNC(int_sk_sndtimeo),
  		[META_ID(SK_SENDMSG_OFF)]	= META_FUNC(int_sk_sendmsg_off),
  		[META_ID(SK_WRITE_PENDING)]	= META_FUNC(int_sk_write_pend),
3113e88c3   Stephen Hemminger   [PKT_SCHED]: vlan...
522
  		[META_ID(VLAN_TAG)]		= META_FUNC(int_vlan_tag),
c2e3143e3   Stephen Hemminger   tc: add meta matc...
523
  		[META_ID(RXHASH)]		= META_FUNC(int_rxhash),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
  	}
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
526
  static inline struct meta_ops *meta_ops(struct meta_value *val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
  {
  	return &__meta_ops[meta_type(val)][meta_id(val)];
  }
  
  /**************************************************************************
   * Type specific operations for TCF_META_TYPE_VAR
   **************************************************************************/
  
  static int meta_var_compare(struct meta_obj *a, struct meta_obj *b)
  {
  	int r = a->len - b->len;
  
  	if (r == 0)
  		r = memcmp((void *) a->value, (void *) b->value, a->len);
  
  	return r;
  }
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
544
  static int meta_var_change(struct meta_value *dst, struct nlattr *nla)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
  {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
546
  	int len = nla_len(nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547

add93b610   Patrick McHardy   [NET_SCHED]: Conv...
548
  	dst->val = (unsigned long)kmemdup(nla_data(nla), len, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
  	if (dst->val == 0UL)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
555
556
  	dst->len = len;
  	return 0;
  }
  
  static void meta_var_destroy(struct meta_value *v)
  {
a51482bde   Jesper Juhl   [NET]: kfree cleanup
557
  	kfree((void *) v->val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
562
563
564
565
566
567
568
569
570
571
  }
  
  static void meta_var_apply_extras(struct meta_value *v,
  				  struct meta_obj *dst)
  {
  	int shift = v->hdr.shift;
  
  	if (shift && shift < dst->len)
  		dst->len -= shift;
  }
  
  static int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
  {
  	if (v->val && v->len)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
572
  		NLA_PUT(skb, tlv, v->len, (void *) v->val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
574
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
577
578
579
580
581
582
583
584
585
586
  	return -1;
  }
  
  /**************************************************************************
   * Type specific operations for TCF_META_TYPE_INT
   **************************************************************************/
  
  static int meta_int_compare(struct meta_obj *a, struct meta_obj *b)
  {
  	/* Let gcc optimize it, the unlikely is not really based on
  	 * some numbers but jump free code for mismatches seems
  	 * more logical. */
98e564055   Thomas Graf   [PKT_SCHED]: Fix ...
587
  	if (unlikely(a->value == b->value))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  		return 0;
98e564055   Thomas Graf   [PKT_SCHED]: Fix ...
589
  	else if (a->value < b->value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
  		return -1;
  	else
  		return 1;
  }
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
594
  static int meta_int_change(struct meta_value *dst, struct nlattr *nla)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
596
597
  	if (nla_len(nla) >= sizeof(unsigned long)) {
  		dst->val = *(unsigned long *) nla_data(nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  		dst->len = sizeof(unsigned long);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
599
  	} else if (nla_len(nla) == sizeof(u32)) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
600
  		dst->val = nla_get_u32(nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
  		dst->len = sizeof(u32);
  	} else
  		return -EINVAL;
  
  	return 0;
  }
  
  static void meta_int_apply_extras(struct meta_value *v,
  				  struct meta_obj *dst)
  {
  	if (v->hdr.shift)
  		dst->value >>= v->hdr.shift;
  
  	if (v->val)
  		dst->value &= v->val;
  }
  
  static int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
  {
  	if (v->len == sizeof(unsigned long))
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
621
  		NLA_PUT(skb, tlv, sizeof(unsigned long), &v->val);
cc7ec456f   Eric Dumazet   net_sched: cleanups
622
  	else if (v->len == sizeof(u32))
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
623
  		NLA_PUT_U32(skb, tlv, v->val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
626
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
629
630
631
632
  	return -1;
  }
  
  /**************************************************************************
   * Type specific operations table
   **************************************************************************/
cc7ec456f   Eric Dumazet   net_sched: cleanups
633
  struct meta_type_ops {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
  	void	(*destroy)(struct meta_value *);
  	int	(*compare)(struct meta_obj *, struct meta_obj *);
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
636
  	int	(*change)(struct meta_value *, struct nlattr *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
  	void	(*apply_extras)(struct meta_value *, struct meta_obj *);
  	int	(*dump)(struct sk_buff *, struct meta_value *, int);
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
640
  static struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX + 1] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
646
647
648
649
650
651
652
653
654
  	[TCF_META_TYPE_VAR] = {
  		.destroy = meta_var_destroy,
  		.compare = meta_var_compare,
  		.change = meta_var_change,
  		.apply_extras = meta_var_apply_extras,
  		.dump = meta_var_dump
  	},
  	[TCF_META_TYPE_INT] = {
  		.compare = meta_int_compare,
  		.change = meta_int_change,
  		.apply_extras = meta_int_apply_extras,
  		.dump = meta_int_dump
  	}
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
655
  static inline struct meta_type_ops *meta_type_ops(struct meta_value *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
660
661
662
  {
  	return &__meta_type_ops[meta_type(v)];
  }
  
  /**************************************************************************
   * Core
   **************************************************************************/
ed7af3b35   Stephen Hemminger   [PKT_SCHED]: dein...
663
664
  static int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info,
  		    struct meta_value *v, struct meta_obj *dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
676
677
678
  {
  	int err = 0;
  
  	if (meta_id(v) == TCF_META_ID_VALUE) {
  		dst->value = v->val;
  		dst->len = v->len;
  		return 0;
  	}
  
  	meta_ops(v)->get(skb, info, v, dst, &err);
  	if (err < 0)
  		return err;
  
  	if (meta_type_ops(v)->apply_extras)
cc7ec456f   Eric Dumazet   net_sched: cleanups
679
  		meta_type_ops(v)->apply_extras(v, dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
  
  	return 0;
  }
  
  static int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m,
  			 struct tcf_pkt_info *info)
  {
  	int r;
  	struct meta_match *meta = (struct meta_match *) m->data;
  	struct meta_obj l_value, r_value;
  
  	if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 ||
  	    meta_get(skb, info, &meta->rvalue, &r_value) < 0)
  		return 0;
  
  	r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value);
  
  	switch (meta->lvalue.hdr.op) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
698
699
700
701
702
703
  	case TCF_EM_OPND_EQ:
  		return !r;
  	case TCF_EM_OPND_LT:
  		return r < 0;
  	case TCF_EM_OPND_GT:
  		return r > 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
707
  	}
  
  	return 0;
  }
ed7af3b35   Stephen Hemminger   [PKT_SCHED]: dein...
708
  static void meta_delete(struct meta_match *meta)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
  {
04f217aca   Stephen Hemminger   [TC]: oops in em_...
710
711
  	if (meta) {
  		struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712

04f217aca   Stephen Hemminger   [TC]: oops in em_...
713
714
715
716
  		if (ops && ops->destroy) {
  			ops->destroy(&meta->lvalue);
  			ops->destroy(&meta->rvalue);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
  	}
  
  	kfree(meta);
  }
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
721
  static inline int meta_change_data(struct meta_value *dst, struct nlattr *nla)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
  {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
723
724
  	if (nla) {
  		if (nla_len(nla) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
  			return -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
726
  		return meta_type_ops(dst)->change(dst, nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
729
730
731
732
733
  	}
  
  	return 0;
  }
  
  static inline int meta_is_supported(struct meta_value *val)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
734
  	return !meta_id(val) || meta_ops(val)->get;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
  }
7a9c1bd40   Patrick McHardy   [NET_SCHED]: Use ...
736
737
738
  static const struct nla_policy meta_policy[TCA_EM_META_MAX + 1] = {
  	[TCA_EM_META_HDR]	= { .len = sizeof(struct tcf_meta_hdr) },
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
741
  static int em_meta_change(struct tcf_proto *tp, void *data, int len,
  			  struct tcf_ematch *m)
  {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
742
  	int err;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
743
  	struct nlattr *tb[TCA_EM_META_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
745
  	struct tcf_meta_hdr *hdr;
  	struct meta_match *meta = NULL;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
746

7a9c1bd40   Patrick McHardy   [NET_SCHED]: Use ...
747
  	err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
748
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
  		goto errout;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
750
  	err = -EINVAL;
7a9c1bd40   Patrick McHardy   [NET_SCHED]: Use ...
751
  	if (tb[TCA_EM_META_HDR] == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
  		goto errout;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
753
  	hdr = nla_data(tb[TCA_EM_META_HDR]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
756
757
758
759
  
  	if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) ||
  	    TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX ||
  	    TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX ||
  	    TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX)
  		goto errout;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
760
  	meta = kzalloc(sizeof(*meta), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
  	if (meta == NULL)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
764
765
766
767
768
769
770
771
  
  	memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
  	memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
  
  	if (!meta_is_supported(&meta->lvalue) ||
  	    !meta_is_supported(&meta->rvalue)) {
  		err = -EOPNOTSUPP;
  		goto errout;
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
772
773
  	if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE]) < 0 ||
  	    meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE]) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
  		goto errout;
  
  	m->datalen = sizeof(*meta);
  	m->data = (unsigned long) meta;
  
  	err = 0;
  errout:
  	if (err && meta)
  		meta_delete(meta);
  	return err;
  }
  
  static void em_meta_destroy(struct tcf_proto *tp, struct tcf_ematch *m)
  {
  	if (m)
  		meta_delete((struct meta_match *) m->data);
  }
  
  static int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em)
  {
  	struct meta_match *meta = (struct meta_match *) em->data;
  	struct tcf_meta_hdr hdr;
  	struct meta_type_ops *ops;
  
  	memset(&hdr, 0, sizeof(hdr));
  	memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left));
  	memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right));
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
801
  	NLA_PUT(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
804
805
  
  	ops = meta_type_ops(&meta->lvalue);
  	if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 ||
  	    ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
806
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
808
  
  	return 0;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
809
  nla_put_failure:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  	return -1;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
811
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
  
  static struct tcf_ematch_ops em_meta_ops = {
  	.kind	  = TCF_EM_META,
  	.change	  = em_meta_change,
  	.match	  = em_meta_match,
  	.destroy  = em_meta_destroy,
  	.dump	  = em_meta_dump,
  	.owner	  = THIS_MODULE,
  	.link	  = LIST_HEAD_INIT(em_meta_ops.link)
  };
  
  static int __init init_em_meta(void)
  {
  	return tcf_em_register(&em_meta_ops);
  }
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
827
  static void __exit exit_em_meta(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
830
831
832
833
834
835
  {
  	tcf_em_unregister(&em_meta_ops);
  }
  
  MODULE_LICENSE("GPL");
  
  module_init(init_em_meta);
  module_exit(exit_em_meta);
db3d99c09   Patrick McHardy   [NET_SCHED]: emat...
836
837
  
  MODULE_ALIAS_TCF_EMATCH(TCF_EM_META);