xref: /openbmc/linux/net/sched/em_meta.c (revision 0da974f4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * net/sched/em_meta.c	Metadata ematch
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Authors:	Thomas Graf <tgraf@suug.ch>
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * ==========================================================================
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * 	The metadata ematch compares two meta objects where each object
141da177e4SLinus Torvalds  * 	represents either a meta value stored in the kernel or a static
151da177e4SLinus Torvalds  * 	value provided by userspace. The objects are not provided by
161da177e4SLinus Torvalds  * 	userspace itself but rather a definition providing the information
171da177e4SLinus Torvalds  * 	to build them. Every object is of a certain type which must be
181da177e4SLinus Torvalds  * 	equal to the object it is being compared to.
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  * 	The definition of a objects conists of the type (meta type), a
211da177e4SLinus Torvalds  * 	identifier (meta id) and additional type specific information.
221da177e4SLinus Torvalds  * 	The meta id is either TCF_META_TYPE_VALUE for values provided by
231da177e4SLinus Torvalds  * 	userspace or a index to the meta operations table consisting of
241da177e4SLinus Torvalds  * 	function pointers to type specific meta data collectors returning
251da177e4SLinus Torvalds  * 	the value of the requested meta value.
261da177e4SLinus Torvalds  *
271da177e4SLinus Torvalds  * 	         lvalue                                   rvalue
281da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
291da177e4SLinus Torvalds  * 	      | type: INT |                           | type: INT |
30261688d0SDavid S. Miller  * 	 def  | id: DEV   |                           | id: VALUE |
311da177e4SLinus Torvalds  * 	      | data:     |                           | data: 3   |
321da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
331da177e4SLinus Torvalds  * 	            |                                       |
34261688d0SDavid S. Miller  * 	            ---> meta_ops[INT][DEV](...)            |
351da177e4SLinus Torvalds  *	                      |                             |
361da177e4SLinus Torvalds  * 	            -----------                             |
371da177e4SLinus Torvalds  * 	            V                                       V
381da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
391da177e4SLinus Torvalds  * 	      | type: INT |                           | type: INT |
40261688d0SDavid S. Miller  * 	 obj  | id: DEV |                             | id: VALUE |
411da177e4SLinus Torvalds  * 	      | data: 2   |<--data got filled out     | data: 3   |
421da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
431da177e4SLinus Torvalds  * 	            |                                         |
441da177e4SLinus Torvalds  * 	            --------------> 2  equals 3 <--------------
451da177e4SLinus Torvalds  *
461da177e4SLinus Torvalds  * 	This is a simplified schema, the complexity varies depending
471da177e4SLinus Torvalds  * 	on the meta type. Obviously, the length of the data must also
481da177e4SLinus Torvalds  * 	be provided for non-numeric types.
491da177e4SLinus Torvalds  *
501da177e4SLinus Torvalds  * 	Additionaly, type dependant modifiers such as shift operators
511da177e4SLinus Torvalds  * 	or mask may be applied to extend the functionaliy. As of now,
521da177e4SLinus Torvalds  * 	the variable length type supports shifting the byte string to
531da177e4SLinus Torvalds  * 	the right, eating up any number of octets and thus supporting
541da177e4SLinus Torvalds  * 	wildcard interface name comparisons such as "ppp%" matching
551da177e4SLinus Torvalds  * 	ppp0..9.
561da177e4SLinus Torvalds  *
571da177e4SLinus Torvalds  * 	NOTE: Certain meta values depend on other subsystems and are
581da177e4SLinus Torvalds  * 	      only available if that subsytem is enabled in the kernel.
591da177e4SLinus Torvalds  */
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #include <linux/module.h>
621da177e4SLinus Torvalds #include <linux/types.h>
631da177e4SLinus Torvalds #include <linux/kernel.h>
641da177e4SLinus Torvalds #include <linux/sched.h>
651da177e4SLinus Torvalds #include <linux/string.h>
661da177e4SLinus Torvalds #include <linux/skbuff.h>
671da177e4SLinus Torvalds #include <linux/random.h>
681da177e4SLinus Torvalds #include <linux/tc_ematch/tc_em_meta.h>
691da177e4SLinus Torvalds #include <net/dst.h>
701da177e4SLinus Torvalds #include <net/route.h>
711da177e4SLinus Torvalds #include <net/pkt_cls.h>
7248900629SThomas Graf #include <net/sock.h>
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds struct meta_obj
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	unsigned long		value;
771da177e4SLinus Torvalds 	unsigned int		len;
781da177e4SLinus Torvalds };
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds struct meta_value
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	struct tcf_meta_val	hdr;
831da177e4SLinus Torvalds 	unsigned long		val;
841da177e4SLinus Torvalds 	unsigned int		len;
851da177e4SLinus Torvalds };
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds struct meta_match
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	struct meta_value	lvalue;
901da177e4SLinus Torvalds 	struct meta_value	rvalue;
911da177e4SLinus Torvalds };
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds static inline int meta_id(struct meta_value *v)
941da177e4SLinus Torvalds {
951da177e4SLinus Torvalds 	return TCF_META_ID(v->hdr.kind);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds static inline int meta_type(struct meta_value *v)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	return TCF_META_TYPE(v->hdr.kind);
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds #define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \
1041da177e4SLinus Torvalds 	struct tcf_pkt_info *info, struct meta_value *v, \
1051da177e4SLinus Torvalds 	struct meta_obj *dst, int *err)
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /**************************************************************************
1081da177e4SLinus Torvalds  * System status & misc
1091da177e4SLinus Torvalds  **************************************************************************/
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds META_COLLECTOR(int_random)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds 	get_random_bytes(&dst->value, sizeof(dst->value));
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds static inline unsigned long fixed_loadavg(int load)
1171da177e4SLinus Torvalds {
1181da177e4SLinus Torvalds 	int rnd_load = load + (FIXED_1/200);
1191da177e4SLinus Torvalds 	int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT;
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	return ((rnd_load >> FSHIFT) * 100) + rnd_frac;
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_0)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	dst->value = fixed_loadavg(avenrun[0]);
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_1)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds 	dst->value = fixed_loadavg(avenrun[1]);
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_2)
1351da177e4SLinus Torvalds {
1361da177e4SLinus Torvalds 	dst->value = fixed_loadavg(avenrun[2]);
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds /**************************************************************************
1401da177e4SLinus Torvalds  * Device names & indices
1411da177e4SLinus Torvalds  **************************************************************************/
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds static inline int int_dev(struct net_device *dev, struct meta_obj *dst)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	if (unlikely(dev == NULL))
1461da177e4SLinus Torvalds 		return -1;
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	dst->value = dev->ifindex;
1491da177e4SLinus Torvalds 	return 0;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds static inline int var_dev(struct net_device *dev, struct meta_obj *dst)
1531da177e4SLinus Torvalds {
1541da177e4SLinus Torvalds 	if (unlikely(dev == NULL))
1551da177e4SLinus Torvalds 		return -1;
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	dst->value = (unsigned long) dev->name;
1581da177e4SLinus Torvalds 	dst->len = strlen(dev->name);
1591da177e4SLinus Torvalds 	return 0;
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds META_COLLECTOR(int_dev)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	*err = int_dev(skb->dev, dst);
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds META_COLLECTOR(var_dev)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds 	*err = var_dev(skb->dev, dst);
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds /**************************************************************************
1731da177e4SLinus Torvalds  * skb attributes
1741da177e4SLinus Torvalds  **************************************************************************/
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds META_COLLECTOR(int_priority)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds 	dst->value = skb->priority;
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds META_COLLECTOR(int_protocol)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds 	/* Let userspace take care of the byte ordering */
1841da177e4SLinus Torvalds 	dst->value = skb->protocol;
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds META_COLLECTOR(int_pkttype)
1881da177e4SLinus Torvalds {
1891da177e4SLinus Torvalds 	dst->value = skb->pkt_type;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds META_COLLECTOR(int_pktlen)
1931da177e4SLinus Torvalds {
1941da177e4SLinus Torvalds 	dst->value = skb->len;
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds META_COLLECTOR(int_datalen)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	dst->value = skb->data_len;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds META_COLLECTOR(int_maclen)
2031da177e4SLinus Torvalds {
2041da177e4SLinus Torvalds 	dst->value = skb->mac_len;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds /**************************************************************************
2081da177e4SLinus Torvalds  * Netfilter
2091da177e4SLinus Torvalds  **************************************************************************/
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds META_COLLECTOR(int_nfmark)
2121da177e4SLinus Torvalds {
2137686ee1aSPatrick McHardy #ifdef CONFIG_NETFILTER
2141da177e4SLinus Torvalds 	dst->value = skb->nfmark;
2157686ee1aSPatrick McHardy #else
2167686ee1aSPatrick McHardy 	dst->value = 0;
2171da177e4SLinus Torvalds #endif
2187686ee1aSPatrick McHardy }
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds /**************************************************************************
2211da177e4SLinus Torvalds  * Traffic Control
2221da177e4SLinus Torvalds  **************************************************************************/
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds META_COLLECTOR(int_tcindex)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	dst->value = skb->tc_index;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds /**************************************************************************
2301da177e4SLinus Torvalds  * Routing
2311da177e4SLinus Torvalds  **************************************************************************/
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds META_COLLECTOR(int_rtclassid)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds 	if (unlikely(skb->dst == NULL))
2361da177e4SLinus Torvalds 		*err = -1;
2371da177e4SLinus Torvalds 	else
2387686ee1aSPatrick McHardy #ifdef CONFIG_NET_CLS_ROUTE
2391da177e4SLinus Torvalds 		dst->value = skb->dst->tclassid;
2407686ee1aSPatrick McHardy #else
2417686ee1aSPatrick McHardy 		dst->value = 0;
2421da177e4SLinus Torvalds #endif
2437686ee1aSPatrick McHardy }
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds META_COLLECTOR(int_rtiif)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	if (unlikely(skb->dst == NULL))
2481da177e4SLinus Torvalds 		*err = -1;
2491da177e4SLinus Torvalds 	else
2501da177e4SLinus Torvalds 		dst->value = ((struct rtable*) skb->dst)->fl.iif;
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds /**************************************************************************
25448900629SThomas Graf  * Socket Attributes
25548900629SThomas Graf  **************************************************************************/
25648900629SThomas Graf 
25748900629SThomas Graf #define SKIP_NONLOCAL(skb)			\
25848900629SThomas Graf 	if (unlikely(skb->sk == NULL)) {	\
25948900629SThomas Graf 		*err = -1;			\
26048900629SThomas Graf 		return;				\
26148900629SThomas Graf 	}
26248900629SThomas Graf 
26348900629SThomas Graf META_COLLECTOR(int_sk_family)
26448900629SThomas Graf {
26548900629SThomas Graf 	SKIP_NONLOCAL(skb);
26648900629SThomas Graf 	dst->value = skb->sk->sk_family;
26748900629SThomas Graf }
26848900629SThomas Graf 
26948900629SThomas Graf META_COLLECTOR(int_sk_state)
27048900629SThomas Graf {
27148900629SThomas Graf 	SKIP_NONLOCAL(skb);
27248900629SThomas Graf 	dst->value = skb->sk->sk_state;
27348900629SThomas Graf }
27448900629SThomas Graf 
27548900629SThomas Graf META_COLLECTOR(int_sk_reuse)
27648900629SThomas Graf {
27748900629SThomas Graf 	SKIP_NONLOCAL(skb);
27848900629SThomas Graf 	dst->value = skb->sk->sk_reuse;
27948900629SThomas Graf }
28048900629SThomas Graf 
28148900629SThomas Graf META_COLLECTOR(int_sk_bound_if)
28248900629SThomas Graf {
28348900629SThomas Graf 	SKIP_NONLOCAL(skb);
28448900629SThomas Graf 	/* No error if bound_dev_if is 0, legal userspace check */
28548900629SThomas Graf 	dst->value = skb->sk->sk_bound_dev_if;
28648900629SThomas Graf }
28748900629SThomas Graf 
28848900629SThomas Graf META_COLLECTOR(var_sk_bound_if)
28948900629SThomas Graf {
29048900629SThomas Graf 	SKIP_NONLOCAL(skb);
29148900629SThomas Graf 
29248900629SThomas Graf 	 if (skb->sk->sk_bound_dev_if == 0) {
29348900629SThomas Graf 		dst->value = (unsigned long) "any";
29448900629SThomas Graf 		dst->len = 3;
29548900629SThomas Graf 	 } else  {
29648900629SThomas Graf 		struct net_device *dev;
29748900629SThomas Graf 
29848900629SThomas Graf 		dev = dev_get_by_index(skb->sk->sk_bound_dev_if);
29948900629SThomas Graf 		*err = var_dev(dev, dst);
30048900629SThomas Graf 		if (dev)
30148900629SThomas Graf 			dev_put(dev);
30248900629SThomas Graf 	 }
30348900629SThomas Graf }
30448900629SThomas Graf 
30548900629SThomas Graf META_COLLECTOR(int_sk_refcnt)
30648900629SThomas Graf {
30748900629SThomas Graf 	SKIP_NONLOCAL(skb);
30848900629SThomas Graf 	dst->value = atomic_read(&skb->sk->sk_refcnt);
30948900629SThomas Graf }
31048900629SThomas Graf 
31148900629SThomas Graf META_COLLECTOR(int_sk_rcvbuf)
31248900629SThomas Graf {
31348900629SThomas Graf 	SKIP_NONLOCAL(skb);
31448900629SThomas Graf 	dst->value = skb->sk->sk_rcvbuf;
31548900629SThomas Graf }
31648900629SThomas Graf 
31748900629SThomas Graf META_COLLECTOR(int_sk_shutdown)
31848900629SThomas Graf {
31948900629SThomas Graf 	SKIP_NONLOCAL(skb);
32048900629SThomas Graf 	dst->value = skb->sk->sk_shutdown;
32148900629SThomas Graf }
32248900629SThomas Graf 
32348900629SThomas Graf META_COLLECTOR(int_sk_proto)
32448900629SThomas Graf {
32548900629SThomas Graf 	SKIP_NONLOCAL(skb);
32648900629SThomas Graf 	dst->value = skb->sk->sk_protocol;
32748900629SThomas Graf }
32848900629SThomas Graf 
32948900629SThomas Graf META_COLLECTOR(int_sk_type)
33048900629SThomas Graf {
33148900629SThomas Graf 	SKIP_NONLOCAL(skb);
33248900629SThomas Graf 	dst->value = skb->sk->sk_type;
33348900629SThomas Graf }
33448900629SThomas Graf 
33548900629SThomas Graf META_COLLECTOR(int_sk_rmem_alloc)
33648900629SThomas Graf {
33748900629SThomas Graf 	SKIP_NONLOCAL(skb);
33848900629SThomas Graf 	dst->value = atomic_read(&skb->sk->sk_rmem_alloc);
33948900629SThomas Graf }
34048900629SThomas Graf 
34148900629SThomas Graf META_COLLECTOR(int_sk_wmem_alloc)
34248900629SThomas Graf {
34348900629SThomas Graf 	SKIP_NONLOCAL(skb);
34448900629SThomas Graf 	dst->value = atomic_read(&skb->sk->sk_wmem_alloc);
34548900629SThomas Graf }
34648900629SThomas Graf 
34748900629SThomas Graf META_COLLECTOR(int_sk_omem_alloc)
34848900629SThomas Graf {
34948900629SThomas Graf 	SKIP_NONLOCAL(skb);
35048900629SThomas Graf 	dst->value = atomic_read(&skb->sk->sk_omem_alloc);
35148900629SThomas Graf }
35248900629SThomas Graf 
35348900629SThomas Graf META_COLLECTOR(int_sk_rcv_qlen)
35448900629SThomas Graf {
35548900629SThomas Graf 	SKIP_NONLOCAL(skb);
35648900629SThomas Graf 	dst->value = skb->sk->sk_receive_queue.qlen;
35748900629SThomas Graf }
35848900629SThomas Graf 
35948900629SThomas Graf META_COLLECTOR(int_sk_snd_qlen)
36048900629SThomas Graf {
36148900629SThomas Graf 	SKIP_NONLOCAL(skb);
36248900629SThomas Graf 	dst->value = skb->sk->sk_write_queue.qlen;
36348900629SThomas Graf }
36448900629SThomas Graf 
36548900629SThomas Graf META_COLLECTOR(int_sk_wmem_queued)
36648900629SThomas Graf {
36748900629SThomas Graf 	SKIP_NONLOCAL(skb);
36848900629SThomas Graf 	dst->value = skb->sk->sk_wmem_queued;
36948900629SThomas Graf }
37048900629SThomas Graf 
37148900629SThomas Graf META_COLLECTOR(int_sk_fwd_alloc)
37248900629SThomas Graf {
37348900629SThomas Graf 	SKIP_NONLOCAL(skb);
37448900629SThomas Graf 	dst->value = skb->sk->sk_forward_alloc;
37548900629SThomas Graf }
37648900629SThomas Graf 
37748900629SThomas Graf META_COLLECTOR(int_sk_sndbuf)
37848900629SThomas Graf {
37948900629SThomas Graf 	SKIP_NONLOCAL(skb);
38048900629SThomas Graf 	dst->value = skb->sk->sk_sndbuf;
38148900629SThomas Graf }
38248900629SThomas Graf 
38348900629SThomas Graf META_COLLECTOR(int_sk_alloc)
38448900629SThomas Graf {
38548900629SThomas Graf 	SKIP_NONLOCAL(skb);
38648900629SThomas Graf 	dst->value = skb->sk->sk_allocation;
38748900629SThomas Graf }
38848900629SThomas Graf 
38948900629SThomas Graf META_COLLECTOR(int_sk_route_caps)
39048900629SThomas Graf {
39148900629SThomas Graf 	SKIP_NONLOCAL(skb);
39248900629SThomas Graf 	dst->value = skb->sk->sk_route_caps;
39348900629SThomas Graf }
39448900629SThomas Graf 
39581c3d547SEric Dumazet META_COLLECTOR(int_sk_hash)
39648900629SThomas Graf {
39748900629SThomas Graf 	SKIP_NONLOCAL(skb);
39881c3d547SEric Dumazet 	dst->value = skb->sk->sk_hash;
39948900629SThomas Graf }
40048900629SThomas Graf 
40148900629SThomas Graf META_COLLECTOR(int_sk_lingertime)
40248900629SThomas Graf {
40348900629SThomas Graf 	SKIP_NONLOCAL(skb);
40448900629SThomas Graf 	dst->value = skb->sk->sk_lingertime / HZ;
40548900629SThomas Graf }
40648900629SThomas Graf 
40748900629SThomas Graf META_COLLECTOR(int_sk_err_qlen)
40848900629SThomas Graf {
40948900629SThomas Graf 	SKIP_NONLOCAL(skb);
41048900629SThomas Graf 	dst->value = skb->sk->sk_error_queue.qlen;
41148900629SThomas Graf }
41248900629SThomas Graf 
41348900629SThomas Graf META_COLLECTOR(int_sk_ack_bl)
41448900629SThomas Graf {
41548900629SThomas Graf 	SKIP_NONLOCAL(skb);
41648900629SThomas Graf 	dst->value = skb->sk->sk_ack_backlog;
41748900629SThomas Graf }
41848900629SThomas Graf 
41948900629SThomas Graf META_COLLECTOR(int_sk_max_ack_bl)
42048900629SThomas Graf {
42148900629SThomas Graf 	SKIP_NONLOCAL(skb);
42248900629SThomas Graf 	dst->value = skb->sk->sk_max_ack_backlog;
42348900629SThomas Graf }
42448900629SThomas Graf 
42548900629SThomas Graf META_COLLECTOR(int_sk_prio)
42648900629SThomas Graf {
42748900629SThomas Graf 	SKIP_NONLOCAL(skb);
42848900629SThomas Graf 	dst->value = skb->sk->sk_priority;
42948900629SThomas Graf }
43048900629SThomas Graf 
43148900629SThomas Graf META_COLLECTOR(int_sk_rcvlowat)
43248900629SThomas Graf {
43348900629SThomas Graf 	SKIP_NONLOCAL(skb);
43448900629SThomas Graf 	dst->value = skb->sk->sk_rcvlowat;
43548900629SThomas Graf }
43648900629SThomas Graf 
43748900629SThomas Graf META_COLLECTOR(int_sk_rcvtimeo)
43848900629SThomas Graf {
43948900629SThomas Graf 	SKIP_NONLOCAL(skb);
44048900629SThomas Graf 	dst->value = skb->sk->sk_rcvtimeo / HZ;
44148900629SThomas Graf }
44248900629SThomas Graf 
44348900629SThomas Graf META_COLLECTOR(int_sk_sndtimeo)
44448900629SThomas Graf {
44548900629SThomas Graf 	SKIP_NONLOCAL(skb);
44648900629SThomas Graf 	dst->value = skb->sk->sk_sndtimeo / HZ;
44748900629SThomas Graf }
44848900629SThomas Graf 
44948900629SThomas Graf META_COLLECTOR(int_sk_sendmsg_off)
45048900629SThomas Graf {
45148900629SThomas Graf 	SKIP_NONLOCAL(skb);
45248900629SThomas Graf 	dst->value = skb->sk->sk_sndmsg_off;
45348900629SThomas Graf }
45448900629SThomas Graf 
45548900629SThomas Graf META_COLLECTOR(int_sk_write_pend)
45648900629SThomas Graf {
45748900629SThomas Graf 	SKIP_NONLOCAL(skb);
45848900629SThomas Graf 	dst->value = skb->sk->sk_write_pending;
45948900629SThomas Graf }
46048900629SThomas Graf 
46148900629SThomas Graf /**************************************************************************
4621da177e4SLinus Torvalds  * Meta value collectors assignment table
4631da177e4SLinus Torvalds  **************************************************************************/
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds struct meta_ops
4661da177e4SLinus Torvalds {
4671da177e4SLinus Torvalds 	void		(*get)(struct sk_buff *, struct tcf_pkt_info *,
4681da177e4SLinus Torvalds 			       struct meta_value *, struct meta_obj *, int *);
4691da177e4SLinus Torvalds };
4701da177e4SLinus Torvalds 
47148900629SThomas Graf #define META_ID(name) TCF_META_ID_##name
47248900629SThomas Graf #define META_FUNC(name) { .get = meta_##name }
47348900629SThomas Graf 
4741da177e4SLinus Torvalds /* Meta value operations table listing all meta value collectors and
4751da177e4SLinus Torvalds  * assigns them to a type and meta id. */
4761da177e4SLinus Torvalds static struct meta_ops __meta_ops[TCF_META_TYPE_MAX+1][TCF_META_ID_MAX+1] = {
4771da177e4SLinus Torvalds 	[TCF_META_TYPE_VAR] = {
47848900629SThomas Graf 		[META_ID(DEV)]			= META_FUNC(var_dev),
47948900629SThomas Graf 		[META_ID(SK_BOUND_IF)] 		= META_FUNC(var_sk_bound_if),
4801da177e4SLinus Torvalds 	},
4811da177e4SLinus Torvalds 	[TCF_META_TYPE_INT] = {
48248900629SThomas Graf 		[META_ID(RANDOM)]		= META_FUNC(int_random),
48348900629SThomas Graf 		[META_ID(LOADAVG_0)]		= META_FUNC(int_loadavg_0),
48448900629SThomas Graf 		[META_ID(LOADAVG_1)]		= META_FUNC(int_loadavg_1),
48548900629SThomas Graf 		[META_ID(LOADAVG_2)]		= META_FUNC(int_loadavg_2),
48648900629SThomas Graf 		[META_ID(DEV)]			= META_FUNC(int_dev),
48748900629SThomas Graf 		[META_ID(PRIORITY)]		= META_FUNC(int_priority),
48848900629SThomas Graf 		[META_ID(PROTOCOL)]		= META_FUNC(int_protocol),
48948900629SThomas Graf 		[META_ID(PKTTYPE)]		= META_FUNC(int_pkttype),
49048900629SThomas Graf 		[META_ID(PKTLEN)]		= META_FUNC(int_pktlen),
49148900629SThomas Graf 		[META_ID(DATALEN)]		= META_FUNC(int_datalen),
49248900629SThomas Graf 		[META_ID(MACLEN)]		= META_FUNC(int_maclen),
49348900629SThomas Graf 		[META_ID(NFMARK)]		= META_FUNC(int_nfmark),
49448900629SThomas Graf 		[META_ID(TCINDEX)]		= META_FUNC(int_tcindex),
49548900629SThomas Graf 		[META_ID(RTCLASSID)]		= META_FUNC(int_rtclassid),
49648900629SThomas Graf 		[META_ID(RTIIF)]		= META_FUNC(int_rtiif),
49748900629SThomas Graf 		[META_ID(SK_FAMILY)]		= META_FUNC(int_sk_family),
49848900629SThomas Graf 		[META_ID(SK_STATE)]		= META_FUNC(int_sk_state),
49948900629SThomas Graf 		[META_ID(SK_REUSE)]		= META_FUNC(int_sk_reuse),
50048900629SThomas Graf 		[META_ID(SK_BOUND_IF)]		= META_FUNC(int_sk_bound_if),
50148900629SThomas Graf 		[META_ID(SK_REFCNT)]		= META_FUNC(int_sk_refcnt),
50248900629SThomas Graf 		[META_ID(SK_RCVBUF)]		= META_FUNC(int_sk_rcvbuf),
50348900629SThomas Graf 		[META_ID(SK_SNDBUF)]		= META_FUNC(int_sk_sndbuf),
50448900629SThomas Graf 		[META_ID(SK_SHUTDOWN)]		= META_FUNC(int_sk_shutdown),
50548900629SThomas Graf 		[META_ID(SK_PROTO)]		= META_FUNC(int_sk_proto),
50648900629SThomas Graf 		[META_ID(SK_TYPE)]		= META_FUNC(int_sk_type),
50748900629SThomas Graf 		[META_ID(SK_RMEM_ALLOC)]	= META_FUNC(int_sk_rmem_alloc),
50848900629SThomas Graf 		[META_ID(SK_WMEM_ALLOC)]	= META_FUNC(int_sk_wmem_alloc),
50948900629SThomas Graf 		[META_ID(SK_OMEM_ALLOC)]	= META_FUNC(int_sk_omem_alloc),
51048900629SThomas Graf 		[META_ID(SK_WMEM_QUEUED)]	= META_FUNC(int_sk_wmem_queued),
51148900629SThomas Graf 		[META_ID(SK_RCV_QLEN)]		= META_FUNC(int_sk_rcv_qlen),
51248900629SThomas Graf 		[META_ID(SK_SND_QLEN)]		= META_FUNC(int_sk_snd_qlen),
51348900629SThomas Graf 		[META_ID(SK_ERR_QLEN)]		= META_FUNC(int_sk_err_qlen),
51448900629SThomas Graf 		[META_ID(SK_FORWARD_ALLOCS)]	= META_FUNC(int_sk_fwd_alloc),
51548900629SThomas Graf 		[META_ID(SK_ALLOCS)]		= META_FUNC(int_sk_alloc),
51648900629SThomas Graf 		[META_ID(SK_ROUTE_CAPS)]	= META_FUNC(int_sk_route_caps),
51781c3d547SEric Dumazet 		[META_ID(SK_HASH)]		= META_FUNC(int_sk_hash),
51848900629SThomas Graf 		[META_ID(SK_LINGERTIME)]	= META_FUNC(int_sk_lingertime),
51948900629SThomas Graf 		[META_ID(SK_ACK_BACKLOG)]	= META_FUNC(int_sk_ack_bl),
52048900629SThomas Graf 		[META_ID(SK_MAX_ACK_BACKLOG)]	= META_FUNC(int_sk_max_ack_bl),
52148900629SThomas Graf 		[META_ID(SK_PRIO)]		= META_FUNC(int_sk_prio),
52248900629SThomas Graf 		[META_ID(SK_RCVLOWAT)]		= META_FUNC(int_sk_rcvlowat),
52348900629SThomas Graf 		[META_ID(SK_RCVTIMEO)]		= META_FUNC(int_sk_rcvtimeo),
52448900629SThomas Graf 		[META_ID(SK_SNDTIMEO)]		= META_FUNC(int_sk_sndtimeo),
52548900629SThomas Graf 		[META_ID(SK_SENDMSG_OFF)]	= META_FUNC(int_sk_sendmsg_off),
52648900629SThomas Graf 		[META_ID(SK_WRITE_PENDING)]	= META_FUNC(int_sk_write_pend),
5271da177e4SLinus Torvalds 	}
5281da177e4SLinus Torvalds };
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds static inline struct meta_ops * meta_ops(struct meta_value *val)
5311da177e4SLinus Torvalds {
5321da177e4SLinus Torvalds 	return &__meta_ops[meta_type(val)][meta_id(val)];
5331da177e4SLinus Torvalds }
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds /**************************************************************************
5361da177e4SLinus Torvalds  * Type specific operations for TCF_META_TYPE_VAR
5371da177e4SLinus Torvalds  **************************************************************************/
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds static int meta_var_compare(struct meta_obj *a, struct meta_obj *b)
5401da177e4SLinus Torvalds {
5411da177e4SLinus Torvalds 	int r = a->len - b->len;
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	if (r == 0)
5441da177e4SLinus Torvalds 		r = memcmp((void *) a->value, (void *) b->value, a->len);
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds 	return r;
5471da177e4SLinus Torvalds }
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds static int meta_var_change(struct meta_value *dst, struct rtattr *rta)
5501da177e4SLinus Torvalds {
5511da177e4SLinus Torvalds 	int len = RTA_PAYLOAD(rta);
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds 	dst->val = (unsigned long) kmalloc(len, GFP_KERNEL);
5541da177e4SLinus Torvalds 	if (dst->val == 0UL)
5551da177e4SLinus Torvalds 		return -ENOMEM;
5561da177e4SLinus Torvalds 	memcpy((void *) dst->val, RTA_DATA(rta), len);
5571da177e4SLinus Torvalds 	dst->len = len;
5581da177e4SLinus Torvalds 	return 0;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds static void meta_var_destroy(struct meta_value *v)
5621da177e4SLinus Torvalds {
5631da177e4SLinus Torvalds 	kfree((void *) v->val);
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds static void meta_var_apply_extras(struct meta_value *v,
5671da177e4SLinus Torvalds 				  struct meta_obj *dst)
5681da177e4SLinus Torvalds {
5691da177e4SLinus Torvalds 	int shift = v->hdr.shift;
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	if (shift && shift < dst->len)
5721da177e4SLinus Torvalds 		dst->len -= shift;
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds static int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds 	if (v->val && v->len)
5781da177e4SLinus Torvalds 		RTA_PUT(skb, tlv, v->len, (void *) v->val);
5791da177e4SLinus Torvalds 	return 0;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds rtattr_failure:
5821da177e4SLinus Torvalds 	return -1;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds /**************************************************************************
5861da177e4SLinus Torvalds  * Type specific operations for TCF_META_TYPE_INT
5871da177e4SLinus Torvalds  **************************************************************************/
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds static int meta_int_compare(struct meta_obj *a, struct meta_obj *b)
5901da177e4SLinus Torvalds {
5911da177e4SLinus Torvalds 	/* Let gcc optimize it, the unlikely is not really based on
5921da177e4SLinus Torvalds 	 * some numbers but jump free code for mismatches seems
5931da177e4SLinus Torvalds 	 * more logical. */
59498e56405SThomas Graf 	if (unlikely(a->value == b->value))
5951da177e4SLinus Torvalds 		return 0;
59698e56405SThomas Graf 	else if (a->value < b->value)
5971da177e4SLinus Torvalds 		return -1;
5981da177e4SLinus Torvalds 	else
5991da177e4SLinus Torvalds 		return 1;
6001da177e4SLinus Torvalds }
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds static int meta_int_change(struct meta_value *dst, struct rtattr *rta)
6031da177e4SLinus Torvalds {
6041da177e4SLinus Torvalds 	if (RTA_PAYLOAD(rta) >= sizeof(unsigned long)) {
6051da177e4SLinus Torvalds 		dst->val = *(unsigned long *) RTA_DATA(rta);
6061da177e4SLinus Torvalds 		dst->len = sizeof(unsigned long);
6071da177e4SLinus Torvalds 	} else if (RTA_PAYLOAD(rta) == sizeof(u32)) {
6081da177e4SLinus Torvalds 		dst->val = *(u32 *) RTA_DATA(rta);
6091da177e4SLinus Torvalds 		dst->len = sizeof(u32);
6101da177e4SLinus Torvalds 	} else
6111da177e4SLinus Torvalds 		return -EINVAL;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	return 0;
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds static void meta_int_apply_extras(struct meta_value *v,
6171da177e4SLinus Torvalds 				  struct meta_obj *dst)
6181da177e4SLinus Torvalds {
6191da177e4SLinus Torvalds 	if (v->hdr.shift)
6201da177e4SLinus Torvalds 		dst->value >>= v->hdr.shift;
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	if (v->val)
6231da177e4SLinus Torvalds 		dst->value &= v->val;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds static int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
6271da177e4SLinus Torvalds {
6281da177e4SLinus Torvalds 	if (v->len == sizeof(unsigned long))
6291da177e4SLinus Torvalds 		RTA_PUT(skb, tlv, sizeof(unsigned long), &v->val);
6301da177e4SLinus Torvalds 	else if (v->len == sizeof(u32)) {
6311da177e4SLinus Torvalds 		u32 d = v->val;
6321da177e4SLinus Torvalds 		RTA_PUT(skb, tlv, sizeof(d), &d);
6331da177e4SLinus Torvalds 	}
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 	return 0;
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds rtattr_failure:
6381da177e4SLinus Torvalds 	return -1;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds /**************************************************************************
6421da177e4SLinus Torvalds  * Type specific operations table
6431da177e4SLinus Torvalds  **************************************************************************/
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds struct meta_type_ops
6461da177e4SLinus Torvalds {
6471da177e4SLinus Torvalds 	void	(*destroy)(struct meta_value *);
6481da177e4SLinus Torvalds 	int	(*compare)(struct meta_obj *, struct meta_obj *);
6491da177e4SLinus Torvalds 	int	(*change)(struct meta_value *, struct rtattr *);
6501da177e4SLinus Torvalds 	void	(*apply_extras)(struct meta_value *, struct meta_obj *);
6511da177e4SLinus Torvalds 	int	(*dump)(struct sk_buff *, struct meta_value *, int);
6521da177e4SLinus Torvalds };
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds static struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX+1] = {
6551da177e4SLinus Torvalds 	[TCF_META_TYPE_VAR] = {
6561da177e4SLinus Torvalds 		.destroy = meta_var_destroy,
6571da177e4SLinus Torvalds 		.compare = meta_var_compare,
6581da177e4SLinus Torvalds 		.change = meta_var_change,
6591da177e4SLinus Torvalds 		.apply_extras = meta_var_apply_extras,
6601da177e4SLinus Torvalds 		.dump = meta_var_dump
6611da177e4SLinus Torvalds 	},
6621da177e4SLinus Torvalds 	[TCF_META_TYPE_INT] = {
6631da177e4SLinus Torvalds 		.compare = meta_int_compare,
6641da177e4SLinus Torvalds 		.change = meta_int_change,
6651da177e4SLinus Torvalds 		.apply_extras = meta_int_apply_extras,
6661da177e4SLinus Torvalds 		.dump = meta_int_dump
6671da177e4SLinus Torvalds 	}
6681da177e4SLinus Torvalds };
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds static inline struct meta_type_ops * meta_type_ops(struct meta_value *v)
6711da177e4SLinus Torvalds {
6721da177e4SLinus Torvalds 	return &__meta_type_ops[meta_type(v)];
6731da177e4SLinus Torvalds }
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds /**************************************************************************
6761da177e4SLinus Torvalds  * Core
6771da177e4SLinus Torvalds  **************************************************************************/
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds static inline int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info,
6801da177e4SLinus Torvalds 			   struct meta_value *v, struct meta_obj *dst)
6811da177e4SLinus Torvalds {
6821da177e4SLinus Torvalds 	int err = 0;
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	if (meta_id(v) == TCF_META_ID_VALUE) {
6851da177e4SLinus Torvalds 		dst->value = v->val;
6861da177e4SLinus Torvalds 		dst->len = v->len;
6871da177e4SLinus Torvalds 		return 0;
6881da177e4SLinus Torvalds 	}
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	meta_ops(v)->get(skb, info, v, dst, &err);
6911da177e4SLinus Torvalds 	if (err < 0)
6921da177e4SLinus Torvalds 		return err;
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	if (meta_type_ops(v)->apply_extras)
6951da177e4SLinus Torvalds 	    meta_type_ops(v)->apply_extras(v, dst);
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	return 0;
6981da177e4SLinus Torvalds }
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds static int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m,
7011da177e4SLinus Torvalds 			 struct tcf_pkt_info *info)
7021da177e4SLinus Torvalds {
7031da177e4SLinus Torvalds 	int r;
7041da177e4SLinus Torvalds 	struct meta_match *meta = (struct meta_match *) m->data;
7051da177e4SLinus Torvalds 	struct meta_obj l_value, r_value;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 ||
7081da177e4SLinus Torvalds 	    meta_get(skb, info, &meta->rvalue, &r_value) < 0)
7091da177e4SLinus Torvalds 		return 0;
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 	r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value);
7121da177e4SLinus Torvalds 
7131da177e4SLinus Torvalds 	switch (meta->lvalue.hdr.op) {
7141da177e4SLinus Torvalds 		case TCF_EM_OPND_EQ:
7151da177e4SLinus Torvalds 			return !r;
7161da177e4SLinus Torvalds 		case TCF_EM_OPND_LT:
7171da177e4SLinus Torvalds 			return r < 0;
7181da177e4SLinus Torvalds 		case TCF_EM_OPND_GT:
7191da177e4SLinus Torvalds 			return r > 0;
7201da177e4SLinus Torvalds 	}
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds 	return 0;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds static inline void meta_delete(struct meta_match *meta)
7261da177e4SLinus Torvalds {
7271da177e4SLinus Torvalds 	struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	if (ops && ops->destroy) {
7301da177e4SLinus Torvalds 		ops->destroy(&meta->lvalue);
7311da177e4SLinus Torvalds 		ops->destroy(&meta->rvalue);
7321da177e4SLinus Torvalds 	}
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds 	kfree(meta);
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds static inline int meta_change_data(struct meta_value *dst, struct rtattr *rta)
7381da177e4SLinus Torvalds {
7391da177e4SLinus Torvalds 	if (rta) {
7401da177e4SLinus Torvalds 		if (RTA_PAYLOAD(rta) == 0)
7411da177e4SLinus Torvalds 			return -EINVAL;
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds 		return meta_type_ops(dst)->change(dst, rta);
7441da177e4SLinus Torvalds 	}
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 	return 0;
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds static inline int meta_is_supported(struct meta_value *val)
7501da177e4SLinus Torvalds {
7511da177e4SLinus Torvalds 	return (!meta_id(val) || meta_ops(val)->get);
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds static int em_meta_change(struct tcf_proto *tp, void *data, int len,
7551da177e4SLinus Torvalds 			  struct tcf_ematch *m)
7561da177e4SLinus Torvalds {
7571da177e4SLinus Torvalds 	int err = -EINVAL;
7581da177e4SLinus Torvalds 	struct rtattr *tb[TCA_EM_META_MAX];
7591da177e4SLinus Torvalds 	struct tcf_meta_hdr *hdr;
7601da177e4SLinus Torvalds 	struct meta_match *meta = NULL;
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 	if (rtattr_parse(tb, TCA_EM_META_MAX, data, len) < 0)
7631da177e4SLinus Torvalds 		goto errout;
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	if (tb[TCA_EM_META_HDR-1] == NULL ||
7661da177e4SLinus Torvalds 	    RTA_PAYLOAD(tb[TCA_EM_META_HDR-1]) < sizeof(*hdr))
7671da177e4SLinus Torvalds 		goto errout;
7681da177e4SLinus Torvalds 	hdr = RTA_DATA(tb[TCA_EM_META_HDR-1]);
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds 	if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) ||
7711da177e4SLinus Torvalds 	    TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX ||
7721da177e4SLinus Torvalds 	    TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX ||
7731da177e4SLinus Torvalds 	    TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX)
7741da177e4SLinus Torvalds 		goto errout;
7751da177e4SLinus Torvalds 
7760da974f4SPanagiotis Issaris 	meta = kzalloc(sizeof(*meta), GFP_KERNEL);
7771da177e4SLinus Torvalds 	if (meta == NULL)
7781da177e4SLinus Torvalds 		goto errout;
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
7811da177e4SLinus Torvalds 	memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	if (!meta_is_supported(&meta->lvalue) ||
7841da177e4SLinus Torvalds 	    !meta_is_supported(&meta->rvalue)) {
7851da177e4SLinus Torvalds 		err = -EOPNOTSUPP;
7861da177e4SLinus Torvalds 		goto errout;
7871da177e4SLinus Torvalds 	}
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds 	if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE-1]) < 0 ||
7901da177e4SLinus Torvalds 	    meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE-1]) < 0)
7911da177e4SLinus Torvalds 		goto errout;
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	m->datalen = sizeof(*meta);
7941da177e4SLinus Torvalds 	m->data = (unsigned long) meta;
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 	err = 0;
7971da177e4SLinus Torvalds errout:
7981da177e4SLinus Torvalds 	if (err && meta)
7991da177e4SLinus Torvalds 		meta_delete(meta);
8001da177e4SLinus Torvalds 	return err;
8011da177e4SLinus Torvalds }
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds static void em_meta_destroy(struct tcf_proto *tp, struct tcf_ematch *m)
8041da177e4SLinus Torvalds {
8051da177e4SLinus Torvalds 	if (m)
8061da177e4SLinus Torvalds 		meta_delete((struct meta_match *) m->data);
8071da177e4SLinus Torvalds }
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds static int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em)
8101da177e4SLinus Torvalds {
8111da177e4SLinus Torvalds 	struct meta_match *meta = (struct meta_match *) em->data;
8121da177e4SLinus Torvalds 	struct tcf_meta_hdr hdr;
8131da177e4SLinus Torvalds 	struct meta_type_ops *ops;
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds 	memset(&hdr, 0, sizeof(hdr));
8161da177e4SLinus Torvalds 	memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left));
8171da177e4SLinus Torvalds 	memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right));
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds 	RTA_PUT(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr);
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds 	ops = meta_type_ops(&meta->lvalue);
8221da177e4SLinus Torvalds 	if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 ||
8231da177e4SLinus Torvalds 	    ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0)
8241da177e4SLinus Torvalds 		goto rtattr_failure;
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds 	return 0;
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds rtattr_failure:
8291da177e4SLinus Torvalds 	return -1;
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds 
8321da177e4SLinus Torvalds static struct tcf_ematch_ops em_meta_ops = {
8331da177e4SLinus Torvalds 	.kind	  = TCF_EM_META,
8341da177e4SLinus Torvalds 	.change	  = em_meta_change,
8351da177e4SLinus Torvalds 	.match	  = em_meta_match,
8361da177e4SLinus Torvalds 	.destroy  = em_meta_destroy,
8371da177e4SLinus Torvalds 	.dump	  = em_meta_dump,
8381da177e4SLinus Torvalds 	.owner	  = THIS_MODULE,
8391da177e4SLinus Torvalds 	.link	  = LIST_HEAD_INIT(em_meta_ops.link)
8401da177e4SLinus Torvalds };
8411da177e4SLinus Torvalds 
8421da177e4SLinus Torvalds static int __init init_em_meta(void)
8431da177e4SLinus Torvalds {
8441da177e4SLinus Torvalds 	return tcf_em_register(&em_meta_ops);
8451da177e4SLinus Torvalds }
8461da177e4SLinus Torvalds 
8471da177e4SLinus Torvalds static void __exit exit_em_meta(void)
8481da177e4SLinus Torvalds {
8491da177e4SLinus Torvalds 	tcf_em_unregister(&em_meta_ops);
8501da177e4SLinus Torvalds }
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds MODULE_LICENSE("GPL");
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds module_init(init_em_meta);
8551da177e4SLinus Torvalds module_exit(exit_em_meta);
856