162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/em_meta.c Metadata ematch 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * ========================================================================== 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * The metadata ematch compares two meta objects where each object 1062306a36Sopenharmony_ci * represents either a meta value stored in the kernel or a static 1162306a36Sopenharmony_ci * value provided by userspace. The objects are not provided by 1262306a36Sopenharmony_ci * userspace itself but rather a definition providing the information 1362306a36Sopenharmony_ci * to build them. Every object is of a certain type which must be 1462306a36Sopenharmony_ci * equal to the object it is being compared to. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * The definition of a objects conists of the type (meta type), a 1762306a36Sopenharmony_ci * identifier (meta id) and additional type specific information. 1862306a36Sopenharmony_ci * The meta id is either TCF_META_TYPE_VALUE for values provided by 1962306a36Sopenharmony_ci * userspace or a index to the meta operations table consisting of 2062306a36Sopenharmony_ci * function pointers to type specific meta data collectors returning 2162306a36Sopenharmony_ci * the value of the requested meta value. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * lvalue rvalue 2462306a36Sopenharmony_ci * +-----------+ +-----------+ 2562306a36Sopenharmony_ci * | type: INT | | type: INT | 2662306a36Sopenharmony_ci * def | id: DEV | | id: VALUE | 2762306a36Sopenharmony_ci * | data: | | data: 3 | 2862306a36Sopenharmony_ci * +-----------+ +-----------+ 2962306a36Sopenharmony_ci * | | 3062306a36Sopenharmony_ci * ---> meta_ops[INT][DEV](...) | 3162306a36Sopenharmony_ci * | | 3262306a36Sopenharmony_ci * ----------- | 3362306a36Sopenharmony_ci * V V 3462306a36Sopenharmony_ci * +-----------+ +-----------+ 3562306a36Sopenharmony_ci * | type: INT | | type: INT | 3662306a36Sopenharmony_ci * obj | id: DEV | | id: VALUE | 3762306a36Sopenharmony_ci * | data: 2 |<--data got filled out | data: 3 | 3862306a36Sopenharmony_ci * +-----------+ +-----------+ 3962306a36Sopenharmony_ci * | | 4062306a36Sopenharmony_ci * --------------> 2 equals 3 <-------------- 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * This is a simplified schema, the complexity varies depending 4362306a36Sopenharmony_ci * on the meta type. Obviously, the length of the data must also 4462306a36Sopenharmony_ci * be provided for non-numeric types. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Additionally, type dependent modifiers such as shift operators 4762306a36Sopenharmony_ci * or mask may be applied to extend the functionality. As of now, 4862306a36Sopenharmony_ci * the variable length type supports shifting the byte string to 4962306a36Sopenharmony_ci * the right, eating up any number of octets and thus supporting 5062306a36Sopenharmony_ci * wildcard interface name comparisons such as "ppp%" matching 5162306a36Sopenharmony_ci * ppp0..9. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * NOTE: Certain meta values depend on other subsystems and are 5462306a36Sopenharmony_ci * only available if that subsystem is enabled in the kernel. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include <linux/slab.h> 5862306a36Sopenharmony_ci#include <linux/module.h> 5962306a36Sopenharmony_ci#include <linux/types.h> 6062306a36Sopenharmony_ci#include <linux/kernel.h> 6162306a36Sopenharmony_ci#include <linux/sched.h> 6262306a36Sopenharmony_ci#include <linux/sched/loadavg.h> 6362306a36Sopenharmony_ci#include <linux/string.h> 6462306a36Sopenharmony_ci#include <linux/skbuff.h> 6562306a36Sopenharmony_ci#include <linux/random.h> 6662306a36Sopenharmony_ci#include <linux/if_vlan.h> 6762306a36Sopenharmony_ci#include <linux/tc_ematch/tc_em_meta.h> 6862306a36Sopenharmony_ci#include <net/dst.h> 6962306a36Sopenharmony_ci#include <net/route.h> 7062306a36Sopenharmony_ci#include <net/pkt_cls.h> 7162306a36Sopenharmony_ci#include <net/sock.h> 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct meta_obj { 7462306a36Sopenharmony_ci unsigned long value; 7562306a36Sopenharmony_ci unsigned int len; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct meta_value { 7962306a36Sopenharmony_ci struct tcf_meta_val hdr; 8062306a36Sopenharmony_ci unsigned long val; 8162306a36Sopenharmony_ci unsigned int len; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct meta_match { 8562306a36Sopenharmony_ci struct meta_value lvalue; 8662306a36Sopenharmony_ci struct meta_value rvalue; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic inline int meta_id(struct meta_value *v) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return TCF_META_ID(v->hdr.kind); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic inline int meta_type(struct meta_value *v) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci return TCF_META_TYPE(v->hdr.kind); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \ 10062306a36Sopenharmony_ci struct tcf_pkt_info *info, struct meta_value *v, \ 10162306a36Sopenharmony_ci struct meta_obj *dst, int *err) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/************************************************************************** 10462306a36Sopenharmony_ci * System status & misc 10562306a36Sopenharmony_ci **************************************************************************/ 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciMETA_COLLECTOR(int_random) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci get_random_bytes(&dst->value, sizeof(dst->value)); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline unsigned long fixed_loadavg(int load) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int rnd_load = load + (FIXED_1/200); 11562306a36Sopenharmony_ci int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return ((rnd_load >> FSHIFT) * 100) + rnd_frac; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciMETA_COLLECTOR(int_loadavg_0) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci dst->value = fixed_loadavg(avenrun[0]); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciMETA_COLLECTOR(int_loadavg_1) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci dst->value = fixed_loadavg(avenrun[1]); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciMETA_COLLECTOR(int_loadavg_2) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci dst->value = fixed_loadavg(avenrun[2]); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/************************************************************************** 13662306a36Sopenharmony_ci * Device names & indices 13762306a36Sopenharmony_ci **************************************************************************/ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic inline int int_dev(struct net_device *dev, struct meta_obj *dst) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci if (unlikely(dev == NULL)) 14262306a36Sopenharmony_ci return -1; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci dst->value = dev->ifindex; 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic inline int var_dev(struct net_device *dev, struct meta_obj *dst) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci if (unlikely(dev == NULL)) 15162306a36Sopenharmony_ci return -1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci dst->value = (unsigned long) dev->name; 15462306a36Sopenharmony_ci dst->len = strlen(dev->name); 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciMETA_COLLECTOR(int_dev) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci *err = int_dev(skb->dev, dst); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciMETA_COLLECTOR(var_dev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci *err = var_dev(skb->dev, dst); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/************************************************************************** 16962306a36Sopenharmony_ci * vlan tag 17062306a36Sopenharmony_ci **************************************************************************/ 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciMETA_COLLECTOR(int_vlan_tag) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci unsigned short tag; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) 17762306a36Sopenharmony_ci dst->value = skb_vlan_tag_get(skb); 17862306a36Sopenharmony_ci else if (!__vlan_get_tag(skb, &tag)) 17962306a36Sopenharmony_ci dst->value = tag; 18062306a36Sopenharmony_ci else 18162306a36Sopenharmony_ci *err = -1; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/************************************************************************** 18762306a36Sopenharmony_ci * skb attributes 18862306a36Sopenharmony_ci **************************************************************************/ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciMETA_COLLECTOR(int_priority) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci dst->value = skb->priority; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciMETA_COLLECTOR(int_protocol) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci /* Let userspace take care of the byte ordering */ 19862306a36Sopenharmony_ci dst->value = skb_protocol(skb, false); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciMETA_COLLECTOR(int_pkttype) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci dst->value = skb->pkt_type; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciMETA_COLLECTOR(int_pktlen) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci dst->value = skb->len; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciMETA_COLLECTOR(int_datalen) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci dst->value = skb->data_len; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciMETA_COLLECTOR(int_maclen) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci dst->value = skb->mac_len; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciMETA_COLLECTOR(int_rxhash) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci dst->value = skb_get_hash(skb); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/************************************************************************** 22762306a36Sopenharmony_ci * Netfilter 22862306a36Sopenharmony_ci **************************************************************************/ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciMETA_COLLECTOR(int_mark) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci dst->value = skb->mark; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/************************************************************************** 23662306a36Sopenharmony_ci * Traffic Control 23762306a36Sopenharmony_ci **************************************************************************/ 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciMETA_COLLECTOR(int_tcindex) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci dst->value = skb->tc_index; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/************************************************************************** 24562306a36Sopenharmony_ci * Routing 24662306a36Sopenharmony_ci **************************************************************************/ 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciMETA_COLLECTOR(int_rtclassid) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci if (unlikely(skb_dst(skb) == NULL)) 25162306a36Sopenharmony_ci *err = -1; 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 25462306a36Sopenharmony_ci dst->value = skb_dst(skb)->tclassid; 25562306a36Sopenharmony_ci#else 25662306a36Sopenharmony_ci dst->value = 0; 25762306a36Sopenharmony_ci#endif 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciMETA_COLLECTOR(int_rtiif) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci if (unlikely(skb_rtable(skb) == NULL)) 26362306a36Sopenharmony_ci *err = -1; 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci dst->value = inet_iif(skb); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/************************************************************************** 26962306a36Sopenharmony_ci * Socket Attributes 27062306a36Sopenharmony_ci **************************************************************************/ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci#define skip_nonlocal(skb) \ 27362306a36Sopenharmony_ci (unlikely(skb->sk == NULL)) 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_family) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 27862306a36Sopenharmony_ci *err = -1; 27962306a36Sopenharmony_ci return; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci dst->value = skb->sk->sk_family; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_state) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 28762306a36Sopenharmony_ci *err = -1; 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci dst->value = skb->sk->sk_state; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_reuse) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 29662306a36Sopenharmony_ci *err = -1; 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci dst->value = skb->sk->sk_reuse; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_bound_if) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 30562306a36Sopenharmony_ci *err = -1; 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci /* No error if bound_dev_if is 0, legal userspace check */ 30962306a36Sopenharmony_ci dst->value = skb->sk->sk_bound_dev_if; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciMETA_COLLECTOR(var_sk_bound_if) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int bound_dev_if; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 31762306a36Sopenharmony_ci *err = -1; 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci bound_dev_if = READ_ONCE(skb->sk->sk_bound_dev_if); 32262306a36Sopenharmony_ci if (bound_dev_if == 0) { 32362306a36Sopenharmony_ci dst->value = (unsigned long) "any"; 32462306a36Sopenharmony_ci dst->len = 3; 32562306a36Sopenharmony_ci } else { 32662306a36Sopenharmony_ci struct net_device *dev; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci rcu_read_lock(); 32962306a36Sopenharmony_ci dev = dev_get_by_index_rcu(sock_net(skb->sk), 33062306a36Sopenharmony_ci bound_dev_if); 33162306a36Sopenharmony_ci *err = var_dev(dev, dst); 33262306a36Sopenharmony_ci rcu_read_unlock(); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_refcnt) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 33962306a36Sopenharmony_ci *err = -1; 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci dst->value = refcount_read(&skb->sk->sk_refcnt); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_rcvbuf) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!sk) { 35062306a36Sopenharmony_ci *err = -1; 35162306a36Sopenharmony_ci return; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci dst->value = sk->sk_rcvbuf; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_shutdown) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!sk) { 36162306a36Sopenharmony_ci *err = -1; 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci dst->value = sk->sk_shutdown; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_proto) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!sk) { 37262306a36Sopenharmony_ci *err = -1; 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci dst->value = sk->sk_protocol; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_type) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (!sk) { 38362306a36Sopenharmony_ci *err = -1; 38462306a36Sopenharmony_ci return; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci dst->value = sk->sk_type; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_rmem_alloc) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!sk) { 39462306a36Sopenharmony_ci *err = -1; 39562306a36Sopenharmony_ci return; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci dst->value = sk_rmem_alloc_get(sk); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_wmem_alloc) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!sk) { 40562306a36Sopenharmony_ci *err = -1; 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci dst->value = sk_wmem_alloc_get(sk); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_omem_alloc) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!sk) { 41662306a36Sopenharmony_ci *err = -1; 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci dst->value = atomic_read(&sk->sk_omem_alloc); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_rcv_qlen) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (!sk) { 42762306a36Sopenharmony_ci *err = -1; 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci dst->value = sk->sk_receive_queue.qlen; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_snd_qlen) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!sk) { 43862306a36Sopenharmony_ci *err = -1; 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci dst->value = sk->sk_write_queue.qlen; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_wmem_queued) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!sk) { 44962306a36Sopenharmony_ci *err = -1; 45062306a36Sopenharmony_ci return; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_wmem_queued); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_fwd_alloc) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!sk) { 46062306a36Sopenharmony_ci *err = -1; 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci dst->value = sk_forward_alloc_get(sk); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_sndbuf) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!sk) { 47162306a36Sopenharmony_ci *err = -1; 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci dst->value = sk->sk_sndbuf; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_alloc) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (!sk) { 48262306a36Sopenharmony_ci *err = -1; 48362306a36Sopenharmony_ci return; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci dst->value = (__force int) sk->sk_allocation; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_hash) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci if (skip_nonlocal(skb)) { 49162306a36Sopenharmony_ci *err = -1; 49262306a36Sopenharmony_ci return; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci dst->value = skb->sk->sk_hash; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_lingertime) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!sk) { 50262306a36Sopenharmony_ci *err = -1; 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_lingertime) / HZ; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_err_qlen) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (!sk) { 51362306a36Sopenharmony_ci *err = -1; 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci dst->value = sk->sk_error_queue.qlen; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_ack_bl) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (!sk) { 52462306a36Sopenharmony_ci *err = -1; 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_ack_backlog); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_max_ack_bl) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!sk) { 53562306a36Sopenharmony_ci *err = -1; 53662306a36Sopenharmony_ci return; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_max_ack_backlog); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_prio) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (!sk) { 54662306a36Sopenharmony_ci *err = -1; 54762306a36Sopenharmony_ci return; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci dst->value = sk->sk_priority; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_rcvlowat) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (!sk) { 55762306a36Sopenharmony_ci *err = -1; 55862306a36Sopenharmony_ci return; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_rcvlowat); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_rcvtimeo) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!sk) { 56862306a36Sopenharmony_ci *err = -1; 56962306a36Sopenharmony_ci return; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_rcvtimeo) / HZ; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_sndtimeo) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!sk) { 57962306a36Sopenharmony_ci *err = -1; 58062306a36Sopenharmony_ci return; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci dst->value = READ_ONCE(sk->sk_sndtimeo) / HZ; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_sendmsg_off) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!sk) { 59062306a36Sopenharmony_ci *err = -1; 59162306a36Sopenharmony_ci return; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci dst->value = sk->sk_frag.offset; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ciMETA_COLLECTOR(int_sk_write_pend) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci const struct sock *sk = skb_to_full_sk(skb); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (!sk) { 60162306a36Sopenharmony_ci *err = -1; 60262306a36Sopenharmony_ci return; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci dst->value = sk->sk_write_pending; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/************************************************************************** 60862306a36Sopenharmony_ci * Meta value collectors assignment table 60962306a36Sopenharmony_ci **************************************************************************/ 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistruct meta_ops { 61262306a36Sopenharmony_ci void (*get)(struct sk_buff *, struct tcf_pkt_info *, 61362306a36Sopenharmony_ci struct meta_value *, struct meta_obj *, int *); 61462306a36Sopenharmony_ci}; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci#define META_ID(name) TCF_META_ID_##name 61762306a36Sopenharmony_ci#define META_FUNC(name) { .get = meta_##name } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/* Meta value operations table listing all meta value collectors and 62062306a36Sopenharmony_ci * assigns them to a type and meta id. */ 62162306a36Sopenharmony_cistatic struct meta_ops __meta_ops[TCF_META_TYPE_MAX + 1][TCF_META_ID_MAX + 1] = { 62262306a36Sopenharmony_ci [TCF_META_TYPE_VAR] = { 62362306a36Sopenharmony_ci [META_ID(DEV)] = META_FUNC(var_dev), 62462306a36Sopenharmony_ci [META_ID(SK_BOUND_IF)] = META_FUNC(var_sk_bound_if), 62562306a36Sopenharmony_ci }, 62662306a36Sopenharmony_ci [TCF_META_TYPE_INT] = { 62762306a36Sopenharmony_ci [META_ID(RANDOM)] = META_FUNC(int_random), 62862306a36Sopenharmony_ci [META_ID(LOADAVG_0)] = META_FUNC(int_loadavg_0), 62962306a36Sopenharmony_ci [META_ID(LOADAVG_1)] = META_FUNC(int_loadavg_1), 63062306a36Sopenharmony_ci [META_ID(LOADAVG_2)] = META_FUNC(int_loadavg_2), 63162306a36Sopenharmony_ci [META_ID(DEV)] = META_FUNC(int_dev), 63262306a36Sopenharmony_ci [META_ID(PRIORITY)] = META_FUNC(int_priority), 63362306a36Sopenharmony_ci [META_ID(PROTOCOL)] = META_FUNC(int_protocol), 63462306a36Sopenharmony_ci [META_ID(PKTTYPE)] = META_FUNC(int_pkttype), 63562306a36Sopenharmony_ci [META_ID(PKTLEN)] = META_FUNC(int_pktlen), 63662306a36Sopenharmony_ci [META_ID(DATALEN)] = META_FUNC(int_datalen), 63762306a36Sopenharmony_ci [META_ID(MACLEN)] = META_FUNC(int_maclen), 63862306a36Sopenharmony_ci [META_ID(NFMARK)] = META_FUNC(int_mark), 63962306a36Sopenharmony_ci [META_ID(TCINDEX)] = META_FUNC(int_tcindex), 64062306a36Sopenharmony_ci [META_ID(RTCLASSID)] = META_FUNC(int_rtclassid), 64162306a36Sopenharmony_ci [META_ID(RTIIF)] = META_FUNC(int_rtiif), 64262306a36Sopenharmony_ci [META_ID(SK_FAMILY)] = META_FUNC(int_sk_family), 64362306a36Sopenharmony_ci [META_ID(SK_STATE)] = META_FUNC(int_sk_state), 64462306a36Sopenharmony_ci [META_ID(SK_REUSE)] = META_FUNC(int_sk_reuse), 64562306a36Sopenharmony_ci [META_ID(SK_BOUND_IF)] = META_FUNC(int_sk_bound_if), 64662306a36Sopenharmony_ci [META_ID(SK_REFCNT)] = META_FUNC(int_sk_refcnt), 64762306a36Sopenharmony_ci [META_ID(SK_RCVBUF)] = META_FUNC(int_sk_rcvbuf), 64862306a36Sopenharmony_ci [META_ID(SK_SNDBUF)] = META_FUNC(int_sk_sndbuf), 64962306a36Sopenharmony_ci [META_ID(SK_SHUTDOWN)] = META_FUNC(int_sk_shutdown), 65062306a36Sopenharmony_ci [META_ID(SK_PROTO)] = META_FUNC(int_sk_proto), 65162306a36Sopenharmony_ci [META_ID(SK_TYPE)] = META_FUNC(int_sk_type), 65262306a36Sopenharmony_ci [META_ID(SK_RMEM_ALLOC)] = META_FUNC(int_sk_rmem_alloc), 65362306a36Sopenharmony_ci [META_ID(SK_WMEM_ALLOC)] = META_FUNC(int_sk_wmem_alloc), 65462306a36Sopenharmony_ci [META_ID(SK_OMEM_ALLOC)] = META_FUNC(int_sk_omem_alloc), 65562306a36Sopenharmony_ci [META_ID(SK_WMEM_QUEUED)] = META_FUNC(int_sk_wmem_queued), 65662306a36Sopenharmony_ci [META_ID(SK_RCV_QLEN)] = META_FUNC(int_sk_rcv_qlen), 65762306a36Sopenharmony_ci [META_ID(SK_SND_QLEN)] = META_FUNC(int_sk_snd_qlen), 65862306a36Sopenharmony_ci [META_ID(SK_ERR_QLEN)] = META_FUNC(int_sk_err_qlen), 65962306a36Sopenharmony_ci [META_ID(SK_FORWARD_ALLOCS)] = META_FUNC(int_sk_fwd_alloc), 66062306a36Sopenharmony_ci [META_ID(SK_ALLOCS)] = META_FUNC(int_sk_alloc), 66162306a36Sopenharmony_ci [META_ID(SK_HASH)] = META_FUNC(int_sk_hash), 66262306a36Sopenharmony_ci [META_ID(SK_LINGERTIME)] = META_FUNC(int_sk_lingertime), 66362306a36Sopenharmony_ci [META_ID(SK_ACK_BACKLOG)] = META_FUNC(int_sk_ack_bl), 66462306a36Sopenharmony_ci [META_ID(SK_MAX_ACK_BACKLOG)] = META_FUNC(int_sk_max_ack_bl), 66562306a36Sopenharmony_ci [META_ID(SK_PRIO)] = META_FUNC(int_sk_prio), 66662306a36Sopenharmony_ci [META_ID(SK_RCVLOWAT)] = META_FUNC(int_sk_rcvlowat), 66762306a36Sopenharmony_ci [META_ID(SK_RCVTIMEO)] = META_FUNC(int_sk_rcvtimeo), 66862306a36Sopenharmony_ci [META_ID(SK_SNDTIMEO)] = META_FUNC(int_sk_sndtimeo), 66962306a36Sopenharmony_ci [META_ID(SK_SENDMSG_OFF)] = META_FUNC(int_sk_sendmsg_off), 67062306a36Sopenharmony_ci [META_ID(SK_WRITE_PENDING)] = META_FUNC(int_sk_write_pend), 67162306a36Sopenharmony_ci [META_ID(VLAN_TAG)] = META_FUNC(int_vlan_tag), 67262306a36Sopenharmony_ci [META_ID(RXHASH)] = META_FUNC(int_rxhash), 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci}; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic inline struct meta_ops *meta_ops(struct meta_value *val) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci return &__meta_ops[meta_type(val)][meta_id(val)]; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/************************************************************************** 68262306a36Sopenharmony_ci * Type specific operations for TCF_META_TYPE_VAR 68362306a36Sopenharmony_ci **************************************************************************/ 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int meta_var_compare(struct meta_obj *a, struct meta_obj *b) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci int r = a->len - b->len; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (r == 0) 69062306a36Sopenharmony_ci r = memcmp((void *) a->value, (void *) b->value, a->len); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return r; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int meta_var_change(struct meta_value *dst, struct nlattr *nla) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci int len = nla_len(nla); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci dst->val = (unsigned long)kmemdup(nla_data(nla), len, GFP_KERNEL); 70062306a36Sopenharmony_ci if (dst->val == 0UL) 70162306a36Sopenharmony_ci return -ENOMEM; 70262306a36Sopenharmony_ci dst->len = len; 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic void meta_var_destroy(struct meta_value *v) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci kfree((void *) v->val); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void meta_var_apply_extras(struct meta_value *v, 71262306a36Sopenharmony_ci struct meta_obj *dst) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci int shift = v->hdr.shift; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (shift && shift < dst->len) 71762306a36Sopenharmony_ci dst->len -= shift; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci if (v->val && v->len && 72362306a36Sopenharmony_ci nla_put(skb, tlv, v->len, (void *) v->val)) 72462306a36Sopenharmony_ci goto nla_put_failure; 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cinla_put_failure: 72862306a36Sopenharmony_ci return -1; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/************************************************************************** 73262306a36Sopenharmony_ci * Type specific operations for TCF_META_TYPE_INT 73362306a36Sopenharmony_ci **************************************************************************/ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int meta_int_compare(struct meta_obj *a, struct meta_obj *b) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci /* Let gcc optimize it, the unlikely is not really based on 73862306a36Sopenharmony_ci * some numbers but jump free code for mismatches seems 73962306a36Sopenharmony_ci * more logical. */ 74062306a36Sopenharmony_ci if (unlikely(a->value == b->value)) 74162306a36Sopenharmony_ci return 0; 74262306a36Sopenharmony_ci else if (a->value < b->value) 74362306a36Sopenharmony_ci return -1; 74462306a36Sopenharmony_ci else 74562306a36Sopenharmony_ci return 1; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int meta_int_change(struct meta_value *dst, struct nlattr *nla) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci if (nla_len(nla) >= sizeof(unsigned long)) { 75162306a36Sopenharmony_ci dst->val = *(unsigned long *) nla_data(nla); 75262306a36Sopenharmony_ci dst->len = sizeof(unsigned long); 75362306a36Sopenharmony_ci } else if (nla_len(nla) == sizeof(u32)) { 75462306a36Sopenharmony_ci dst->val = nla_get_u32(nla); 75562306a36Sopenharmony_ci dst->len = sizeof(u32); 75662306a36Sopenharmony_ci } else 75762306a36Sopenharmony_ci return -EINVAL; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic void meta_int_apply_extras(struct meta_value *v, 76362306a36Sopenharmony_ci struct meta_obj *dst) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci if (v->hdr.shift) 76662306a36Sopenharmony_ci dst->value >>= v->hdr.shift; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (v->val) 76962306a36Sopenharmony_ci dst->value &= v->val; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci if (v->len == sizeof(unsigned long)) { 77562306a36Sopenharmony_ci if (nla_put(skb, tlv, sizeof(unsigned long), &v->val)) 77662306a36Sopenharmony_ci goto nla_put_failure; 77762306a36Sopenharmony_ci } else if (v->len == sizeof(u32)) { 77862306a36Sopenharmony_ci if (nla_put_u32(skb, tlv, v->val)) 77962306a36Sopenharmony_ci goto nla_put_failure; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return 0; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_cinla_put_failure: 78562306a36Sopenharmony_ci return -1; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/************************************************************************** 78962306a36Sopenharmony_ci * Type specific operations table 79062306a36Sopenharmony_ci **************************************************************************/ 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistruct meta_type_ops { 79362306a36Sopenharmony_ci void (*destroy)(struct meta_value *); 79462306a36Sopenharmony_ci int (*compare)(struct meta_obj *, struct meta_obj *); 79562306a36Sopenharmony_ci int (*change)(struct meta_value *, struct nlattr *); 79662306a36Sopenharmony_ci void (*apply_extras)(struct meta_value *, struct meta_obj *); 79762306a36Sopenharmony_ci int (*dump)(struct sk_buff *, struct meta_value *, int); 79862306a36Sopenharmony_ci}; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic const struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX + 1] = { 80162306a36Sopenharmony_ci [TCF_META_TYPE_VAR] = { 80262306a36Sopenharmony_ci .destroy = meta_var_destroy, 80362306a36Sopenharmony_ci .compare = meta_var_compare, 80462306a36Sopenharmony_ci .change = meta_var_change, 80562306a36Sopenharmony_ci .apply_extras = meta_var_apply_extras, 80662306a36Sopenharmony_ci .dump = meta_var_dump 80762306a36Sopenharmony_ci }, 80862306a36Sopenharmony_ci [TCF_META_TYPE_INT] = { 80962306a36Sopenharmony_ci .compare = meta_int_compare, 81062306a36Sopenharmony_ci .change = meta_int_change, 81162306a36Sopenharmony_ci .apply_extras = meta_int_apply_extras, 81262306a36Sopenharmony_ci .dump = meta_int_dump 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci}; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic inline const struct meta_type_ops *meta_type_ops(struct meta_value *v) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci return &__meta_type_ops[meta_type(v)]; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/************************************************************************** 82262306a36Sopenharmony_ci * Core 82362306a36Sopenharmony_ci **************************************************************************/ 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info, 82662306a36Sopenharmony_ci struct meta_value *v, struct meta_obj *dst) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci int err = 0; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (meta_id(v) == TCF_META_ID_VALUE) { 83162306a36Sopenharmony_ci dst->value = v->val; 83262306a36Sopenharmony_ci dst->len = v->len; 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci meta_ops(v)->get(skb, info, v, dst, &err); 83762306a36Sopenharmony_ci if (err < 0) 83862306a36Sopenharmony_ci return err; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (meta_type_ops(v)->apply_extras) 84162306a36Sopenharmony_ci meta_type_ops(v)->apply_extras(v, dst); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m, 84762306a36Sopenharmony_ci struct tcf_pkt_info *info) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci int r; 85062306a36Sopenharmony_ci struct meta_match *meta = (struct meta_match *) m->data; 85162306a36Sopenharmony_ci struct meta_obj l_value, r_value; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 || 85462306a36Sopenharmony_ci meta_get(skb, info, &meta->rvalue, &r_value) < 0) 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci switch (meta->lvalue.hdr.op) { 86062306a36Sopenharmony_ci case TCF_EM_OPND_EQ: 86162306a36Sopenharmony_ci return !r; 86262306a36Sopenharmony_ci case TCF_EM_OPND_LT: 86362306a36Sopenharmony_ci return r < 0; 86462306a36Sopenharmony_ci case TCF_EM_OPND_GT: 86562306a36Sopenharmony_ci return r > 0; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci return 0; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic void meta_delete(struct meta_match *meta) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci if (meta) { 87462306a36Sopenharmony_ci const struct meta_type_ops *ops = meta_type_ops(&meta->lvalue); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (ops && ops->destroy) { 87762306a36Sopenharmony_ci ops->destroy(&meta->lvalue); 87862306a36Sopenharmony_ci ops->destroy(&meta->rvalue); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci kfree(meta); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic inline int meta_change_data(struct meta_value *dst, struct nlattr *nla) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci if (nla) { 88862306a36Sopenharmony_ci if (nla_len(nla) == 0) 88962306a36Sopenharmony_ci return -EINVAL; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return meta_type_ops(dst)->change(dst, nla); 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic inline int meta_is_supported(struct meta_value *val) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci return !meta_id(val) || meta_ops(val)->get; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic const struct nla_policy meta_policy[TCA_EM_META_MAX + 1] = { 90362306a36Sopenharmony_ci [TCA_EM_META_HDR] = { .len = sizeof(struct tcf_meta_hdr) }, 90462306a36Sopenharmony_ci}; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int em_meta_change(struct net *net, void *data, int len, 90762306a36Sopenharmony_ci struct tcf_ematch *m) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci int err; 91062306a36Sopenharmony_ci struct nlattr *tb[TCA_EM_META_MAX + 1]; 91162306a36Sopenharmony_ci struct tcf_meta_hdr *hdr; 91262306a36Sopenharmony_ci struct meta_match *meta = NULL; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci err = nla_parse_deprecated(tb, TCA_EM_META_MAX, data, len, 91562306a36Sopenharmony_ci meta_policy, NULL); 91662306a36Sopenharmony_ci if (err < 0) 91762306a36Sopenharmony_ci goto errout; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci err = -EINVAL; 92062306a36Sopenharmony_ci if (tb[TCA_EM_META_HDR] == NULL) 92162306a36Sopenharmony_ci goto errout; 92262306a36Sopenharmony_ci hdr = nla_data(tb[TCA_EM_META_HDR]); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) || 92562306a36Sopenharmony_ci TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX || 92662306a36Sopenharmony_ci TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX || 92762306a36Sopenharmony_ci TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX) 92862306a36Sopenharmony_ci goto errout; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci meta = kzalloc(sizeof(*meta), GFP_KERNEL); 93162306a36Sopenharmony_ci if (meta == NULL) { 93262306a36Sopenharmony_ci err = -ENOMEM; 93362306a36Sopenharmony_ci goto errout; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left)); 93762306a36Sopenharmony_ci memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right)); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (!meta_is_supported(&meta->lvalue) || 94062306a36Sopenharmony_ci !meta_is_supported(&meta->rvalue)) { 94162306a36Sopenharmony_ci err = -EOPNOTSUPP; 94262306a36Sopenharmony_ci goto errout; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE]) < 0 || 94662306a36Sopenharmony_ci meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE]) < 0) 94762306a36Sopenharmony_ci goto errout; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci m->datalen = sizeof(*meta); 95062306a36Sopenharmony_ci m->data = (unsigned long) meta; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci err = 0; 95362306a36Sopenharmony_cierrout: 95462306a36Sopenharmony_ci if (err && meta) 95562306a36Sopenharmony_ci meta_delete(meta); 95662306a36Sopenharmony_ci return err; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic void em_meta_destroy(struct tcf_ematch *m) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci if (m) 96262306a36Sopenharmony_ci meta_delete((struct meta_match *) m->data); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct meta_match *meta = (struct meta_match *) em->data; 96862306a36Sopenharmony_ci struct tcf_meta_hdr hdr; 96962306a36Sopenharmony_ci const struct meta_type_ops *ops; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci memset(&hdr, 0, sizeof(hdr)); 97262306a36Sopenharmony_ci memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left)); 97362306a36Sopenharmony_ci memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right)); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (nla_put(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr)) 97662306a36Sopenharmony_ci goto nla_put_failure; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ops = meta_type_ops(&meta->lvalue); 97962306a36Sopenharmony_ci if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 || 98062306a36Sopenharmony_ci ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0) 98162306a36Sopenharmony_ci goto nla_put_failure; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_cinla_put_failure: 98662306a36Sopenharmony_ci return -1; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic struct tcf_ematch_ops em_meta_ops = { 99062306a36Sopenharmony_ci .kind = TCF_EM_META, 99162306a36Sopenharmony_ci .change = em_meta_change, 99262306a36Sopenharmony_ci .match = em_meta_match, 99362306a36Sopenharmony_ci .destroy = em_meta_destroy, 99462306a36Sopenharmony_ci .dump = em_meta_dump, 99562306a36Sopenharmony_ci .owner = THIS_MODULE, 99662306a36Sopenharmony_ci .link = LIST_HEAD_INIT(em_meta_ops.link) 99762306a36Sopenharmony_ci}; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic int __init init_em_meta(void) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci return tcf_em_register(&em_meta_ops); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic void __exit exit_em_meta(void) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci tcf_em_unregister(&em_meta_ops); 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cimodule_init(init_em_meta); 101262306a36Sopenharmony_cimodule_exit(exit_em_meta); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ciMODULE_ALIAS_TCF_EMATCH(TCF_EM_META); 1015