xref: /openbmc/linux/net/sched/em_meta.c (revision bc1fb82a)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * net/sched/em_meta.c	Metadata ematch
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:	Thomas Graf <tgraf@suug.ch>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * ==========================================================================
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * 	The metadata ematch compares two meta objects where each object
101da177e4SLinus Torvalds  * 	represents either a meta value stored in the kernel or a static
111da177e4SLinus Torvalds  * 	value provided by userspace. The objects are not provided by
121da177e4SLinus Torvalds  * 	userspace itself but rather a definition providing the information
131da177e4SLinus Torvalds  * 	to build them. Every object is of a certain type which must be
141da177e4SLinus Torvalds  * 	equal to the object it is being compared to.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * 	The definition of a objects conists of the type (meta type), a
171da177e4SLinus Torvalds  * 	identifier (meta id) and additional type specific information.
181da177e4SLinus Torvalds  * 	The meta id is either TCF_META_TYPE_VALUE for values provided by
191da177e4SLinus Torvalds  * 	userspace or a index to the meta operations table consisting of
201da177e4SLinus Torvalds  * 	function pointers to type specific meta data collectors returning
211da177e4SLinus Torvalds  * 	the value of the requested meta value.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  * 	         lvalue                                   rvalue
241da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
251da177e4SLinus Torvalds  * 	      | type: INT |                           | type: INT |
26261688d0SDavid S. Miller  * 	 def  | id: DEV   |                           | id: VALUE |
271da177e4SLinus Torvalds  * 	      | data:     |                           | data: 3   |
281da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
291da177e4SLinus Torvalds  * 	            |                                       |
30261688d0SDavid S. Miller  * 	            ---> meta_ops[INT][DEV](...)            |
311da177e4SLinus Torvalds  *	                      |                             |
321da177e4SLinus Torvalds  * 	            -----------                             |
331da177e4SLinus Torvalds  * 	            V                                       V
341da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
351da177e4SLinus Torvalds  * 	      | type: INT |                           | type: INT |
36261688d0SDavid S. Miller  * 	 obj  | id: DEV |                             | id: VALUE |
371da177e4SLinus Torvalds  * 	      | data: 2   |<--data got filled out     | data: 3   |
381da177e4SLinus Torvalds  * 	      +-----------+                           +-----------+
391da177e4SLinus Torvalds  * 	            |                                         |
401da177e4SLinus Torvalds  * 	            --------------> 2  equals 3 <--------------
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  * 	This is a simplified schema, the complexity varies depending
431da177e4SLinus Torvalds  * 	on the meta type. Obviously, the length of the data must also
441da177e4SLinus Torvalds  * 	be provided for non-numeric types.
451da177e4SLinus Torvalds  *
4625985edcSLucas De Marchi  * 	Additionally, type dependent modifiers such as shift operators
474170f0efSTaichi Nishimura  * 	or mask may be applied to extend the functionality. As of now,
481da177e4SLinus Torvalds  * 	the variable length type supports shifting the byte string to
491da177e4SLinus Torvalds  * 	the right, eating up any number of octets and thus supporting
501da177e4SLinus Torvalds  * 	wildcard interface name comparisons such as "ppp%" matching
511da177e4SLinus Torvalds  * 	ppp0..9.
521da177e4SLinus Torvalds  *
531da177e4SLinus Torvalds  * 	NOTE: Certain meta values depend on other subsystems and are
543a4fa0a2SRobert P. J. Day  * 	      only available if that subsystem is enabled in the kernel.
551da177e4SLinus Torvalds  */
561da177e4SLinus Torvalds 
575a0e3ad6STejun Heo #include <linux/slab.h>
581da177e4SLinus Torvalds #include <linux/module.h>
591da177e4SLinus Torvalds #include <linux/types.h>
601da177e4SLinus Torvalds #include <linux/kernel.h>
611da177e4SLinus Torvalds #include <linux/sched.h>
624f17722cSIngo Molnar #include <linux/sched/loadavg.h>
631da177e4SLinus Torvalds #include <linux/string.h>
641da177e4SLinus Torvalds #include <linux/skbuff.h>
651da177e4SLinus Torvalds #include <linux/random.h>
663113e88cSStephen Hemminger #include <linux/if_vlan.h>
671da177e4SLinus Torvalds #include <linux/tc_ematch/tc_em_meta.h>
681da177e4SLinus Torvalds #include <net/dst.h>
691da177e4SLinus Torvalds #include <net/route.h>
701da177e4SLinus Torvalds #include <net/pkt_cls.h>
7148900629SThomas Graf #include <net/sock.h>
721da177e4SLinus Torvalds 
73cc7ec456SEric Dumazet struct meta_obj {
741da177e4SLinus Torvalds 	unsigned long		value;
751da177e4SLinus Torvalds 	unsigned int		len;
761da177e4SLinus Torvalds };
771da177e4SLinus Torvalds 
78cc7ec456SEric Dumazet struct meta_value {
791da177e4SLinus Torvalds 	struct tcf_meta_val	hdr;
801da177e4SLinus Torvalds 	unsigned long		val;
811da177e4SLinus Torvalds 	unsigned int		len;
821da177e4SLinus Torvalds };
831da177e4SLinus Torvalds 
84cc7ec456SEric Dumazet struct meta_match {
851da177e4SLinus Torvalds 	struct meta_value	lvalue;
861da177e4SLinus Torvalds 	struct meta_value	rvalue;
871da177e4SLinus Torvalds };
881da177e4SLinus Torvalds 
meta_id(struct meta_value * v)891da177e4SLinus Torvalds static inline int meta_id(struct meta_value *v)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	return TCF_META_ID(v->hdr.kind);
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds 
meta_type(struct meta_value * v)941da177e4SLinus Torvalds static inline int meta_type(struct meta_value *v)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	return TCF_META_TYPE(v->hdr.kind);
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds #define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \
1001da177e4SLinus Torvalds 	struct tcf_pkt_info *info, struct meta_value *v, \
1011da177e4SLinus Torvalds 	struct meta_obj *dst, int *err)
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds /**************************************************************************
1041da177e4SLinus Torvalds  * System status & misc
1051da177e4SLinus Torvalds  **************************************************************************/
1061da177e4SLinus Torvalds 
META_COLLECTOR(int_random)1071da177e4SLinus Torvalds META_COLLECTOR(int_random)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds 	get_random_bytes(&dst->value, sizeof(dst->value));
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
fixed_loadavg(int load)1121da177e4SLinus Torvalds static inline unsigned long fixed_loadavg(int load)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds 	int rnd_load = load + (FIXED_1/200);
1151da177e4SLinus Torvalds 	int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	return ((rnd_load >> FSHIFT) * 100) + rnd_frac;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
META_COLLECTOR(int_loadavg_0)1201da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_0)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds 	dst->value = fixed_loadavg(avenrun[0]);
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
META_COLLECTOR(int_loadavg_1)1251da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_1)
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	dst->value = fixed_loadavg(avenrun[1]);
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
META_COLLECTOR(int_loadavg_2)1301da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_2)
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	dst->value = fixed_loadavg(avenrun[2]);
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds /**************************************************************************
1361da177e4SLinus Torvalds  * Device names & indices
1371da177e4SLinus Torvalds  **************************************************************************/
1381da177e4SLinus Torvalds 
int_dev(struct net_device * dev,struct meta_obj * dst)1391da177e4SLinus Torvalds static inline int int_dev(struct net_device *dev, struct meta_obj *dst)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	if (unlikely(dev == NULL))
1421da177e4SLinus Torvalds 		return -1;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	dst->value = dev->ifindex;
1451da177e4SLinus Torvalds 	return 0;
1461da177e4SLinus Torvalds }
1471da177e4SLinus Torvalds 
var_dev(struct net_device * dev,struct meta_obj * dst)1481da177e4SLinus Torvalds static inline int var_dev(struct net_device *dev, struct meta_obj *dst)
1491da177e4SLinus Torvalds {
1501da177e4SLinus Torvalds 	if (unlikely(dev == NULL))
1511da177e4SLinus Torvalds 		return -1;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	dst->value = (unsigned long) dev->name;
1541da177e4SLinus Torvalds 	dst->len = strlen(dev->name);
1551da177e4SLinus Torvalds 	return 0;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
META_COLLECTOR(int_dev)1581da177e4SLinus Torvalds META_COLLECTOR(int_dev)
1591da177e4SLinus Torvalds {
1601da177e4SLinus Torvalds 	*err = int_dev(skb->dev, dst);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
META_COLLECTOR(var_dev)1631da177e4SLinus Torvalds META_COLLECTOR(var_dev)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	*err = var_dev(skb->dev, dst);
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds /**************************************************************************
1693113e88cSStephen Hemminger  * vlan tag
1703113e88cSStephen Hemminger  **************************************************************************/
1713113e88cSStephen Hemminger 
META_COLLECTOR(int_vlan_tag)1723113e88cSStephen Hemminger META_COLLECTOR(int_vlan_tag)
1733113e88cSStephen Hemminger {
1741a31f204SStephen Hemminger 	unsigned short tag;
1751a31f204SStephen Hemminger 
176d65f2fa6SShmulik Ladkani 	if (skb_vlan_tag_present(skb))
177d65f2fa6SShmulik Ladkani 		dst->value = skb_vlan_tag_get(skb);
178d65f2fa6SShmulik Ladkani 	else if (!__vlan_get_tag(skb, &tag))
1793113e88cSStephen Hemminger 		dst->value = tag;
180d65f2fa6SShmulik Ladkani 	else
181d65f2fa6SShmulik Ladkani 		*err = -1;
1823113e88cSStephen Hemminger }
1833113e88cSStephen Hemminger 
1843113e88cSStephen Hemminger 
1853113e88cSStephen Hemminger 
1863113e88cSStephen Hemminger /**************************************************************************
1871da177e4SLinus Torvalds  * skb attributes
1881da177e4SLinus Torvalds  **************************************************************************/
1891da177e4SLinus Torvalds 
META_COLLECTOR(int_priority)1901da177e4SLinus Torvalds META_COLLECTOR(int_priority)
1911da177e4SLinus Torvalds {
1921da177e4SLinus Torvalds 	dst->value = skb->priority;
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds 
META_COLLECTOR(int_protocol)1951da177e4SLinus Torvalds META_COLLECTOR(int_protocol)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	/* Let userspace take care of the byte ordering */
198d7bf2ebeSToke Høiland-Jørgensen 	dst->value = skb_protocol(skb, false);
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
META_COLLECTOR(int_pkttype)2011da177e4SLinus Torvalds META_COLLECTOR(int_pkttype)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	dst->value = skb->pkt_type;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
META_COLLECTOR(int_pktlen)2061da177e4SLinus Torvalds META_COLLECTOR(int_pktlen)
2071da177e4SLinus Torvalds {
2081da177e4SLinus Torvalds 	dst->value = skb->len;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds 
META_COLLECTOR(int_datalen)2111da177e4SLinus Torvalds META_COLLECTOR(int_datalen)
2121da177e4SLinus Torvalds {
2131da177e4SLinus Torvalds 	dst->value = skb->data_len;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds 
META_COLLECTOR(int_maclen)2161da177e4SLinus Torvalds META_COLLECTOR(int_maclen)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	dst->value = skb->mac_len;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
META_COLLECTOR(int_rxhash)221c2e3143eSStephen Hemminger META_COLLECTOR(int_rxhash)
222c2e3143eSStephen Hemminger {
2233958afa1STom Herbert 	dst->value = skb_get_hash(skb);
224c2e3143eSStephen Hemminger }
225c2e3143eSStephen Hemminger 
2261da177e4SLinus Torvalds /**************************************************************************
2271da177e4SLinus Torvalds  * Netfilter
2281da177e4SLinus Torvalds  **************************************************************************/
2291da177e4SLinus Torvalds 
META_COLLECTOR(int_mark)23082e91ffeSThomas Graf META_COLLECTOR(int_mark)
2311da177e4SLinus Torvalds {
23282e91ffeSThomas Graf 	dst->value = skb->mark;
2337686ee1aSPatrick McHardy }
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds /**************************************************************************
2361da177e4SLinus Torvalds  * Traffic Control
2371da177e4SLinus Torvalds  **************************************************************************/
2381da177e4SLinus Torvalds 
META_COLLECTOR(int_tcindex)2391da177e4SLinus Torvalds META_COLLECTOR(int_tcindex)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds 	dst->value = skb->tc_index;
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds /**************************************************************************
2451da177e4SLinus Torvalds  * Routing
2461da177e4SLinus Torvalds  **************************************************************************/
2471da177e4SLinus Torvalds 
META_COLLECTOR(int_rtclassid)2481da177e4SLinus Torvalds META_COLLECTOR(int_rtclassid)
2491da177e4SLinus Torvalds {
250adf30907SEric Dumazet 	if (unlikely(skb_dst(skb) == NULL))
2511da177e4SLinus Torvalds 		*err = -1;
2521da177e4SLinus Torvalds 	else
253c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID
254adf30907SEric Dumazet 		dst->value = skb_dst(skb)->tclassid;
2557686ee1aSPatrick McHardy #else
2567686ee1aSPatrick McHardy 		dst->value = 0;
2571da177e4SLinus Torvalds #endif
2587686ee1aSPatrick McHardy }
2591da177e4SLinus Torvalds 
META_COLLECTOR(int_rtiif)2601da177e4SLinus Torvalds META_COLLECTOR(int_rtiif)
2611da177e4SLinus Torvalds {
262511c3f92SEric Dumazet 	if (unlikely(skb_rtable(skb) == NULL))
2631da177e4SLinus Torvalds 		*err = -1;
2641da177e4SLinus Torvalds 	else
26592101b3bSDavid S. Miller 		dst->value = inet_iif(skb);
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds /**************************************************************************
26948900629SThomas Graf  * Socket Attributes
27048900629SThomas Graf  **************************************************************************/
27148900629SThomas Graf 
2724f8f61ebSYang Yingliang #define skip_nonlocal(skb) \
2734f8f61ebSYang Yingliang 	(unlikely(skb->sk == NULL))
27448900629SThomas Graf 
META_COLLECTOR(int_sk_family)27548900629SThomas Graf META_COLLECTOR(int_sk_family)
27648900629SThomas Graf {
2774f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
2784f8f61ebSYang Yingliang 		*err = -1;
2794f8f61ebSYang Yingliang 		return;
2804f8f61ebSYang Yingliang 	}
28148900629SThomas Graf 	dst->value = skb->sk->sk_family;
28248900629SThomas Graf }
28348900629SThomas Graf 
META_COLLECTOR(int_sk_state)28448900629SThomas Graf META_COLLECTOR(int_sk_state)
28548900629SThomas Graf {
2864f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
2874f8f61ebSYang Yingliang 		*err = -1;
2884f8f61ebSYang Yingliang 		return;
2894f8f61ebSYang Yingliang 	}
29048900629SThomas Graf 	dst->value = skb->sk->sk_state;
29148900629SThomas Graf }
29248900629SThomas Graf 
META_COLLECTOR(int_sk_reuse)29348900629SThomas Graf META_COLLECTOR(int_sk_reuse)
29448900629SThomas Graf {
2954f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
2964f8f61ebSYang Yingliang 		*err = -1;
2974f8f61ebSYang Yingliang 		return;
2984f8f61ebSYang Yingliang 	}
29948900629SThomas Graf 	dst->value = skb->sk->sk_reuse;
30048900629SThomas Graf }
30148900629SThomas Graf 
META_COLLECTOR(int_sk_bound_if)30248900629SThomas Graf META_COLLECTOR(int_sk_bound_if)
30348900629SThomas Graf {
3044f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
3054f8f61ebSYang Yingliang 		*err = -1;
3064f8f61ebSYang Yingliang 		return;
3074f8f61ebSYang Yingliang 	}
30848900629SThomas Graf 	/* No error if bound_dev_if is 0, legal userspace check */
30948900629SThomas Graf 	dst->value = skb->sk->sk_bound_dev_if;
31048900629SThomas Graf }
31148900629SThomas Graf 
META_COLLECTOR(var_sk_bound_if)31248900629SThomas Graf META_COLLECTOR(var_sk_bound_if)
31348900629SThomas Graf {
31470f87de9SEric Dumazet 	int bound_dev_if;
31570f87de9SEric Dumazet 
3164f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
3174f8f61ebSYang Yingliang 		*err = -1;
3184f8f61ebSYang Yingliang 		return;
3194f8f61ebSYang Yingliang 	}
32048900629SThomas Graf 
32170f87de9SEric Dumazet 	bound_dev_if = READ_ONCE(skb->sk->sk_bound_dev_if);
32270f87de9SEric Dumazet 	if (bound_dev_if == 0) {
32348900629SThomas Graf 		dst->value = (unsigned long) "any";
32448900629SThomas Graf 		dst->len = 3;
32548900629SThomas Graf 	} else {
32648900629SThomas Graf 		struct net_device *dev;
32748900629SThomas Graf 
328d0075634SEric Dumazet 		rcu_read_lock();
3292939e275SEric Dumazet 		dev = dev_get_by_index_rcu(sock_net(skb->sk),
33070f87de9SEric Dumazet 					   bound_dev_if);
33148900629SThomas Graf 		*err = var_dev(dev, dst);
332d0075634SEric Dumazet 		rcu_read_unlock();
33348900629SThomas Graf 	}
33448900629SThomas Graf }
33548900629SThomas Graf 
META_COLLECTOR(int_sk_refcnt)33648900629SThomas Graf META_COLLECTOR(int_sk_refcnt)
33748900629SThomas Graf {
3384f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
3394f8f61ebSYang Yingliang 		*err = -1;
3404f8f61ebSYang Yingliang 		return;
3414f8f61ebSYang Yingliang 	}
34241c6d650SReshetova, Elena 	dst->value = refcount_read(&skb->sk->sk_refcnt);
34348900629SThomas Graf }
34448900629SThomas Graf 
META_COLLECTOR(int_sk_rcvbuf)34548900629SThomas Graf META_COLLECTOR(int_sk_rcvbuf)
34648900629SThomas Graf {
34702a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
34802a56c81SEric Dumazet 
34902a56c81SEric Dumazet 	if (!sk) {
3504f8f61ebSYang Yingliang 		*err = -1;
3514f8f61ebSYang Yingliang 		return;
3524f8f61ebSYang Yingliang 	}
35302a56c81SEric Dumazet 	dst->value = sk->sk_rcvbuf;
35448900629SThomas Graf }
35548900629SThomas Graf 
META_COLLECTOR(int_sk_shutdown)35648900629SThomas Graf META_COLLECTOR(int_sk_shutdown)
35748900629SThomas Graf {
35802a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
35902a56c81SEric Dumazet 
36002a56c81SEric Dumazet 	if (!sk) {
3614f8f61ebSYang Yingliang 		*err = -1;
3624f8f61ebSYang Yingliang 		return;
3634f8f61ebSYang Yingliang 	}
36402a56c81SEric Dumazet 	dst->value = sk->sk_shutdown;
36548900629SThomas Graf }
36648900629SThomas Graf 
META_COLLECTOR(int_sk_proto)36748900629SThomas Graf META_COLLECTOR(int_sk_proto)
36848900629SThomas Graf {
36902a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
37002a56c81SEric Dumazet 
37102a56c81SEric Dumazet 	if (!sk) {
3724f8f61ebSYang Yingliang 		*err = -1;
3734f8f61ebSYang Yingliang 		return;
3744f8f61ebSYang Yingliang 	}
37502a56c81SEric Dumazet 	dst->value = sk->sk_protocol;
37648900629SThomas Graf }
37748900629SThomas Graf 
META_COLLECTOR(int_sk_type)37848900629SThomas Graf META_COLLECTOR(int_sk_type)
37948900629SThomas Graf {
38002a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
38102a56c81SEric Dumazet 
38202a56c81SEric Dumazet 	if (!sk) {
3834f8f61ebSYang Yingliang 		*err = -1;
3844f8f61ebSYang Yingliang 		return;
3854f8f61ebSYang Yingliang 	}
38602a56c81SEric Dumazet 	dst->value = sk->sk_type;
38748900629SThomas Graf }
38848900629SThomas Graf 
META_COLLECTOR(int_sk_rmem_alloc)38948900629SThomas Graf META_COLLECTOR(int_sk_rmem_alloc)
39048900629SThomas Graf {
39102a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
39202a56c81SEric Dumazet 
39302a56c81SEric Dumazet 	if (!sk) {
3944f8f61ebSYang Yingliang 		*err = -1;
3954f8f61ebSYang Yingliang 		return;
3964f8f61ebSYang Yingliang 	}
39702a56c81SEric Dumazet 	dst->value = sk_rmem_alloc_get(sk);
39848900629SThomas Graf }
39948900629SThomas Graf 
META_COLLECTOR(int_sk_wmem_alloc)40048900629SThomas Graf META_COLLECTOR(int_sk_wmem_alloc)
40148900629SThomas Graf {
40202a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
40302a56c81SEric Dumazet 
40402a56c81SEric Dumazet 	if (!sk) {
4054f8f61ebSYang Yingliang 		*err = -1;
4064f8f61ebSYang Yingliang 		return;
4074f8f61ebSYang Yingliang 	}
40802a56c81SEric Dumazet 	dst->value = sk_wmem_alloc_get(sk);
40948900629SThomas Graf }
41048900629SThomas Graf 
META_COLLECTOR(int_sk_omem_alloc)41148900629SThomas Graf META_COLLECTOR(int_sk_omem_alloc)
41248900629SThomas Graf {
41302a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
41402a56c81SEric Dumazet 
41502a56c81SEric Dumazet 	if (!sk) {
4164f8f61ebSYang Yingliang 		*err = -1;
4174f8f61ebSYang Yingliang 		return;
4184f8f61ebSYang Yingliang 	}
41902a56c81SEric Dumazet 	dst->value = atomic_read(&sk->sk_omem_alloc);
42048900629SThomas Graf }
42148900629SThomas Graf 
META_COLLECTOR(int_sk_rcv_qlen)42248900629SThomas Graf META_COLLECTOR(int_sk_rcv_qlen)
42348900629SThomas Graf {
42402a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
42502a56c81SEric Dumazet 
42602a56c81SEric Dumazet 	if (!sk) {
4274f8f61ebSYang Yingliang 		*err = -1;
4284f8f61ebSYang Yingliang 		return;
4294f8f61ebSYang Yingliang 	}
43002a56c81SEric Dumazet 	dst->value = sk->sk_receive_queue.qlen;
43148900629SThomas Graf }
43248900629SThomas Graf 
META_COLLECTOR(int_sk_snd_qlen)43348900629SThomas Graf META_COLLECTOR(int_sk_snd_qlen)
43448900629SThomas Graf {
43502a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
43602a56c81SEric Dumazet 
43702a56c81SEric Dumazet 	if (!sk) {
4384f8f61ebSYang Yingliang 		*err = -1;
4394f8f61ebSYang Yingliang 		return;
4404f8f61ebSYang Yingliang 	}
44102a56c81SEric Dumazet 	dst->value = sk->sk_write_queue.qlen;
44248900629SThomas Graf }
44348900629SThomas Graf 
META_COLLECTOR(int_sk_wmem_queued)44448900629SThomas Graf META_COLLECTOR(int_sk_wmem_queued)
44548900629SThomas Graf {
44602a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
44702a56c81SEric Dumazet 
44802a56c81SEric Dumazet 	if (!sk) {
4494f8f61ebSYang Yingliang 		*err = -1;
4504f8f61ebSYang Yingliang 		return;
4514f8f61ebSYang Yingliang 	}
452ab4e846aSEric Dumazet 	dst->value = READ_ONCE(sk->sk_wmem_queued);
45348900629SThomas Graf }
45448900629SThomas Graf 
META_COLLECTOR(int_sk_fwd_alloc)45548900629SThomas Graf META_COLLECTOR(int_sk_fwd_alloc)
45648900629SThomas Graf {
45702a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
45802a56c81SEric Dumazet 
45902a56c81SEric Dumazet 	if (!sk) {
4604f8f61ebSYang Yingliang 		*err = -1;
4614f8f61ebSYang Yingliang 		return;
4624f8f61ebSYang Yingliang 	}
463292e6077SPaolo Abeni 	dst->value = sk_forward_alloc_get(sk);
46448900629SThomas Graf }
46548900629SThomas Graf 
META_COLLECTOR(int_sk_sndbuf)46648900629SThomas Graf META_COLLECTOR(int_sk_sndbuf)
46748900629SThomas Graf {
46802a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
46902a56c81SEric Dumazet 
47002a56c81SEric Dumazet 	if (!sk) {
4714f8f61ebSYang Yingliang 		*err = -1;
4724f8f61ebSYang Yingliang 		return;
4734f8f61ebSYang Yingliang 	}
47402a56c81SEric Dumazet 	dst->value = sk->sk_sndbuf;
47548900629SThomas Graf }
47648900629SThomas Graf 
META_COLLECTOR(int_sk_alloc)47748900629SThomas Graf META_COLLECTOR(int_sk_alloc)
47848900629SThomas Graf {
47902a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
48002a56c81SEric Dumazet 
48102a56c81SEric Dumazet 	if (!sk) {
4824f8f61ebSYang Yingliang 		*err = -1;
4834f8f61ebSYang Yingliang 		return;
4844f8f61ebSYang Yingliang 	}
48502a56c81SEric Dumazet 	dst->value = (__force int) sk->sk_allocation;
48648900629SThomas Graf }
48748900629SThomas Graf 
META_COLLECTOR(int_sk_hash)48881c3d547SEric Dumazet META_COLLECTOR(int_sk_hash)
48948900629SThomas Graf {
4904f8f61ebSYang Yingliang 	if (skip_nonlocal(skb)) {
4914f8f61ebSYang Yingliang 		*err = -1;
4924f8f61ebSYang Yingliang 		return;
4934f8f61ebSYang Yingliang 	}
49481c3d547SEric Dumazet 	dst->value = skb->sk->sk_hash;
49548900629SThomas Graf }
49648900629SThomas Graf 
META_COLLECTOR(int_sk_lingertime)49748900629SThomas Graf META_COLLECTOR(int_sk_lingertime)
49848900629SThomas Graf {
49902a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
50002a56c81SEric Dumazet 
50102a56c81SEric Dumazet 	if (!sk) {
5024f8f61ebSYang Yingliang 		*err = -1;
5034f8f61ebSYang Yingliang 		return;
5044f8f61ebSYang Yingliang 	}
505*bc1fb82aSEric Dumazet 	dst->value = READ_ONCE(sk->sk_lingertime) / HZ;
50648900629SThomas Graf }
50748900629SThomas Graf 
META_COLLECTOR(int_sk_err_qlen)50848900629SThomas Graf META_COLLECTOR(int_sk_err_qlen)
50948900629SThomas Graf {
51002a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
51102a56c81SEric Dumazet 
51202a56c81SEric Dumazet 	if (!sk) {
5134f8f61ebSYang Yingliang 		*err = -1;
5144f8f61ebSYang Yingliang 		return;
5154f8f61ebSYang Yingliang 	}
51602a56c81SEric Dumazet 	dst->value = sk->sk_error_queue.qlen;
51748900629SThomas Graf }
51848900629SThomas Graf 
META_COLLECTOR(int_sk_ack_bl)51948900629SThomas Graf META_COLLECTOR(int_sk_ack_bl)
52048900629SThomas Graf {
52102a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
52202a56c81SEric Dumazet 
52302a56c81SEric Dumazet 	if (!sk) {
5244f8f61ebSYang Yingliang 		*err = -1;
5254f8f61ebSYang Yingliang 		return;
5264f8f61ebSYang Yingliang 	}
527288efe86SEric Dumazet 	dst->value = READ_ONCE(sk->sk_ack_backlog);
52848900629SThomas Graf }
52948900629SThomas Graf 
META_COLLECTOR(int_sk_max_ack_bl)53048900629SThomas Graf META_COLLECTOR(int_sk_max_ack_bl)
53148900629SThomas Graf {
53202a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
53302a56c81SEric Dumazet 
53402a56c81SEric Dumazet 	if (!sk) {
5354f8f61ebSYang Yingliang 		*err = -1;
5364f8f61ebSYang Yingliang 		return;
5374f8f61ebSYang Yingliang 	}
538099ecf59SEric Dumazet 	dst->value = READ_ONCE(sk->sk_max_ack_backlog);
53948900629SThomas Graf }
54048900629SThomas Graf 
META_COLLECTOR(int_sk_prio)54148900629SThomas Graf META_COLLECTOR(int_sk_prio)
54248900629SThomas Graf {
54302a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
54402a56c81SEric Dumazet 
54502a56c81SEric Dumazet 	if (!sk) {
5464f8f61ebSYang Yingliang 		*err = -1;
5474f8f61ebSYang Yingliang 		return;
5484f8f61ebSYang Yingliang 	}
54902a56c81SEric Dumazet 	dst->value = sk->sk_priority;
55048900629SThomas Graf }
55148900629SThomas Graf 
META_COLLECTOR(int_sk_rcvlowat)55248900629SThomas Graf META_COLLECTOR(int_sk_rcvlowat)
55348900629SThomas Graf {
55402a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
55502a56c81SEric Dumazet 
55602a56c81SEric Dumazet 	if (!sk) {
5574f8f61ebSYang Yingliang 		*err = -1;
5584f8f61ebSYang Yingliang 		return;
5594f8f61ebSYang Yingliang 	}
560eac66402SEric Dumazet 	dst->value = READ_ONCE(sk->sk_rcvlowat);
56148900629SThomas Graf }
56248900629SThomas Graf 
META_COLLECTOR(int_sk_rcvtimeo)56348900629SThomas Graf META_COLLECTOR(int_sk_rcvtimeo)
56448900629SThomas Graf {
56502a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
56602a56c81SEric Dumazet 
56702a56c81SEric Dumazet 	if (!sk) {
5684f8f61ebSYang Yingliang 		*err = -1;
5694f8f61ebSYang Yingliang 		return;
5704f8f61ebSYang Yingliang 	}
571285975ddSEric Dumazet 	dst->value = READ_ONCE(sk->sk_rcvtimeo) / HZ;
57248900629SThomas Graf }
57348900629SThomas Graf 
META_COLLECTOR(int_sk_sndtimeo)57448900629SThomas Graf META_COLLECTOR(int_sk_sndtimeo)
57548900629SThomas Graf {
57602a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
57702a56c81SEric Dumazet 
57802a56c81SEric Dumazet 	if (!sk) {
5794f8f61ebSYang Yingliang 		*err = -1;
5804f8f61ebSYang Yingliang 		return;
5814f8f61ebSYang Yingliang 	}
582285975ddSEric Dumazet 	dst->value = READ_ONCE(sk->sk_sndtimeo) / HZ;
58348900629SThomas Graf }
58448900629SThomas Graf 
META_COLLECTOR(int_sk_sendmsg_off)58548900629SThomas Graf META_COLLECTOR(int_sk_sendmsg_off)
58648900629SThomas Graf {
58702a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
58802a56c81SEric Dumazet 
58902a56c81SEric Dumazet 	if (!sk) {
5904f8f61ebSYang Yingliang 		*err = -1;
5914f8f61ebSYang Yingliang 		return;
5924f8f61ebSYang Yingliang 	}
59302a56c81SEric Dumazet 	dst->value = sk->sk_frag.offset;
59448900629SThomas Graf }
59548900629SThomas Graf 
META_COLLECTOR(int_sk_write_pend)59648900629SThomas Graf META_COLLECTOR(int_sk_write_pend)
59748900629SThomas Graf {
59802a56c81SEric Dumazet 	const struct sock *sk = skb_to_full_sk(skb);
59902a56c81SEric Dumazet 
60002a56c81SEric Dumazet 	if (!sk) {
6014f8f61ebSYang Yingliang 		*err = -1;
6024f8f61ebSYang Yingliang 		return;
6034f8f61ebSYang Yingliang 	}
60402a56c81SEric Dumazet 	dst->value = sk->sk_write_pending;
60548900629SThomas Graf }
60648900629SThomas Graf 
60748900629SThomas Graf /**************************************************************************
6081da177e4SLinus Torvalds  * Meta value collectors assignment table
6091da177e4SLinus Torvalds  **************************************************************************/
6101da177e4SLinus Torvalds 
611cc7ec456SEric Dumazet struct meta_ops {
6121da177e4SLinus Torvalds 	void		(*get)(struct sk_buff *, struct tcf_pkt_info *,
6131da177e4SLinus Torvalds 			       struct meta_value *, struct meta_obj *, int *);
6141da177e4SLinus Torvalds };
6151da177e4SLinus Torvalds 
61648900629SThomas Graf #define META_ID(name) TCF_META_ID_##name
61748900629SThomas Graf #define META_FUNC(name) { .get = meta_##name }
61848900629SThomas Graf 
6191da177e4SLinus Torvalds /* Meta value operations table listing all meta value collectors and
6201da177e4SLinus Torvalds  * assigns them to a type and meta id. */
6211da177e4SLinus Torvalds static struct meta_ops __meta_ops[TCF_META_TYPE_MAX + 1][TCF_META_ID_MAX + 1] = {
6221da177e4SLinus Torvalds 	[TCF_META_TYPE_VAR] = {
62348900629SThomas Graf 		[META_ID(DEV)]			= META_FUNC(var_dev),
62448900629SThomas Graf 		[META_ID(SK_BOUND_IF)] 		= META_FUNC(var_sk_bound_if),
6251da177e4SLinus Torvalds 	},
6261da177e4SLinus Torvalds 	[TCF_META_TYPE_INT] = {
62748900629SThomas Graf 		[META_ID(RANDOM)]		= META_FUNC(int_random),
62848900629SThomas Graf 		[META_ID(LOADAVG_0)]		= META_FUNC(int_loadavg_0),
62948900629SThomas Graf 		[META_ID(LOADAVG_1)]		= META_FUNC(int_loadavg_1),
63048900629SThomas Graf 		[META_ID(LOADAVG_2)]		= META_FUNC(int_loadavg_2),
63148900629SThomas Graf 		[META_ID(DEV)]			= META_FUNC(int_dev),
63248900629SThomas Graf 		[META_ID(PRIORITY)]		= META_FUNC(int_priority),
63348900629SThomas Graf 		[META_ID(PROTOCOL)]		= META_FUNC(int_protocol),
63448900629SThomas Graf 		[META_ID(PKTTYPE)]		= META_FUNC(int_pkttype),
63548900629SThomas Graf 		[META_ID(PKTLEN)]		= META_FUNC(int_pktlen),
63648900629SThomas Graf 		[META_ID(DATALEN)]		= META_FUNC(int_datalen),
63748900629SThomas Graf 		[META_ID(MACLEN)]		= META_FUNC(int_maclen),
63882e91ffeSThomas Graf 		[META_ID(NFMARK)]		= META_FUNC(int_mark),
63948900629SThomas Graf 		[META_ID(TCINDEX)]		= META_FUNC(int_tcindex),
64048900629SThomas Graf 		[META_ID(RTCLASSID)]		= META_FUNC(int_rtclassid),
64148900629SThomas Graf 		[META_ID(RTIIF)]		= META_FUNC(int_rtiif),
64248900629SThomas Graf 		[META_ID(SK_FAMILY)]		= META_FUNC(int_sk_family),
64348900629SThomas Graf 		[META_ID(SK_STATE)]		= META_FUNC(int_sk_state),
64448900629SThomas Graf 		[META_ID(SK_REUSE)]		= META_FUNC(int_sk_reuse),
64548900629SThomas Graf 		[META_ID(SK_BOUND_IF)]		= META_FUNC(int_sk_bound_if),
64648900629SThomas Graf 		[META_ID(SK_REFCNT)]		= META_FUNC(int_sk_refcnt),
64748900629SThomas Graf 		[META_ID(SK_RCVBUF)]		= META_FUNC(int_sk_rcvbuf),
64848900629SThomas Graf 		[META_ID(SK_SNDBUF)]		= META_FUNC(int_sk_sndbuf),
64948900629SThomas Graf 		[META_ID(SK_SHUTDOWN)]		= META_FUNC(int_sk_shutdown),
65048900629SThomas Graf 		[META_ID(SK_PROTO)]		= META_FUNC(int_sk_proto),
65148900629SThomas Graf 		[META_ID(SK_TYPE)]		= META_FUNC(int_sk_type),
65248900629SThomas Graf 		[META_ID(SK_RMEM_ALLOC)]	= META_FUNC(int_sk_rmem_alloc),
65348900629SThomas Graf 		[META_ID(SK_WMEM_ALLOC)]	= META_FUNC(int_sk_wmem_alloc),
65448900629SThomas Graf 		[META_ID(SK_OMEM_ALLOC)]	= META_FUNC(int_sk_omem_alloc),
65548900629SThomas Graf 		[META_ID(SK_WMEM_QUEUED)]	= META_FUNC(int_sk_wmem_queued),
65648900629SThomas Graf 		[META_ID(SK_RCV_QLEN)]		= META_FUNC(int_sk_rcv_qlen),
65748900629SThomas Graf 		[META_ID(SK_SND_QLEN)]		= META_FUNC(int_sk_snd_qlen),
65848900629SThomas Graf 		[META_ID(SK_ERR_QLEN)]		= META_FUNC(int_sk_err_qlen),
65948900629SThomas Graf 		[META_ID(SK_FORWARD_ALLOCS)]	= META_FUNC(int_sk_fwd_alloc),
66048900629SThomas Graf 		[META_ID(SK_ALLOCS)]		= META_FUNC(int_sk_alloc),
66181c3d547SEric Dumazet 		[META_ID(SK_HASH)]		= META_FUNC(int_sk_hash),
66248900629SThomas Graf 		[META_ID(SK_LINGERTIME)]	= META_FUNC(int_sk_lingertime),
66348900629SThomas Graf 		[META_ID(SK_ACK_BACKLOG)]	= META_FUNC(int_sk_ack_bl),
66448900629SThomas Graf 		[META_ID(SK_MAX_ACK_BACKLOG)]	= META_FUNC(int_sk_max_ack_bl),
66548900629SThomas Graf 		[META_ID(SK_PRIO)]		= META_FUNC(int_sk_prio),
66648900629SThomas Graf 		[META_ID(SK_RCVLOWAT)]		= META_FUNC(int_sk_rcvlowat),
66748900629SThomas Graf 		[META_ID(SK_RCVTIMEO)]		= META_FUNC(int_sk_rcvtimeo),
66848900629SThomas Graf 		[META_ID(SK_SNDTIMEO)]		= META_FUNC(int_sk_sndtimeo),
66948900629SThomas Graf 		[META_ID(SK_SENDMSG_OFF)]	= META_FUNC(int_sk_sendmsg_off),
67048900629SThomas Graf 		[META_ID(SK_WRITE_PENDING)]	= META_FUNC(int_sk_write_pend),
6713113e88cSStephen Hemminger 		[META_ID(VLAN_TAG)]		= META_FUNC(int_vlan_tag),
672c2e3143eSStephen Hemminger 		[META_ID(RXHASH)]		= META_FUNC(int_rxhash),
6731da177e4SLinus Torvalds 	}
6741da177e4SLinus Torvalds };
6751da177e4SLinus Torvalds 
meta_ops(struct meta_value * val)6761da177e4SLinus Torvalds static inline struct meta_ops *meta_ops(struct meta_value *val)
6771da177e4SLinus Torvalds {
6781da177e4SLinus Torvalds 	return &__meta_ops[meta_type(val)][meta_id(val)];
6791da177e4SLinus Torvalds }
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds /**************************************************************************
6821da177e4SLinus Torvalds  * Type specific operations for TCF_META_TYPE_VAR
6831da177e4SLinus Torvalds  **************************************************************************/
6841da177e4SLinus Torvalds 
meta_var_compare(struct meta_obj * a,struct meta_obj * b)6851da177e4SLinus Torvalds static int meta_var_compare(struct meta_obj *a, struct meta_obj *b)
6861da177e4SLinus Torvalds {
6871da177e4SLinus Torvalds 	int r = a->len - b->len;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	if (r == 0)
6901da177e4SLinus Torvalds 		r = memcmp((void *) a->value, (void *) b->value, a->len);
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds 	return r;
6931da177e4SLinus Torvalds }
6941da177e4SLinus Torvalds 
meta_var_change(struct meta_value * dst,struct nlattr * nla)695add93b61SPatrick McHardy static int meta_var_change(struct meta_value *dst, struct nlattr *nla)
6961da177e4SLinus Torvalds {
697add93b61SPatrick McHardy 	int len = nla_len(nla);
6981da177e4SLinus Torvalds 
699add93b61SPatrick McHardy 	dst->val = (unsigned long)kmemdup(nla_data(nla), len, GFP_KERNEL);
7001da177e4SLinus Torvalds 	if (dst->val == 0UL)
7011da177e4SLinus Torvalds 		return -ENOMEM;
7021da177e4SLinus Torvalds 	dst->len = len;
7031da177e4SLinus Torvalds 	return 0;
7041da177e4SLinus Torvalds }
7051da177e4SLinus Torvalds 
meta_var_destroy(struct meta_value * v)7061da177e4SLinus Torvalds static void meta_var_destroy(struct meta_value *v)
7071da177e4SLinus Torvalds {
7081da177e4SLinus Torvalds 	kfree((void *) v->val);
7091da177e4SLinus Torvalds }
7101da177e4SLinus Torvalds 
meta_var_apply_extras(struct meta_value * v,struct meta_obj * dst)7111da177e4SLinus Torvalds static void meta_var_apply_extras(struct meta_value *v,
7121da177e4SLinus Torvalds 				  struct meta_obj *dst)
7131da177e4SLinus Torvalds {
7141da177e4SLinus Torvalds 	int shift = v->hdr.shift;
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds 	if (shift && shift < dst->len)
7171da177e4SLinus Torvalds 		dst->len -= shift;
7181da177e4SLinus Torvalds }
7191da177e4SLinus Torvalds 
meta_var_dump(struct sk_buff * skb,struct meta_value * v,int tlv)7201da177e4SLinus Torvalds static int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
7211da177e4SLinus Torvalds {
7221b34ec43SDavid S. Miller 	if (v->val && v->len &&
7231b34ec43SDavid S. Miller 	    nla_put(skb, tlv, v->len, (void *) v->val))
7241b34ec43SDavid S. Miller 		goto nla_put_failure;
7251da177e4SLinus Torvalds 	return 0;
7261da177e4SLinus Torvalds 
727add93b61SPatrick McHardy nla_put_failure:
7281da177e4SLinus Torvalds 	return -1;
7291da177e4SLinus Torvalds }
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds /**************************************************************************
7321da177e4SLinus Torvalds  * Type specific operations for TCF_META_TYPE_INT
7331da177e4SLinus Torvalds  **************************************************************************/
7341da177e4SLinus Torvalds 
meta_int_compare(struct meta_obj * a,struct meta_obj * b)7351da177e4SLinus Torvalds static int meta_int_compare(struct meta_obj *a, struct meta_obj *b)
7361da177e4SLinus Torvalds {
7371da177e4SLinus Torvalds 	/* Let gcc optimize it, the unlikely is not really based on
7381da177e4SLinus Torvalds 	 * some numbers but jump free code for mismatches seems
7391da177e4SLinus Torvalds 	 * more logical. */
74098e56405SThomas Graf 	if (unlikely(a->value == b->value))
7411da177e4SLinus Torvalds 		return 0;
74298e56405SThomas Graf 	else if (a->value < b->value)
7431da177e4SLinus Torvalds 		return -1;
7441da177e4SLinus Torvalds 	else
7451da177e4SLinus Torvalds 		return 1;
7461da177e4SLinus Torvalds }
7471da177e4SLinus Torvalds 
meta_int_change(struct meta_value * dst,struct nlattr * nla)748add93b61SPatrick McHardy static int meta_int_change(struct meta_value *dst, struct nlattr *nla)
7491da177e4SLinus Torvalds {
750add93b61SPatrick McHardy 	if (nla_len(nla) >= sizeof(unsigned long)) {
751add93b61SPatrick McHardy 		dst->val = *(unsigned long *) nla_data(nla);
7521da177e4SLinus Torvalds 		dst->len = sizeof(unsigned long);
753add93b61SPatrick McHardy 	} else if (nla_len(nla) == sizeof(u32)) {
7541587bac4SPatrick McHardy 		dst->val = nla_get_u32(nla);
7551da177e4SLinus Torvalds 		dst->len = sizeof(u32);
7561da177e4SLinus Torvalds 	} else
7571da177e4SLinus Torvalds 		return -EINVAL;
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	return 0;
7601da177e4SLinus Torvalds }
7611da177e4SLinus Torvalds 
meta_int_apply_extras(struct meta_value * v,struct meta_obj * dst)7621da177e4SLinus Torvalds static void meta_int_apply_extras(struct meta_value *v,
7631da177e4SLinus Torvalds 				  struct meta_obj *dst)
7641da177e4SLinus Torvalds {
7651da177e4SLinus Torvalds 	if (v->hdr.shift)
7661da177e4SLinus Torvalds 		dst->value >>= v->hdr.shift;
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds 	if (v->val)
7691da177e4SLinus Torvalds 		dst->value &= v->val;
7701da177e4SLinus Torvalds }
7711da177e4SLinus Torvalds 
meta_int_dump(struct sk_buff * skb,struct meta_value * v,int tlv)7721da177e4SLinus Torvalds static int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
7731da177e4SLinus Torvalds {
7741b34ec43SDavid S. Miller 	if (v->len == sizeof(unsigned long)) {
7751b34ec43SDavid S. Miller 		if (nla_put(skb, tlv, sizeof(unsigned long), &v->val))
7761b34ec43SDavid S. Miller 			goto nla_put_failure;
7771b34ec43SDavid S. Miller 	} else if (v->len == sizeof(u32)) {
7781b34ec43SDavid S. Miller 		if (nla_put_u32(skb, tlv, v->val))
7791b34ec43SDavid S. Miller 			goto nla_put_failure;
7801b34ec43SDavid S. Miller 	}
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	return 0;
7831da177e4SLinus Torvalds 
784add93b61SPatrick McHardy nla_put_failure:
7851da177e4SLinus Torvalds 	return -1;
7861da177e4SLinus Torvalds }
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds /**************************************************************************
7891da177e4SLinus Torvalds  * Type specific operations table
7901da177e4SLinus Torvalds  **************************************************************************/
7911da177e4SLinus Torvalds 
792cc7ec456SEric Dumazet struct meta_type_ops {
7931da177e4SLinus Torvalds 	void	(*destroy)(struct meta_value *);
7941da177e4SLinus Torvalds 	int	(*compare)(struct meta_obj *, struct meta_obj *);
795add93b61SPatrick McHardy 	int	(*change)(struct meta_value *, struct nlattr *);
7961da177e4SLinus Torvalds 	void	(*apply_extras)(struct meta_value *, struct meta_obj *);
7971da177e4SLinus Torvalds 	int	(*dump)(struct sk_buff *, struct meta_value *, int);
7981da177e4SLinus Torvalds };
7991da177e4SLinus Torvalds 
800cfe2f14cSJulia Lawall static const struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX + 1] = {
8011da177e4SLinus Torvalds 	[TCF_META_TYPE_VAR] = {
8021da177e4SLinus Torvalds 		.destroy = meta_var_destroy,
8031da177e4SLinus Torvalds 		.compare = meta_var_compare,
8041da177e4SLinus Torvalds 		.change = meta_var_change,
8051da177e4SLinus Torvalds 		.apply_extras = meta_var_apply_extras,
8061da177e4SLinus Torvalds 		.dump = meta_var_dump
8071da177e4SLinus Torvalds 	},
8081da177e4SLinus Torvalds 	[TCF_META_TYPE_INT] = {
8091da177e4SLinus Torvalds 		.compare = meta_int_compare,
8101da177e4SLinus Torvalds 		.change = meta_int_change,
8111da177e4SLinus Torvalds 		.apply_extras = meta_int_apply_extras,
8121da177e4SLinus Torvalds 		.dump = meta_int_dump
8131da177e4SLinus Torvalds 	}
8141da177e4SLinus Torvalds };
8151da177e4SLinus Torvalds 
meta_type_ops(struct meta_value * v)816cfe2f14cSJulia Lawall static inline const struct meta_type_ops *meta_type_ops(struct meta_value *v)
8171da177e4SLinus Torvalds {
8181da177e4SLinus Torvalds 	return &__meta_type_ops[meta_type(v)];
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds /**************************************************************************
8221da177e4SLinus Torvalds  * Core
8231da177e4SLinus Torvalds  **************************************************************************/
8241da177e4SLinus Torvalds 
meta_get(struct sk_buff * skb,struct tcf_pkt_info * info,struct meta_value * v,struct meta_obj * dst)825ed7af3b3SStephen Hemminger static int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info,
8261da177e4SLinus Torvalds 		    struct meta_value *v, struct meta_obj *dst)
8271da177e4SLinus Torvalds {
8281da177e4SLinus Torvalds 	int err = 0;
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds 	if (meta_id(v) == TCF_META_ID_VALUE) {
8311da177e4SLinus Torvalds 		dst->value = v->val;
8321da177e4SLinus Torvalds 		dst->len = v->len;
8331da177e4SLinus Torvalds 		return 0;
8341da177e4SLinus Torvalds 	}
8351da177e4SLinus Torvalds 
8361da177e4SLinus Torvalds 	meta_ops(v)->get(skb, info, v, dst, &err);
8371da177e4SLinus Torvalds 	if (err < 0)
8381da177e4SLinus Torvalds 		return err;
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	if (meta_type_ops(v)->apply_extras)
8411da177e4SLinus Torvalds 		meta_type_ops(v)->apply_extras(v, dst);
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds 	return 0;
8441da177e4SLinus Torvalds }
8451da177e4SLinus Torvalds 
em_meta_match(struct sk_buff * skb,struct tcf_ematch * m,struct tcf_pkt_info * info)8461da177e4SLinus Torvalds static int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m,
8471da177e4SLinus Torvalds 			 struct tcf_pkt_info *info)
8481da177e4SLinus Torvalds {
8491da177e4SLinus Torvalds 	int r;
8501da177e4SLinus Torvalds 	struct meta_match *meta = (struct meta_match *) m->data;
8511da177e4SLinus Torvalds 	struct meta_obj l_value, r_value;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 ||
8541da177e4SLinus Torvalds 	    meta_get(skb, info, &meta->rvalue, &r_value) < 0)
8551da177e4SLinus Torvalds 		return 0;
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds 	r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value);
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 	switch (meta->lvalue.hdr.op) {
8601da177e4SLinus Torvalds 	case TCF_EM_OPND_EQ:
8611da177e4SLinus Torvalds 		return !r;
8621da177e4SLinus Torvalds 	case TCF_EM_OPND_LT:
8631da177e4SLinus Torvalds 		return r < 0;
8641da177e4SLinus Torvalds 	case TCF_EM_OPND_GT:
8651da177e4SLinus Torvalds 		return r > 0;
8661da177e4SLinus Torvalds 	}
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	return 0;
8691da177e4SLinus Torvalds }
8701da177e4SLinus Torvalds 
meta_delete(struct meta_match * meta)871ed7af3b3SStephen Hemminger static void meta_delete(struct meta_match *meta)
8721da177e4SLinus Torvalds {
87304f217acSStephen Hemminger 	if (meta) {
874cfe2f14cSJulia Lawall 		const struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 		if (ops && ops->destroy) {
8771da177e4SLinus Torvalds 			ops->destroy(&meta->lvalue);
8781da177e4SLinus Torvalds 			ops->destroy(&meta->rvalue);
8791da177e4SLinus Torvalds 		}
88004f217acSStephen Hemminger 	}
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	kfree(meta);
8831da177e4SLinus Torvalds }
8841da177e4SLinus Torvalds 
meta_change_data(struct meta_value * dst,struct nlattr * nla)885add93b61SPatrick McHardy static inline int meta_change_data(struct meta_value *dst, struct nlattr *nla)
8861da177e4SLinus Torvalds {
887add93b61SPatrick McHardy 	if (nla) {
888add93b61SPatrick McHardy 		if (nla_len(nla) == 0)
8891da177e4SLinus Torvalds 			return -EINVAL;
8901da177e4SLinus Torvalds 
891add93b61SPatrick McHardy 		return meta_type_ops(dst)->change(dst, nla);
8921da177e4SLinus Torvalds 	}
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	return 0;
8951da177e4SLinus Torvalds }
8961da177e4SLinus Torvalds 
meta_is_supported(struct meta_value * val)8971da177e4SLinus Torvalds static inline int meta_is_supported(struct meta_value *val)
8981da177e4SLinus Torvalds {
899cc7ec456SEric Dumazet 	return !meta_id(val) || meta_ops(val)->get;
9001da177e4SLinus Torvalds }
9011da177e4SLinus Torvalds 
9027a9c1bd4SPatrick McHardy static const struct nla_policy meta_policy[TCA_EM_META_MAX + 1] = {
9037a9c1bd4SPatrick McHardy 	[TCA_EM_META_HDR]	= { .len = sizeof(struct tcf_meta_hdr) },
9047a9c1bd4SPatrick McHardy };
9057a9c1bd4SPatrick McHardy 
em_meta_change(struct net * net,void * data,int len,struct tcf_ematch * m)90682a470f1SJohn Fastabend static int em_meta_change(struct net *net, void *data, int len,
9071da177e4SLinus Torvalds 			  struct tcf_ematch *m)
9081da177e4SLinus Torvalds {
909cee63723SPatrick McHardy 	int err;
910add93b61SPatrick McHardy 	struct nlattr *tb[TCA_EM_META_MAX + 1];
9111da177e4SLinus Torvalds 	struct tcf_meta_hdr *hdr;
9121da177e4SLinus Torvalds 	struct meta_match *meta = NULL;
9131da177e4SLinus Torvalds 
9148cb08174SJohannes Berg 	err = nla_parse_deprecated(tb, TCA_EM_META_MAX, data, len,
9158cb08174SJohannes Berg 				   meta_policy, NULL);
916cee63723SPatrick McHardy 	if (err < 0)
9171da177e4SLinus Torvalds 		goto errout;
9181da177e4SLinus Torvalds 
919cee63723SPatrick McHardy 	err = -EINVAL;
9207a9c1bd4SPatrick McHardy 	if (tb[TCA_EM_META_HDR] == NULL)
9211da177e4SLinus Torvalds 		goto errout;
922add93b61SPatrick McHardy 	hdr = nla_data(tb[TCA_EM_META_HDR]);
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 	if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) ||
9251da177e4SLinus Torvalds 	    TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX ||
9261da177e4SLinus Torvalds 	    TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX ||
9271da177e4SLinus Torvalds 	    TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX)
9281da177e4SLinus Torvalds 		goto errout;
9291da177e4SLinus Torvalds 
9300da974f4SPanagiotis Issaris 	meta = kzalloc(sizeof(*meta), GFP_KERNEL);
9310c4e4020Sstephen hemminger 	if (meta == NULL) {
9320c4e4020Sstephen hemminger 		err = -ENOMEM;
9331da177e4SLinus Torvalds 		goto errout;
9340c4e4020Sstephen hemminger 	}
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
9371da177e4SLinus Torvalds 	memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 	if (!meta_is_supported(&meta->lvalue) ||
9401da177e4SLinus Torvalds 	    !meta_is_supported(&meta->rvalue)) {
9411da177e4SLinus Torvalds 		err = -EOPNOTSUPP;
9421da177e4SLinus Torvalds 		goto errout;
9431da177e4SLinus Torvalds 	}
9441da177e4SLinus Torvalds 
945add93b61SPatrick McHardy 	if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE]) < 0 ||
946add93b61SPatrick McHardy 	    meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE]) < 0)
9471da177e4SLinus Torvalds 		goto errout;
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 	m->datalen = sizeof(*meta);
9501da177e4SLinus Torvalds 	m->data = (unsigned long) meta;
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	err = 0;
9531da177e4SLinus Torvalds errout:
9541da177e4SLinus Torvalds 	if (err && meta)
9551da177e4SLinus Torvalds 		meta_delete(meta);
9561da177e4SLinus Torvalds 	return err;
9571da177e4SLinus Torvalds }
9581da177e4SLinus Torvalds 
em_meta_destroy(struct tcf_ematch * m)95982a470f1SJohn Fastabend static void em_meta_destroy(struct tcf_ematch *m)
9601da177e4SLinus Torvalds {
9611da177e4SLinus Torvalds 	if (m)
9621da177e4SLinus Torvalds 		meta_delete((struct meta_match *) m->data);
9631da177e4SLinus Torvalds }
9641da177e4SLinus Torvalds 
em_meta_dump(struct sk_buff * skb,struct tcf_ematch * em)9651da177e4SLinus Torvalds static int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em)
9661da177e4SLinus Torvalds {
9671da177e4SLinus Torvalds 	struct meta_match *meta = (struct meta_match *) em->data;
9681da177e4SLinus Torvalds 	struct tcf_meta_hdr hdr;
969cfe2f14cSJulia Lawall 	const struct meta_type_ops *ops;
9701da177e4SLinus Torvalds 
9711da177e4SLinus Torvalds 	memset(&hdr, 0, sizeof(hdr));
9721da177e4SLinus Torvalds 	memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left));
9731da177e4SLinus Torvalds 	memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right));
9741da177e4SLinus Torvalds 
9751b34ec43SDavid S. Miller 	if (nla_put(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr))
9761b34ec43SDavid S. Miller 		goto nla_put_failure;
9771da177e4SLinus Torvalds 
9781da177e4SLinus Torvalds 	ops = meta_type_ops(&meta->lvalue);
9791da177e4SLinus Torvalds 	if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 ||
9801da177e4SLinus Torvalds 	    ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0)
981add93b61SPatrick McHardy 		goto nla_put_failure;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 	return 0;
9841da177e4SLinus Torvalds 
985add93b61SPatrick McHardy nla_put_failure:
9861da177e4SLinus Torvalds 	return -1;
9871da177e4SLinus Torvalds }
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds static struct tcf_ematch_ops em_meta_ops = {
9901da177e4SLinus Torvalds 	.kind	  = TCF_EM_META,
9911da177e4SLinus Torvalds 	.change	  = em_meta_change,
9921da177e4SLinus Torvalds 	.match	  = em_meta_match,
9931da177e4SLinus Torvalds 	.destroy  = em_meta_destroy,
9941da177e4SLinus Torvalds 	.dump	  = em_meta_dump,
9951da177e4SLinus Torvalds 	.owner	  = THIS_MODULE,
9961da177e4SLinus Torvalds 	.link	  = LIST_HEAD_INIT(em_meta_ops.link)
9971da177e4SLinus Torvalds };
9981da177e4SLinus Torvalds 
init_em_meta(void)9991da177e4SLinus Torvalds static int __init init_em_meta(void)
10001da177e4SLinus Torvalds {
10011da177e4SLinus Torvalds 	return tcf_em_register(&em_meta_ops);
10021da177e4SLinus Torvalds }
10031da177e4SLinus Torvalds 
exit_em_meta(void)10041da177e4SLinus Torvalds static void __exit exit_em_meta(void)
10051da177e4SLinus Torvalds {
10061da177e4SLinus Torvalds 	tcf_em_unregister(&em_meta_ops);
10071da177e4SLinus Torvalds }
10081da177e4SLinus Torvalds 
10091da177e4SLinus Torvalds MODULE_LICENSE("GPL");
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds module_init(init_em_meta);
10121da177e4SLinus Torvalds module_exit(exit_em_meta);
1013db3d99c0SPatrick McHardy 
1014db3d99c0SPatrick McHardy MODULE_ALIAS_TCF_EMATCH(TCF_EM_META);
1015