18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/em_meta.c Metadata ematch 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * ========================================================================== 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The metadata ematch compares two meta objects where each object 108c2ecf20Sopenharmony_ci * represents either a meta value stored in the kernel or a static 118c2ecf20Sopenharmony_ci * value provided by userspace. The objects are not provided by 128c2ecf20Sopenharmony_ci * userspace itself but rather a definition providing the information 138c2ecf20Sopenharmony_ci * to build them. Every object is of a certain type which must be 148c2ecf20Sopenharmony_ci * equal to the object it is being compared to. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The definition of a objects conists of the type (meta type), a 178c2ecf20Sopenharmony_ci * identifier (meta id) and additional type specific information. 188c2ecf20Sopenharmony_ci * The meta id is either TCF_META_TYPE_VALUE for values provided by 198c2ecf20Sopenharmony_ci * userspace or a index to the meta operations table consisting of 208c2ecf20Sopenharmony_ci * function pointers to type specific meta data collectors returning 218c2ecf20Sopenharmony_ci * the value of the requested meta value. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * lvalue rvalue 248c2ecf20Sopenharmony_ci * +-----------+ +-----------+ 258c2ecf20Sopenharmony_ci * | type: INT | | type: INT | 268c2ecf20Sopenharmony_ci * def | id: DEV | | id: VALUE | 278c2ecf20Sopenharmony_ci * | data: | | data: 3 | 288c2ecf20Sopenharmony_ci * +-----------+ +-----------+ 298c2ecf20Sopenharmony_ci * | | 308c2ecf20Sopenharmony_ci * ---> meta_ops[INT][DEV](...) | 318c2ecf20Sopenharmony_ci * | | 328c2ecf20Sopenharmony_ci * ----------- | 338c2ecf20Sopenharmony_ci * V V 348c2ecf20Sopenharmony_ci * +-----------+ +-----------+ 358c2ecf20Sopenharmony_ci * | type: INT | | type: INT | 368c2ecf20Sopenharmony_ci * obj | id: DEV | | id: VALUE | 378c2ecf20Sopenharmony_ci * | data: 2 |<--data got filled out | data: 3 | 388c2ecf20Sopenharmony_ci * +-----------+ +-----------+ 398c2ecf20Sopenharmony_ci * | | 408c2ecf20Sopenharmony_ci * --------------> 2 equals 3 <-------------- 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * This is a simplified schema, the complexity varies depending 438c2ecf20Sopenharmony_ci * on the meta type. Obviously, the length of the data must also 448c2ecf20Sopenharmony_ci * be provided for non-numeric types. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * Additionally, type dependent modifiers such as shift operators 478c2ecf20Sopenharmony_ci * or mask may be applied to extend the functionaliy. As of now, 488c2ecf20Sopenharmony_ci * the variable length type supports shifting the byte string to 498c2ecf20Sopenharmony_ci * the right, eating up any number of octets and thus supporting 508c2ecf20Sopenharmony_ci * wildcard interface name comparisons such as "ppp%" matching 518c2ecf20Sopenharmony_ci * ppp0..9. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * NOTE: Certain meta values depend on other subsystems and are 548c2ecf20Sopenharmony_ci * only available if that subsystem is enabled in the kernel. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#include <linux/slab.h> 588c2ecf20Sopenharmony_ci#include <linux/module.h> 598c2ecf20Sopenharmony_ci#include <linux/types.h> 608c2ecf20Sopenharmony_ci#include <linux/kernel.h> 618c2ecf20Sopenharmony_ci#include <linux/sched.h> 628c2ecf20Sopenharmony_ci#include <linux/sched/loadavg.h> 638c2ecf20Sopenharmony_ci#include <linux/string.h> 648c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 658c2ecf20Sopenharmony_ci#include <linux/random.h> 668c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 678c2ecf20Sopenharmony_ci#include <linux/tc_ematch/tc_em_meta.h> 688c2ecf20Sopenharmony_ci#include <net/dst.h> 698c2ecf20Sopenharmony_ci#include <net/route.h> 708c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 718c2ecf20Sopenharmony_ci#include <net/sock.h> 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct meta_obj { 748c2ecf20Sopenharmony_ci unsigned long value; 758c2ecf20Sopenharmony_ci unsigned int len; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct meta_value { 798c2ecf20Sopenharmony_ci struct tcf_meta_val hdr; 808c2ecf20Sopenharmony_ci unsigned long val; 818c2ecf20Sopenharmony_ci unsigned int len; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct meta_match { 858c2ecf20Sopenharmony_ci struct meta_value lvalue; 868c2ecf20Sopenharmony_ci struct meta_value rvalue; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic inline int meta_id(struct meta_value *v) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return TCF_META_ID(v->hdr.kind); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline int meta_type(struct meta_value *v) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return TCF_META_TYPE(v->hdr.kind); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \ 1008c2ecf20Sopenharmony_ci struct tcf_pkt_info *info, struct meta_value *v, \ 1018c2ecf20Sopenharmony_ci struct meta_obj *dst, int *err) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/************************************************************************** 1048c2ecf20Sopenharmony_ci * System status & misc 1058c2ecf20Sopenharmony_ci **************************************************************************/ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_random) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci get_random_bytes(&dst->value, sizeof(dst->value)); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic inline unsigned long fixed_loadavg(int load) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int rnd_load = load + (FIXED_1/200); 1158c2ecf20Sopenharmony_ci int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return ((rnd_load >> FSHIFT) * 100) + rnd_frac; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_loadavg_0) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci dst->value = fixed_loadavg(avenrun[0]); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_loadavg_1) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci dst->value = fixed_loadavg(avenrun[1]); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_loadavg_2) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci dst->value = fixed_loadavg(avenrun[2]); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/************************************************************************** 1368c2ecf20Sopenharmony_ci * Device names & indices 1378c2ecf20Sopenharmony_ci **************************************************************************/ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic inline int int_dev(struct net_device *dev, struct meta_obj *dst) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci if (unlikely(dev == NULL)) 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci dst->value = dev->ifindex; 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic inline int var_dev(struct net_device *dev, struct meta_obj *dst) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci if (unlikely(dev == NULL)) 1518c2ecf20Sopenharmony_ci return -1; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci dst->value = (unsigned long) dev->name; 1548c2ecf20Sopenharmony_ci dst->len = strlen(dev->name); 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_dev) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci *err = int_dev(skb->dev, dst); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciMETA_COLLECTOR(var_dev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci *err = var_dev(skb->dev, dst); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/************************************************************************** 1698c2ecf20Sopenharmony_ci * vlan tag 1708c2ecf20Sopenharmony_ci **************************************************************************/ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_vlan_tag) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci unsigned short tag; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) 1778c2ecf20Sopenharmony_ci dst->value = skb_vlan_tag_get(skb); 1788c2ecf20Sopenharmony_ci else if (!__vlan_get_tag(skb, &tag)) 1798c2ecf20Sopenharmony_ci dst->value = tag; 1808c2ecf20Sopenharmony_ci else 1818c2ecf20Sopenharmony_ci *err = -1; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/************************************************************************** 1878c2ecf20Sopenharmony_ci * skb attributes 1888c2ecf20Sopenharmony_ci **************************************************************************/ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_priority) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci dst->value = skb->priority; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_protocol) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci /* Let userspace take care of the byte ordering */ 1988c2ecf20Sopenharmony_ci dst->value = skb_protocol(skb, false); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_pkttype) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci dst->value = skb->pkt_type; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_pktlen) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci dst->value = skb->len; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_datalen) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci dst->value = skb->data_len; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_maclen) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci dst->value = skb->mac_len; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_rxhash) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci dst->value = skb_get_hash(skb); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/************************************************************************** 2278c2ecf20Sopenharmony_ci * Netfilter 2288c2ecf20Sopenharmony_ci **************************************************************************/ 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_mark) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci dst->value = skb->mark; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/************************************************************************** 2368c2ecf20Sopenharmony_ci * Traffic Control 2378c2ecf20Sopenharmony_ci **************************************************************************/ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_tcindex) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci dst->value = skb->tc_index; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/************************************************************************** 2458c2ecf20Sopenharmony_ci * Routing 2468c2ecf20Sopenharmony_ci **************************************************************************/ 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_rtclassid) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci if (unlikely(skb_dst(skb) == NULL)) 2518c2ecf20Sopenharmony_ci *err = -1; 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 2548c2ecf20Sopenharmony_ci dst->value = skb_dst(skb)->tclassid; 2558c2ecf20Sopenharmony_ci#else 2568c2ecf20Sopenharmony_ci dst->value = 0; 2578c2ecf20Sopenharmony_ci#endif 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_rtiif) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if (unlikely(skb_rtable(skb) == NULL)) 2638c2ecf20Sopenharmony_ci *err = -1; 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci dst->value = inet_iif(skb); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/************************************************************************** 2698c2ecf20Sopenharmony_ci * Socket Attributes 2708c2ecf20Sopenharmony_ci **************************************************************************/ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci#define skip_nonlocal(skb) \ 2738c2ecf20Sopenharmony_ci (unlikely(skb->sk == NULL)) 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_family) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 2788c2ecf20Sopenharmony_ci *err = -1; 2798c2ecf20Sopenharmony_ci return; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci dst->value = skb->sk->sk_family; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_state) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 2878c2ecf20Sopenharmony_ci *err = -1; 2888c2ecf20Sopenharmony_ci return; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci dst->value = skb->sk->sk_state; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_reuse) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 2968c2ecf20Sopenharmony_ci *err = -1; 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci dst->value = skb->sk->sk_reuse; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_bound_if) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 3058c2ecf20Sopenharmony_ci *err = -1; 3068c2ecf20Sopenharmony_ci return; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci /* No error if bound_dev_if is 0, legal userspace check */ 3098c2ecf20Sopenharmony_ci dst->value = skb->sk->sk_bound_dev_if; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ciMETA_COLLECTOR(var_sk_bound_if) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 3158c2ecf20Sopenharmony_ci *err = -1; 3168c2ecf20Sopenharmony_ci return; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (skb->sk->sk_bound_dev_if == 0) { 3208c2ecf20Sopenharmony_ci dst->value = (unsigned long) "any"; 3218c2ecf20Sopenharmony_ci dst->len = 3; 3228c2ecf20Sopenharmony_ci } else { 3238c2ecf20Sopenharmony_ci struct net_device *dev; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci rcu_read_lock(); 3268c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(sock_net(skb->sk), 3278c2ecf20Sopenharmony_ci skb->sk->sk_bound_dev_if); 3288c2ecf20Sopenharmony_ci *err = var_dev(dev, dst); 3298c2ecf20Sopenharmony_ci rcu_read_unlock(); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_refcnt) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 3368c2ecf20Sopenharmony_ci *err = -1; 3378c2ecf20Sopenharmony_ci return; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci dst->value = refcount_read(&skb->sk->sk_refcnt); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_rcvbuf) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!sk) { 3478c2ecf20Sopenharmony_ci *err = -1; 3488c2ecf20Sopenharmony_ci return; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci dst->value = sk->sk_rcvbuf; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_shutdown) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!sk) { 3588c2ecf20Sopenharmony_ci *err = -1; 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci dst->value = sk->sk_shutdown; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_proto) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!sk) { 3698c2ecf20Sopenharmony_ci *err = -1; 3708c2ecf20Sopenharmony_ci return; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci dst->value = sk->sk_protocol; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_type) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!sk) { 3808c2ecf20Sopenharmony_ci *err = -1; 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci dst->value = sk->sk_type; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_rmem_alloc) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (!sk) { 3918c2ecf20Sopenharmony_ci *err = -1; 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci dst->value = sk_rmem_alloc_get(sk); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_wmem_alloc) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!sk) { 4028c2ecf20Sopenharmony_ci *err = -1; 4038c2ecf20Sopenharmony_ci return; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci dst->value = sk_wmem_alloc_get(sk); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_omem_alloc) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!sk) { 4138c2ecf20Sopenharmony_ci *err = -1; 4148c2ecf20Sopenharmony_ci return; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci dst->value = atomic_read(&sk->sk_omem_alloc); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_rcv_qlen) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!sk) { 4248c2ecf20Sopenharmony_ci *err = -1; 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci dst->value = sk->sk_receive_queue.qlen; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_snd_qlen) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (!sk) { 4358c2ecf20Sopenharmony_ci *err = -1; 4368c2ecf20Sopenharmony_ci return; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci dst->value = sk->sk_write_queue.qlen; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_wmem_queued) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!sk) { 4468c2ecf20Sopenharmony_ci *err = -1; 4478c2ecf20Sopenharmony_ci return; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci dst->value = READ_ONCE(sk->sk_wmem_queued); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_fwd_alloc) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (!sk) { 4578c2ecf20Sopenharmony_ci *err = -1; 4588c2ecf20Sopenharmony_ci return; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci dst->value = sk->sk_forward_alloc; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_sndbuf) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!sk) { 4688c2ecf20Sopenharmony_ci *err = -1; 4698c2ecf20Sopenharmony_ci return; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci dst->value = sk->sk_sndbuf; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_alloc) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (!sk) { 4798c2ecf20Sopenharmony_ci *err = -1; 4808c2ecf20Sopenharmony_ci return; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci dst->value = (__force int) sk->sk_allocation; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_hash) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci if (skip_nonlocal(skb)) { 4888c2ecf20Sopenharmony_ci *err = -1; 4898c2ecf20Sopenharmony_ci return; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci dst->value = skb->sk->sk_hash; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_lingertime) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!sk) { 4998c2ecf20Sopenharmony_ci *err = -1; 5008c2ecf20Sopenharmony_ci return; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci dst->value = sk->sk_lingertime / HZ; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_err_qlen) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (!sk) { 5108c2ecf20Sopenharmony_ci *err = -1; 5118c2ecf20Sopenharmony_ci return; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci dst->value = sk->sk_error_queue.qlen; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_ack_bl) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (!sk) { 5218c2ecf20Sopenharmony_ci *err = -1; 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci dst->value = READ_ONCE(sk->sk_ack_backlog); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_max_ack_bl) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!sk) { 5328c2ecf20Sopenharmony_ci *err = -1; 5338c2ecf20Sopenharmony_ci return; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci dst->value = READ_ONCE(sk->sk_max_ack_backlog); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_prio) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (!sk) { 5438c2ecf20Sopenharmony_ci *err = -1; 5448c2ecf20Sopenharmony_ci return; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci dst->value = sk->sk_priority; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_rcvlowat) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!sk) { 5548c2ecf20Sopenharmony_ci *err = -1; 5558c2ecf20Sopenharmony_ci return; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci dst->value = READ_ONCE(sk->sk_rcvlowat); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_rcvtimeo) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (!sk) { 5658c2ecf20Sopenharmony_ci *err = -1; 5668c2ecf20Sopenharmony_ci return; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci dst->value = sk->sk_rcvtimeo / HZ; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_sndtimeo) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (!sk) { 5768c2ecf20Sopenharmony_ci *err = -1; 5778c2ecf20Sopenharmony_ci return; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci dst->value = sk->sk_sndtimeo / HZ; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_sendmsg_off) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (!sk) { 5878c2ecf20Sopenharmony_ci *err = -1; 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci dst->value = sk->sk_frag.offset; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ciMETA_COLLECTOR(int_sk_write_pend) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (!sk) { 5988c2ecf20Sopenharmony_ci *err = -1; 5998c2ecf20Sopenharmony_ci return; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci dst->value = sk->sk_write_pending; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/************************************************************************** 6058c2ecf20Sopenharmony_ci * Meta value collectors assignment table 6068c2ecf20Sopenharmony_ci **************************************************************************/ 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistruct meta_ops { 6098c2ecf20Sopenharmony_ci void (*get)(struct sk_buff *, struct tcf_pkt_info *, 6108c2ecf20Sopenharmony_ci struct meta_value *, struct meta_obj *, int *); 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci#define META_ID(name) TCF_META_ID_##name 6148c2ecf20Sopenharmony_ci#define META_FUNC(name) { .get = meta_##name } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci/* Meta value operations table listing all meta value collectors and 6178c2ecf20Sopenharmony_ci * assigns them to a type and meta id. */ 6188c2ecf20Sopenharmony_cistatic struct meta_ops __meta_ops[TCF_META_TYPE_MAX + 1][TCF_META_ID_MAX + 1] = { 6198c2ecf20Sopenharmony_ci [TCF_META_TYPE_VAR] = { 6208c2ecf20Sopenharmony_ci [META_ID(DEV)] = META_FUNC(var_dev), 6218c2ecf20Sopenharmony_ci [META_ID(SK_BOUND_IF)] = META_FUNC(var_sk_bound_if), 6228c2ecf20Sopenharmony_ci }, 6238c2ecf20Sopenharmony_ci [TCF_META_TYPE_INT] = { 6248c2ecf20Sopenharmony_ci [META_ID(RANDOM)] = META_FUNC(int_random), 6258c2ecf20Sopenharmony_ci [META_ID(LOADAVG_0)] = META_FUNC(int_loadavg_0), 6268c2ecf20Sopenharmony_ci [META_ID(LOADAVG_1)] = META_FUNC(int_loadavg_1), 6278c2ecf20Sopenharmony_ci [META_ID(LOADAVG_2)] = META_FUNC(int_loadavg_2), 6288c2ecf20Sopenharmony_ci [META_ID(DEV)] = META_FUNC(int_dev), 6298c2ecf20Sopenharmony_ci [META_ID(PRIORITY)] = META_FUNC(int_priority), 6308c2ecf20Sopenharmony_ci [META_ID(PROTOCOL)] = META_FUNC(int_protocol), 6318c2ecf20Sopenharmony_ci [META_ID(PKTTYPE)] = META_FUNC(int_pkttype), 6328c2ecf20Sopenharmony_ci [META_ID(PKTLEN)] = META_FUNC(int_pktlen), 6338c2ecf20Sopenharmony_ci [META_ID(DATALEN)] = META_FUNC(int_datalen), 6348c2ecf20Sopenharmony_ci [META_ID(MACLEN)] = META_FUNC(int_maclen), 6358c2ecf20Sopenharmony_ci [META_ID(NFMARK)] = META_FUNC(int_mark), 6368c2ecf20Sopenharmony_ci [META_ID(TCINDEX)] = META_FUNC(int_tcindex), 6378c2ecf20Sopenharmony_ci [META_ID(RTCLASSID)] = META_FUNC(int_rtclassid), 6388c2ecf20Sopenharmony_ci [META_ID(RTIIF)] = META_FUNC(int_rtiif), 6398c2ecf20Sopenharmony_ci [META_ID(SK_FAMILY)] = META_FUNC(int_sk_family), 6408c2ecf20Sopenharmony_ci [META_ID(SK_STATE)] = META_FUNC(int_sk_state), 6418c2ecf20Sopenharmony_ci [META_ID(SK_REUSE)] = META_FUNC(int_sk_reuse), 6428c2ecf20Sopenharmony_ci [META_ID(SK_BOUND_IF)] = META_FUNC(int_sk_bound_if), 6438c2ecf20Sopenharmony_ci [META_ID(SK_REFCNT)] = META_FUNC(int_sk_refcnt), 6448c2ecf20Sopenharmony_ci [META_ID(SK_RCVBUF)] = META_FUNC(int_sk_rcvbuf), 6458c2ecf20Sopenharmony_ci [META_ID(SK_SNDBUF)] = META_FUNC(int_sk_sndbuf), 6468c2ecf20Sopenharmony_ci [META_ID(SK_SHUTDOWN)] = META_FUNC(int_sk_shutdown), 6478c2ecf20Sopenharmony_ci [META_ID(SK_PROTO)] = META_FUNC(int_sk_proto), 6488c2ecf20Sopenharmony_ci [META_ID(SK_TYPE)] = META_FUNC(int_sk_type), 6498c2ecf20Sopenharmony_ci [META_ID(SK_RMEM_ALLOC)] = META_FUNC(int_sk_rmem_alloc), 6508c2ecf20Sopenharmony_ci [META_ID(SK_WMEM_ALLOC)] = META_FUNC(int_sk_wmem_alloc), 6518c2ecf20Sopenharmony_ci [META_ID(SK_OMEM_ALLOC)] = META_FUNC(int_sk_omem_alloc), 6528c2ecf20Sopenharmony_ci [META_ID(SK_WMEM_QUEUED)] = META_FUNC(int_sk_wmem_queued), 6538c2ecf20Sopenharmony_ci [META_ID(SK_RCV_QLEN)] = META_FUNC(int_sk_rcv_qlen), 6548c2ecf20Sopenharmony_ci [META_ID(SK_SND_QLEN)] = META_FUNC(int_sk_snd_qlen), 6558c2ecf20Sopenharmony_ci [META_ID(SK_ERR_QLEN)] = META_FUNC(int_sk_err_qlen), 6568c2ecf20Sopenharmony_ci [META_ID(SK_FORWARD_ALLOCS)] = META_FUNC(int_sk_fwd_alloc), 6578c2ecf20Sopenharmony_ci [META_ID(SK_ALLOCS)] = META_FUNC(int_sk_alloc), 6588c2ecf20Sopenharmony_ci [META_ID(SK_HASH)] = META_FUNC(int_sk_hash), 6598c2ecf20Sopenharmony_ci [META_ID(SK_LINGERTIME)] = META_FUNC(int_sk_lingertime), 6608c2ecf20Sopenharmony_ci [META_ID(SK_ACK_BACKLOG)] = META_FUNC(int_sk_ack_bl), 6618c2ecf20Sopenharmony_ci [META_ID(SK_MAX_ACK_BACKLOG)] = META_FUNC(int_sk_max_ack_bl), 6628c2ecf20Sopenharmony_ci [META_ID(SK_PRIO)] = META_FUNC(int_sk_prio), 6638c2ecf20Sopenharmony_ci [META_ID(SK_RCVLOWAT)] = META_FUNC(int_sk_rcvlowat), 6648c2ecf20Sopenharmony_ci [META_ID(SK_RCVTIMEO)] = META_FUNC(int_sk_rcvtimeo), 6658c2ecf20Sopenharmony_ci [META_ID(SK_SNDTIMEO)] = META_FUNC(int_sk_sndtimeo), 6668c2ecf20Sopenharmony_ci [META_ID(SK_SENDMSG_OFF)] = META_FUNC(int_sk_sendmsg_off), 6678c2ecf20Sopenharmony_ci [META_ID(SK_WRITE_PENDING)] = META_FUNC(int_sk_write_pend), 6688c2ecf20Sopenharmony_ci [META_ID(VLAN_TAG)] = META_FUNC(int_vlan_tag), 6698c2ecf20Sopenharmony_ci [META_ID(RXHASH)] = META_FUNC(int_rxhash), 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci}; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic inline struct meta_ops *meta_ops(struct meta_value *val) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci return &__meta_ops[meta_type(val)][meta_id(val)]; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/************************************************************************** 6798c2ecf20Sopenharmony_ci * Type specific operations for TCF_META_TYPE_VAR 6808c2ecf20Sopenharmony_ci **************************************************************************/ 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int meta_var_compare(struct meta_obj *a, struct meta_obj *b) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci int r = a->len - b->len; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (r == 0) 6878c2ecf20Sopenharmony_ci r = memcmp((void *) a->value, (void *) b->value, a->len); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return r; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int meta_var_change(struct meta_value *dst, struct nlattr *nla) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci int len = nla_len(nla); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci dst->val = (unsigned long)kmemdup(nla_data(nla), len, GFP_KERNEL); 6978c2ecf20Sopenharmony_ci if (dst->val == 0UL) 6988c2ecf20Sopenharmony_ci return -ENOMEM; 6998c2ecf20Sopenharmony_ci dst->len = len; 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic void meta_var_destroy(struct meta_value *v) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci kfree((void *) v->val); 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic void meta_var_apply_extras(struct meta_value *v, 7098c2ecf20Sopenharmony_ci struct meta_obj *dst) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci int shift = v->hdr.shift; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (shift && shift < dst->len) 7148c2ecf20Sopenharmony_ci dst->len -= shift; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci if (v->val && v->len && 7208c2ecf20Sopenharmony_ci nla_put(skb, tlv, v->len, (void *) v->val)) 7218c2ecf20Sopenharmony_ci goto nla_put_failure; 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cinla_put_failure: 7258c2ecf20Sopenharmony_ci return -1; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/************************************************************************** 7298c2ecf20Sopenharmony_ci * Type specific operations for TCF_META_TYPE_INT 7308c2ecf20Sopenharmony_ci **************************************************************************/ 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic int meta_int_compare(struct meta_obj *a, struct meta_obj *b) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci /* Let gcc optimize it, the unlikely is not really based on 7358c2ecf20Sopenharmony_ci * some numbers but jump free code for mismatches seems 7368c2ecf20Sopenharmony_ci * more logical. */ 7378c2ecf20Sopenharmony_ci if (unlikely(a->value == b->value)) 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci else if (a->value < b->value) 7408c2ecf20Sopenharmony_ci return -1; 7418c2ecf20Sopenharmony_ci else 7428c2ecf20Sopenharmony_ci return 1; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic int meta_int_change(struct meta_value *dst, struct nlattr *nla) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci if (nla_len(nla) >= sizeof(unsigned long)) { 7488c2ecf20Sopenharmony_ci dst->val = *(unsigned long *) nla_data(nla); 7498c2ecf20Sopenharmony_ci dst->len = sizeof(unsigned long); 7508c2ecf20Sopenharmony_ci } else if (nla_len(nla) == sizeof(u32)) { 7518c2ecf20Sopenharmony_ci dst->val = nla_get_u32(nla); 7528c2ecf20Sopenharmony_ci dst->len = sizeof(u32); 7538c2ecf20Sopenharmony_ci } else 7548c2ecf20Sopenharmony_ci return -EINVAL; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic void meta_int_apply_extras(struct meta_value *v, 7608c2ecf20Sopenharmony_ci struct meta_obj *dst) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci if (v->hdr.shift) 7638c2ecf20Sopenharmony_ci dst->value >>= v->hdr.shift; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (v->val) 7668c2ecf20Sopenharmony_ci dst->value &= v->val; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci if (v->len == sizeof(unsigned long)) { 7728c2ecf20Sopenharmony_ci if (nla_put(skb, tlv, sizeof(unsigned long), &v->val)) 7738c2ecf20Sopenharmony_ci goto nla_put_failure; 7748c2ecf20Sopenharmony_ci } else if (v->len == sizeof(u32)) { 7758c2ecf20Sopenharmony_ci if (nla_put_u32(skb, tlv, v->val)) 7768c2ecf20Sopenharmony_ci goto nla_put_failure; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return 0; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cinla_put_failure: 7828c2ecf20Sopenharmony_ci return -1; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci/************************************************************************** 7868c2ecf20Sopenharmony_ci * Type specific operations table 7878c2ecf20Sopenharmony_ci **************************************************************************/ 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistruct meta_type_ops { 7908c2ecf20Sopenharmony_ci void (*destroy)(struct meta_value *); 7918c2ecf20Sopenharmony_ci int (*compare)(struct meta_obj *, struct meta_obj *); 7928c2ecf20Sopenharmony_ci int (*change)(struct meta_value *, struct nlattr *); 7938c2ecf20Sopenharmony_ci void (*apply_extras)(struct meta_value *, struct meta_obj *); 7948c2ecf20Sopenharmony_ci int (*dump)(struct sk_buff *, struct meta_value *, int); 7958c2ecf20Sopenharmony_ci}; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic const struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX + 1] = { 7988c2ecf20Sopenharmony_ci [TCF_META_TYPE_VAR] = { 7998c2ecf20Sopenharmony_ci .destroy = meta_var_destroy, 8008c2ecf20Sopenharmony_ci .compare = meta_var_compare, 8018c2ecf20Sopenharmony_ci .change = meta_var_change, 8028c2ecf20Sopenharmony_ci .apply_extras = meta_var_apply_extras, 8038c2ecf20Sopenharmony_ci .dump = meta_var_dump 8048c2ecf20Sopenharmony_ci }, 8058c2ecf20Sopenharmony_ci [TCF_META_TYPE_INT] = { 8068c2ecf20Sopenharmony_ci .compare = meta_int_compare, 8078c2ecf20Sopenharmony_ci .change = meta_int_change, 8088c2ecf20Sopenharmony_ci .apply_extras = meta_int_apply_extras, 8098c2ecf20Sopenharmony_ci .dump = meta_int_dump 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci}; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic inline const struct meta_type_ops *meta_type_ops(struct meta_value *v) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci return &__meta_type_ops[meta_type(v)]; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci/************************************************************************** 8198c2ecf20Sopenharmony_ci * Core 8208c2ecf20Sopenharmony_ci **************************************************************************/ 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info, 8238c2ecf20Sopenharmony_ci struct meta_value *v, struct meta_obj *dst) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci int err = 0; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (meta_id(v) == TCF_META_ID_VALUE) { 8288c2ecf20Sopenharmony_ci dst->value = v->val; 8298c2ecf20Sopenharmony_ci dst->len = v->len; 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci meta_ops(v)->get(skb, info, v, dst, &err); 8348c2ecf20Sopenharmony_ci if (err < 0) 8358c2ecf20Sopenharmony_ci return err; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (meta_type_ops(v)->apply_extras) 8388c2ecf20Sopenharmony_ci meta_type_ops(v)->apply_extras(v, dst); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m, 8448c2ecf20Sopenharmony_ci struct tcf_pkt_info *info) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci int r; 8478c2ecf20Sopenharmony_ci struct meta_match *meta = (struct meta_match *) m->data; 8488c2ecf20Sopenharmony_ci struct meta_obj l_value, r_value; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 || 8518c2ecf20Sopenharmony_ci meta_get(skb, info, &meta->rvalue, &r_value) < 0) 8528c2ecf20Sopenharmony_ci return 0; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci switch (meta->lvalue.hdr.op) { 8578c2ecf20Sopenharmony_ci case TCF_EM_OPND_EQ: 8588c2ecf20Sopenharmony_ci return !r; 8598c2ecf20Sopenharmony_ci case TCF_EM_OPND_LT: 8608c2ecf20Sopenharmony_ci return r < 0; 8618c2ecf20Sopenharmony_ci case TCF_EM_OPND_GT: 8628c2ecf20Sopenharmony_ci return r > 0; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic void meta_delete(struct meta_match *meta) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci if (meta) { 8718c2ecf20Sopenharmony_ci const struct meta_type_ops *ops = meta_type_ops(&meta->lvalue); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (ops && ops->destroy) { 8748c2ecf20Sopenharmony_ci ops->destroy(&meta->lvalue); 8758c2ecf20Sopenharmony_ci ops->destroy(&meta->rvalue); 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci kfree(meta); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic inline int meta_change_data(struct meta_value *dst, struct nlattr *nla) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci if (nla) { 8858c2ecf20Sopenharmony_ci if (nla_len(nla) == 0) 8868c2ecf20Sopenharmony_ci return -EINVAL; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return meta_type_ops(dst)->change(dst, nla); 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return 0; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic inline int meta_is_supported(struct meta_value *val) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci return !meta_id(val) || meta_ops(val)->get; 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic const struct nla_policy meta_policy[TCA_EM_META_MAX + 1] = { 9008c2ecf20Sopenharmony_ci [TCA_EM_META_HDR] = { .len = sizeof(struct tcf_meta_hdr) }, 9018c2ecf20Sopenharmony_ci}; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic int em_meta_change(struct net *net, void *data, int len, 9048c2ecf20Sopenharmony_ci struct tcf_ematch *m) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci int err; 9078c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_EM_META_MAX + 1]; 9088c2ecf20Sopenharmony_ci struct tcf_meta_hdr *hdr; 9098c2ecf20Sopenharmony_ci struct meta_match *meta = NULL; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci err = nla_parse_deprecated(tb, TCA_EM_META_MAX, data, len, 9128c2ecf20Sopenharmony_ci meta_policy, NULL); 9138c2ecf20Sopenharmony_ci if (err < 0) 9148c2ecf20Sopenharmony_ci goto errout; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci err = -EINVAL; 9178c2ecf20Sopenharmony_ci if (tb[TCA_EM_META_HDR] == NULL) 9188c2ecf20Sopenharmony_ci goto errout; 9198c2ecf20Sopenharmony_ci hdr = nla_data(tb[TCA_EM_META_HDR]); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) || 9228c2ecf20Sopenharmony_ci TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX || 9238c2ecf20Sopenharmony_ci TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX || 9248c2ecf20Sopenharmony_ci TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX) 9258c2ecf20Sopenharmony_ci goto errout; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci meta = kzalloc(sizeof(*meta), GFP_KERNEL); 9288c2ecf20Sopenharmony_ci if (meta == NULL) { 9298c2ecf20Sopenharmony_ci err = -ENOMEM; 9308c2ecf20Sopenharmony_ci goto errout; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left)); 9348c2ecf20Sopenharmony_ci memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right)); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (!meta_is_supported(&meta->lvalue) || 9378c2ecf20Sopenharmony_ci !meta_is_supported(&meta->rvalue)) { 9388c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 9398c2ecf20Sopenharmony_ci goto errout; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE]) < 0 || 9438c2ecf20Sopenharmony_ci meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE]) < 0) 9448c2ecf20Sopenharmony_ci goto errout; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci m->datalen = sizeof(*meta); 9478c2ecf20Sopenharmony_ci m->data = (unsigned long) meta; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci err = 0; 9508c2ecf20Sopenharmony_cierrout: 9518c2ecf20Sopenharmony_ci if (err && meta) 9528c2ecf20Sopenharmony_ci meta_delete(meta); 9538c2ecf20Sopenharmony_ci return err; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic void em_meta_destroy(struct tcf_ematch *m) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci if (m) 9598c2ecf20Sopenharmony_ci meta_delete((struct meta_match *) m->data); 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct meta_match *meta = (struct meta_match *) em->data; 9658c2ecf20Sopenharmony_ci struct tcf_meta_hdr hdr; 9668c2ecf20Sopenharmony_ci const struct meta_type_ops *ops; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci memset(&hdr, 0, sizeof(hdr)); 9698c2ecf20Sopenharmony_ci memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left)); 9708c2ecf20Sopenharmony_ci memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right)); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr)) 9738c2ecf20Sopenharmony_ci goto nla_put_failure; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci ops = meta_type_ops(&meta->lvalue); 9768c2ecf20Sopenharmony_ci if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 || 9778c2ecf20Sopenharmony_ci ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0) 9788c2ecf20Sopenharmony_ci goto nla_put_failure; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci return 0; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cinla_put_failure: 9838c2ecf20Sopenharmony_ci return -1; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic struct tcf_ematch_ops em_meta_ops = { 9878c2ecf20Sopenharmony_ci .kind = TCF_EM_META, 9888c2ecf20Sopenharmony_ci .change = em_meta_change, 9898c2ecf20Sopenharmony_ci .match = em_meta_match, 9908c2ecf20Sopenharmony_ci .destroy = em_meta_destroy, 9918c2ecf20Sopenharmony_ci .dump = em_meta_dump, 9928c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9938c2ecf20Sopenharmony_ci .link = LIST_HEAD_INIT(em_meta_ops.link) 9948c2ecf20Sopenharmony_ci}; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int __init init_em_meta(void) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci return tcf_em_register(&em_meta_ops); 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_cistatic void __exit exit_em_meta(void) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci tcf_em_unregister(&em_meta_ops); 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cimodule_init(init_em_meta); 10098c2ecf20Sopenharmony_cimodule_exit(exit_em_meta); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ciMODULE_ALIAS_TCF_EMATCH(TCF_EM_META); 1012