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