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 47*4170f0efSTaichi 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 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 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 1071da177e4SLinus Torvalds META_COLLECTOR(int_random) 1081da177e4SLinus Torvalds { 1091da177e4SLinus Torvalds get_random_bytes(&dst->value, sizeof(dst->value)); 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 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 1201da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_0) 1211da177e4SLinus Torvalds { 1221da177e4SLinus Torvalds dst->value = fixed_loadavg(avenrun[0]); 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds META_COLLECTOR(int_loadavg_1) 1261da177e4SLinus Torvalds { 1271da177e4SLinus Torvalds dst->value = fixed_loadavg(avenrun[1]); 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 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 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 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 1581da177e4SLinus Torvalds META_COLLECTOR(int_dev) 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds *err = int_dev(skb->dev, dst); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 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 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 1901da177e4SLinus Torvalds META_COLLECTOR(int_priority) 1911da177e4SLinus Torvalds { 1921da177e4SLinus Torvalds dst->value = skb->priority; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 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 2011da177e4SLinus Torvalds META_COLLECTOR(int_pkttype) 2021da177e4SLinus Torvalds { 2031da177e4SLinus Torvalds dst->value = skb->pkt_type; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds META_COLLECTOR(int_pktlen) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds dst->value = skb->len; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds META_COLLECTOR(int_datalen) 2121da177e4SLinus Torvalds { 2131da177e4SLinus Torvalds dst->value = skb->data_len; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds META_COLLECTOR(int_maclen) 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds dst->value = skb->mac_len; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 } 50502a56c81SEric Dumazet dst->value = sk->sk_lingertime / HZ; 50648900629SThomas Graf } 50748900629SThomas Graf 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 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 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 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 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 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 } 57102a56c81SEric Dumazet dst->value = sk->sk_rcvtimeo / HZ; 57248900629SThomas Graf } 57348900629SThomas Graf 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 } 58202a56c81SEric Dumazet dst->value = sk->sk_sndtimeo / HZ; 58348900629SThomas Graf } 58448900629SThomas Graf 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 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 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 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 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 7061da177e4SLinus Torvalds static void meta_var_destroy(struct meta_value *v) 7071da177e4SLinus Torvalds { 7081da177e4SLinus Torvalds kfree((void *) v->val); 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 9991da177e4SLinus Torvalds static int __init init_em_meta(void) 10001da177e4SLinus Torvalds { 10011da177e4SLinus Torvalds return tcf_em_register(&em_meta_ops); 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 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