162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/cls_api.c Packet classifier API. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Changes: 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/kmod.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/idr.h> 2362306a36Sopenharmony_ci#include <linux/jhash.h> 2462306a36Sopenharmony_ci#include <linux/rculist.h> 2562306a36Sopenharmony_ci#include <linux/rhashtable.h> 2662306a36Sopenharmony_ci#include <net/net_namespace.h> 2762306a36Sopenharmony_ci#include <net/sock.h> 2862306a36Sopenharmony_ci#include <net/netlink.h> 2962306a36Sopenharmony_ci#include <net/pkt_sched.h> 3062306a36Sopenharmony_ci#include <net/pkt_cls.h> 3162306a36Sopenharmony_ci#include <net/tc_act/tc_pedit.h> 3262306a36Sopenharmony_ci#include <net/tc_act/tc_mirred.h> 3362306a36Sopenharmony_ci#include <net/tc_act/tc_vlan.h> 3462306a36Sopenharmony_ci#include <net/tc_act/tc_tunnel_key.h> 3562306a36Sopenharmony_ci#include <net/tc_act/tc_csum.h> 3662306a36Sopenharmony_ci#include <net/tc_act/tc_gact.h> 3762306a36Sopenharmony_ci#include <net/tc_act/tc_police.h> 3862306a36Sopenharmony_ci#include <net/tc_act/tc_sample.h> 3962306a36Sopenharmony_ci#include <net/tc_act/tc_skbedit.h> 4062306a36Sopenharmony_ci#include <net/tc_act/tc_ct.h> 4162306a36Sopenharmony_ci#include <net/tc_act/tc_mpls.h> 4262306a36Sopenharmony_ci#include <net/tc_act/tc_gate.h> 4362306a36Sopenharmony_ci#include <net/flow_offload.h> 4462306a36Sopenharmony_ci#include <net/tc_wrapper.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* The list of all installed classifier types */ 4762306a36Sopenharmony_cistatic LIST_HEAD(tcf_proto_base); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Protects list of registered TC modules. It is pure SMP lock. */ 5062306a36Sopenharmony_cistatic DEFINE_RWLOCK(cls_mod_lock); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct xarray tcf_exts_miss_cookies_xa; 5362306a36Sopenharmony_cistruct tcf_exts_miss_cookie_node { 5462306a36Sopenharmony_ci const struct tcf_chain *chain; 5562306a36Sopenharmony_ci const struct tcf_proto *tp; 5662306a36Sopenharmony_ci const struct tcf_exts *exts; 5762306a36Sopenharmony_ci u32 chain_index; 5862306a36Sopenharmony_ci u32 tp_prio; 5962306a36Sopenharmony_ci u32 handle; 6062306a36Sopenharmony_ci u32 miss_cookie_base; 6162306a36Sopenharmony_ci struct rcu_head rcu; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Each tc action entry cookie will be comprised of 32bit miss_cookie_base + 6562306a36Sopenharmony_ci * action index in the exts tc actions array. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ciunion tcf_exts_miss_cookie { 6862306a36Sopenharmony_ci struct { 6962306a36Sopenharmony_ci u32 miss_cookie_base; 7062306a36Sopenharmony_ci u32 act_index; 7162306a36Sopenharmony_ci }; 7262306a36Sopenharmony_ci u64 miss_cookie; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 7662306a36Sopenharmony_cistatic int 7762306a36Sopenharmony_citcf_exts_miss_cookie_base_alloc(struct tcf_exts *exts, struct tcf_proto *tp, 7862306a36Sopenharmony_ci u32 handle) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct tcf_exts_miss_cookie_node *n; 8162306a36Sopenharmony_ci static u32 next; 8262306a36Sopenharmony_ci int err; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (WARN_ON(!handle || !tp->ops->get_exts)) 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci n = kzalloc(sizeof(*n), GFP_KERNEL); 8862306a36Sopenharmony_ci if (!n) 8962306a36Sopenharmony_ci return -ENOMEM; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci n->chain_index = tp->chain->index; 9262306a36Sopenharmony_ci n->chain = tp->chain; 9362306a36Sopenharmony_ci n->tp_prio = tp->prio; 9462306a36Sopenharmony_ci n->tp = tp; 9562306a36Sopenharmony_ci n->exts = exts; 9662306a36Sopenharmony_ci n->handle = handle; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci err = xa_alloc_cyclic(&tcf_exts_miss_cookies_xa, &n->miss_cookie_base, 9962306a36Sopenharmony_ci n, xa_limit_32b, &next, GFP_KERNEL); 10062306a36Sopenharmony_ci if (err) 10162306a36Sopenharmony_ci goto err_xa_alloc; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci exts->miss_cookie_node = n; 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cierr_xa_alloc: 10762306a36Sopenharmony_ci kfree(n); 10862306a36Sopenharmony_ci return err; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void tcf_exts_miss_cookie_base_destroy(struct tcf_exts *exts) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct tcf_exts_miss_cookie_node *n; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!exts->miss_cookie_node) 11662306a36Sopenharmony_ci return; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci n = exts->miss_cookie_node; 11962306a36Sopenharmony_ci xa_erase(&tcf_exts_miss_cookies_xa, n->miss_cookie_base); 12062306a36Sopenharmony_ci kfree_rcu(n, rcu); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct tcf_exts_miss_cookie_node * 12462306a36Sopenharmony_citcf_exts_miss_cookie_lookup(u64 miss_cookie, int *act_index) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci union tcf_exts_miss_cookie mc = { .miss_cookie = miss_cookie, }; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci *act_index = mc.act_index; 12962306a36Sopenharmony_ci return xa_load(&tcf_exts_miss_cookies_xa, mc.miss_cookie_base); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci#else /* IS_ENABLED(CONFIG_NET_TC_SKB_EXT) */ 13262306a36Sopenharmony_cistatic int 13362306a36Sopenharmony_citcf_exts_miss_cookie_base_alloc(struct tcf_exts *exts, struct tcf_proto *tp, 13462306a36Sopenharmony_ci u32 handle) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void tcf_exts_miss_cookie_base_destroy(struct tcf_exts *exts) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_NET_TC_SKB_EXT) */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic u64 tcf_exts_miss_cookie_get(u32 miss_cookie_base, int act_index) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci union tcf_exts_miss_cookie mc = { .act_index = act_index, }; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!miss_cookie_base) 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci mc.miss_cookie_base = miss_cookie_base; 15262306a36Sopenharmony_ci return mc.miss_cookie; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 15662306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(tc_skb_ext_tc); 15762306a36Sopenharmony_ciEXPORT_SYMBOL(tc_skb_ext_tc); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_civoid tc_skb_ext_tc_enable(void) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci static_branch_inc(&tc_skb_ext_tc); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ciEXPORT_SYMBOL(tc_skb_ext_tc_enable); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_civoid tc_skb_ext_tc_disable(void) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci static_branch_dec(&tc_skb_ext_tc); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL(tc_skb_ext_tc_disable); 17062306a36Sopenharmony_ci#endif 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic u32 destroy_obj_hashfn(const struct tcf_proto *tp) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci return jhash_3words(tp->chain->index, tp->prio, 17562306a36Sopenharmony_ci (__force __u32)tp->protocol, 0); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void tcf_proto_signal_destroying(struct tcf_chain *chain, 17962306a36Sopenharmony_ci struct tcf_proto *tp) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct tcf_block *block = chain->block; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci mutex_lock(&block->proto_destroy_lock); 18462306a36Sopenharmony_ci hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node, 18562306a36Sopenharmony_ci destroy_obj_hashfn(tp)); 18662306a36Sopenharmony_ci mutex_unlock(&block->proto_destroy_lock); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic bool tcf_proto_cmp(const struct tcf_proto *tp1, 19062306a36Sopenharmony_ci const struct tcf_proto *tp2) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci return tp1->chain->index == tp2->chain->index && 19362306a36Sopenharmony_ci tp1->prio == tp2->prio && 19462306a36Sopenharmony_ci tp1->protocol == tp2->protocol; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic bool tcf_proto_exists_destroying(struct tcf_chain *chain, 19862306a36Sopenharmony_ci struct tcf_proto *tp) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u32 hash = destroy_obj_hashfn(tp); 20162306a36Sopenharmony_ci struct tcf_proto *iter; 20262306a36Sopenharmony_ci bool found = false; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci rcu_read_lock(); 20562306a36Sopenharmony_ci hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter, 20662306a36Sopenharmony_ci destroy_ht_node, hash) { 20762306a36Sopenharmony_ci if (tcf_proto_cmp(tp, iter)) { 20862306a36Sopenharmony_ci found = true; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci rcu_read_unlock(); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return found; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void 21862306a36Sopenharmony_citcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct tcf_block *block = chain->block; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci mutex_lock(&block->proto_destroy_lock); 22362306a36Sopenharmony_ci if (hash_hashed(&tp->destroy_ht_node)) 22462306a36Sopenharmony_ci hash_del_rcu(&tp->destroy_ht_node); 22562306a36Sopenharmony_ci mutex_unlock(&block->proto_destroy_lock); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* Find classifier type by string name */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci const struct tcf_proto_ops *t, *res = NULL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (kind) { 23562306a36Sopenharmony_ci read_lock(&cls_mod_lock); 23662306a36Sopenharmony_ci list_for_each_entry(t, &tcf_proto_base, head) { 23762306a36Sopenharmony_ci if (strcmp(kind, t->kind) == 0) { 23862306a36Sopenharmony_ci if (try_module_get(t->owner)) 23962306a36Sopenharmony_ci res = t; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci read_unlock(&cls_mod_lock); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci return res; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic const struct tcf_proto_ops * 24962306a36Sopenharmony_citcf_proto_lookup_ops(const char *kind, bool rtnl_held, 25062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci const struct tcf_proto_ops *ops; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ops = __tcf_proto_lookup_ops(kind); 25562306a36Sopenharmony_ci if (ops) 25662306a36Sopenharmony_ci return ops; 25762306a36Sopenharmony_ci#ifdef CONFIG_MODULES 25862306a36Sopenharmony_ci if (rtnl_held) 25962306a36Sopenharmony_ci rtnl_unlock(); 26062306a36Sopenharmony_ci request_module("cls_%s", kind); 26162306a36Sopenharmony_ci if (rtnl_held) 26262306a36Sopenharmony_ci rtnl_lock(); 26362306a36Sopenharmony_ci ops = __tcf_proto_lookup_ops(kind); 26462306a36Sopenharmony_ci /* We dropped the RTNL semaphore in order to perform 26562306a36Sopenharmony_ci * the module load. So, even if we succeeded in loading 26662306a36Sopenharmony_ci * the module we have to replay the request. We indicate 26762306a36Sopenharmony_ci * this using -EAGAIN. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci if (ops) { 27062306a36Sopenharmony_ci module_put(ops->owner); 27162306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci#endif 27462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "TC classifier not found"); 27562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* Register(unregister) new classifier type */ 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciint register_tcf_proto_ops(struct tcf_proto_ops *ops) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct tcf_proto_ops *t; 28362306a36Sopenharmony_ci int rc = -EEXIST; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci write_lock(&cls_mod_lock); 28662306a36Sopenharmony_ci list_for_each_entry(t, &tcf_proto_base, head) 28762306a36Sopenharmony_ci if (!strcmp(ops->kind, t->kind)) 28862306a36Sopenharmony_ci goto out; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci list_add_tail(&ops->head, &tcf_proto_base); 29162306a36Sopenharmony_ci rc = 0; 29262306a36Sopenharmony_ciout: 29362306a36Sopenharmony_ci write_unlock(&cls_mod_lock); 29462306a36Sopenharmony_ci return rc; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ciEXPORT_SYMBOL(register_tcf_proto_ops); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct workqueue_struct *tc_filter_wq; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_civoid unregister_tcf_proto_ops(struct tcf_proto_ops *ops) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct tcf_proto_ops *t; 30362306a36Sopenharmony_ci int rc = -ENOENT; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Wait for outstanding call_rcu()s, if any, from a 30662306a36Sopenharmony_ci * tcf_proto_ops's destroy() handler. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci rcu_barrier(); 30962306a36Sopenharmony_ci flush_workqueue(tc_filter_wq); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci write_lock(&cls_mod_lock); 31262306a36Sopenharmony_ci list_for_each_entry(t, &tcf_proto_base, head) { 31362306a36Sopenharmony_ci if (t == ops) { 31462306a36Sopenharmony_ci list_del(&t->head); 31562306a36Sopenharmony_ci rc = 0; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci write_unlock(&cls_mod_lock); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci WARN(rc, "unregister tc filter kind(%s) failed %d\n", ops->kind, rc); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_tcf_proto_ops); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cibool tcf_queue_work(struct rcu_work *rwork, work_func_t func) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci INIT_RCU_WORK(rwork, func); 32862306a36Sopenharmony_ci return queue_rcu_work(tc_filter_wq, rwork); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_queue_work); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* Select new prio value from the range, managed by kernel. */ 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic inline u32 tcf_auto_prio(struct tcf_proto *tp) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci u32 first = TC_H_MAKE(0xC0000000U, 0U); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (tp) 33962306a36Sopenharmony_ci first = tp->prio - 1; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return TC_H_MAJ(first); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic bool tcf_proto_check_kind(struct nlattr *kind, char *name) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci if (kind) 34762306a36Sopenharmony_ci return nla_strscpy(name, kind, IFNAMSIZ) < 0; 34862306a36Sopenharmony_ci memset(name, 0, IFNAMSIZ); 34962306a36Sopenharmony_ci return false; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic bool tcf_proto_is_unlocked(const char *kind) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci const struct tcf_proto_ops *ops; 35562306a36Sopenharmony_ci bool ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (strlen(kind) == 0) 35862306a36Sopenharmony_ci return false; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ops = tcf_proto_lookup_ops(kind, false, NULL); 36162306a36Sopenharmony_ci /* On error return false to take rtnl lock. Proto lookup/create 36262306a36Sopenharmony_ci * functions will perform lookup again and properly handle errors. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci if (IS_ERR(ops)) 36562306a36Sopenharmony_ci return false; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED); 36862306a36Sopenharmony_ci module_put(ops->owner); 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, 37362306a36Sopenharmony_ci u32 prio, struct tcf_chain *chain, 37462306a36Sopenharmony_ci bool rtnl_held, 37562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct tcf_proto *tp; 37862306a36Sopenharmony_ci int err; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci tp = kzalloc(sizeof(*tp), GFP_KERNEL); 38162306a36Sopenharmony_ci if (!tp) 38262306a36Sopenharmony_ci return ERR_PTR(-ENOBUFS); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack); 38562306a36Sopenharmony_ci if (IS_ERR(tp->ops)) { 38662306a36Sopenharmony_ci err = PTR_ERR(tp->ops); 38762306a36Sopenharmony_ci goto errout; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci tp->classify = tp->ops->classify; 39062306a36Sopenharmony_ci tp->protocol = protocol; 39162306a36Sopenharmony_ci tp->prio = prio; 39262306a36Sopenharmony_ci tp->chain = chain; 39362306a36Sopenharmony_ci spin_lock_init(&tp->lock); 39462306a36Sopenharmony_ci refcount_set(&tp->refcnt, 1); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci err = tp->ops->init(tp); 39762306a36Sopenharmony_ci if (err) { 39862306a36Sopenharmony_ci module_put(tp->ops->owner); 39962306a36Sopenharmony_ci goto errout; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci return tp; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cierrout: 40462306a36Sopenharmony_ci kfree(tp); 40562306a36Sopenharmony_ci return ERR_PTR(err); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic void tcf_proto_get(struct tcf_proto *tp) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci refcount_inc(&tp->refcnt); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic void tcf_chain_put(struct tcf_chain *chain); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held, 41662306a36Sopenharmony_ci bool sig_destroy, struct netlink_ext_ack *extack) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci tp->ops->destroy(tp, rtnl_held, extack); 41962306a36Sopenharmony_ci if (sig_destroy) 42062306a36Sopenharmony_ci tcf_proto_signal_destroyed(tp->chain, tp); 42162306a36Sopenharmony_ci tcf_chain_put(tp->chain); 42262306a36Sopenharmony_ci module_put(tp->ops->owner); 42362306a36Sopenharmony_ci kfree_rcu(tp, rcu); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held, 42762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (refcount_dec_and_test(&tp->refcnt)) 43062306a36Sopenharmony_ci tcf_proto_destroy(tp, rtnl_held, true, extack); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic bool tcf_proto_check_delete(struct tcf_proto *tp) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci if (tp->ops->delete_empty) 43662306a36Sopenharmony_ci return tp->ops->delete_empty(tp); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci tp->deleting = true; 43962306a36Sopenharmony_ci return tp->deleting; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void tcf_proto_mark_delete(struct tcf_proto *tp) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci spin_lock(&tp->lock); 44562306a36Sopenharmony_ci tp->deleting = true; 44662306a36Sopenharmony_ci spin_unlock(&tp->lock); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic bool tcf_proto_is_deleting(struct tcf_proto *tp) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci bool deleting; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci spin_lock(&tp->lock); 45462306a36Sopenharmony_ci deleting = tp->deleting; 45562306a36Sopenharmony_ci spin_unlock(&tp->lock); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return deleting; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci#define ASSERT_BLOCK_LOCKED(block) \ 46162306a36Sopenharmony_ci lockdep_assert_held(&(block)->lock) 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistruct tcf_filter_chain_list_item { 46462306a36Sopenharmony_ci struct list_head list; 46562306a36Sopenharmony_ci tcf_chain_head_change_t *chain_head_change; 46662306a36Sopenharmony_ci void *chain_head_change_priv; 46762306a36Sopenharmony_ci}; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_create(struct tcf_block *block, 47062306a36Sopenharmony_ci u32 chain_index) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct tcf_chain *chain; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ASSERT_BLOCK_LOCKED(block); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci chain = kzalloc(sizeof(*chain), GFP_KERNEL); 47762306a36Sopenharmony_ci if (!chain) 47862306a36Sopenharmony_ci return NULL; 47962306a36Sopenharmony_ci list_add_tail_rcu(&chain->list, &block->chain_list); 48062306a36Sopenharmony_ci mutex_init(&chain->filter_chain_lock); 48162306a36Sopenharmony_ci chain->block = block; 48262306a36Sopenharmony_ci chain->index = chain_index; 48362306a36Sopenharmony_ci chain->refcnt = 1; 48462306a36Sopenharmony_ci if (!chain->index) 48562306a36Sopenharmony_ci block->chain0.chain = chain; 48662306a36Sopenharmony_ci return chain; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, 49062306a36Sopenharmony_ci struct tcf_proto *tp_head) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci if (item->chain_head_change) 49362306a36Sopenharmony_ci item->chain_head_change(tp_head, item->chain_head_change_priv); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void tcf_chain0_head_change(struct tcf_chain *chain, 49762306a36Sopenharmony_ci struct tcf_proto *tp_head) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct tcf_filter_chain_list_item *item; 50062306a36Sopenharmony_ci struct tcf_block *block = chain->block; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (chain->index) 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci mutex_lock(&block->lock); 50662306a36Sopenharmony_ci list_for_each_entry(item, &block->chain0.filter_chain_list, list) 50762306a36Sopenharmony_ci tcf_chain_head_change_item(item, tp_head); 50862306a36Sopenharmony_ci mutex_unlock(&block->lock); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* Returns true if block can be safely freed. */ 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic bool tcf_chain_detach(struct tcf_chain *chain) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct tcf_block *block = chain->block; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ASSERT_BLOCK_LOCKED(block); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci list_del_rcu(&chain->list); 52062306a36Sopenharmony_ci if (!chain->index) 52162306a36Sopenharmony_ci block->chain0.chain = NULL; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (list_empty(&block->chain_list) && 52462306a36Sopenharmony_ci refcount_read(&block->refcnt) == 0) 52562306a36Sopenharmony_ci return true; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return false; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic void tcf_block_destroy(struct tcf_block *block) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci mutex_destroy(&block->lock); 53362306a36Sopenharmony_ci mutex_destroy(&block->proto_destroy_lock); 53462306a36Sopenharmony_ci kfree_rcu(block, rcu); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic void tcf_chain_destroy(struct tcf_chain *chain, bool free_block) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct tcf_block *block = chain->block; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci mutex_destroy(&chain->filter_chain_lock); 54262306a36Sopenharmony_ci kfree_rcu(chain, rcu); 54362306a36Sopenharmony_ci if (free_block) 54462306a36Sopenharmony_ci tcf_block_destroy(block); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void tcf_chain_hold(struct tcf_chain *chain) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci ASSERT_BLOCK_LOCKED(chain->block); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ++chain->refcnt; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic bool tcf_chain_held_by_acts_only(struct tcf_chain *chain) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci ASSERT_BLOCK_LOCKED(chain->block); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* In case all the references are action references, this 55962306a36Sopenharmony_ci * chain should not be shown to the user. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci return chain->refcnt == chain->action_refcnt; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_lookup(struct tcf_block *block, 56562306a36Sopenharmony_ci u32 chain_index) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct tcf_chain *chain; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ASSERT_BLOCK_LOCKED(block); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci list_for_each_entry(chain, &block->chain_list, list) { 57262306a36Sopenharmony_ci if (chain->index == chain_index) 57362306a36Sopenharmony_ci return chain; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci return NULL; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 57962306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block, 58062306a36Sopenharmony_ci u32 chain_index) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct tcf_chain *chain; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci list_for_each_entry_rcu(chain, &block->chain_list, list) { 58562306a36Sopenharmony_ci if (chain->index == chain_index) 58662306a36Sopenharmony_ci return chain; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci return NULL; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci#endif 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 59362306a36Sopenharmony_ci u32 seq, u16 flags, int event, bool unicast, 59462306a36Sopenharmony_ci struct netlink_ext_ack *extack); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic struct tcf_chain *__tcf_chain_get(struct tcf_block *block, 59762306a36Sopenharmony_ci u32 chain_index, bool create, 59862306a36Sopenharmony_ci bool by_act) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct tcf_chain *chain = NULL; 60162306a36Sopenharmony_ci bool is_first_reference; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci mutex_lock(&block->lock); 60462306a36Sopenharmony_ci chain = tcf_chain_lookup(block, chain_index); 60562306a36Sopenharmony_ci if (chain) { 60662306a36Sopenharmony_ci tcf_chain_hold(chain); 60762306a36Sopenharmony_ci } else { 60862306a36Sopenharmony_ci if (!create) 60962306a36Sopenharmony_ci goto errout; 61062306a36Sopenharmony_ci chain = tcf_chain_create(block, chain_index); 61162306a36Sopenharmony_ci if (!chain) 61262306a36Sopenharmony_ci goto errout; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (by_act) 61662306a36Sopenharmony_ci ++chain->action_refcnt; 61762306a36Sopenharmony_ci is_first_reference = chain->refcnt - chain->action_refcnt == 1; 61862306a36Sopenharmony_ci mutex_unlock(&block->lock); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Send notification only in case we got the first 62162306a36Sopenharmony_ci * non-action reference. Until then, the chain acts only as 62262306a36Sopenharmony_ci * a placeholder for actions pointing to it and user ought 62362306a36Sopenharmony_ci * not know about them. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci if (is_first_reference && !by_act) 62662306a36Sopenharmony_ci tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 62762306a36Sopenharmony_ci RTM_NEWCHAIN, false, NULL); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return chain; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cierrout: 63262306a36Sopenharmony_ci mutex_unlock(&block->lock); 63362306a36Sopenharmony_ci return chain; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 63762306a36Sopenharmony_ci bool create) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci return __tcf_chain_get(block, chain_index, create, false); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistruct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci return __tcf_chain_get(block, chain_index, true, true); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_chain_get_by_act); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 64962306a36Sopenharmony_ci void *tmplt_priv); 65062306a36Sopenharmony_cistatic int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 65162306a36Sopenharmony_ci void *tmplt_priv, u32 chain_index, 65262306a36Sopenharmony_ci struct tcf_block *block, struct sk_buff *oskb, 65362306a36Sopenharmony_ci u32 seq, u16 flags, bool unicast); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic void __tcf_chain_put(struct tcf_chain *chain, bool by_act, 65662306a36Sopenharmony_ci bool explicitly_created) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct tcf_block *block = chain->block; 65962306a36Sopenharmony_ci const struct tcf_proto_ops *tmplt_ops; 66062306a36Sopenharmony_ci unsigned int refcnt, non_act_refcnt; 66162306a36Sopenharmony_ci bool free_block = false; 66262306a36Sopenharmony_ci void *tmplt_priv; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci mutex_lock(&block->lock); 66562306a36Sopenharmony_ci if (explicitly_created) { 66662306a36Sopenharmony_ci if (!chain->explicitly_created) { 66762306a36Sopenharmony_ci mutex_unlock(&block->lock); 66862306a36Sopenharmony_ci return; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci chain->explicitly_created = false; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (by_act) 67462306a36Sopenharmony_ci chain->action_refcnt--; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* tc_chain_notify_delete can't be called while holding block lock. 67762306a36Sopenharmony_ci * However, when block is unlocked chain can be changed concurrently, so 67862306a36Sopenharmony_ci * save these to temporary variables. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci refcnt = --chain->refcnt; 68162306a36Sopenharmony_ci non_act_refcnt = refcnt - chain->action_refcnt; 68262306a36Sopenharmony_ci tmplt_ops = chain->tmplt_ops; 68362306a36Sopenharmony_ci tmplt_priv = chain->tmplt_priv; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (non_act_refcnt == chain->explicitly_created && !by_act) { 68662306a36Sopenharmony_ci if (non_act_refcnt == 0) 68762306a36Sopenharmony_ci tc_chain_notify_delete(tmplt_ops, tmplt_priv, 68862306a36Sopenharmony_ci chain->index, block, NULL, 0, 0, 68962306a36Sopenharmony_ci false); 69062306a36Sopenharmony_ci /* Last reference to chain, no need to lock. */ 69162306a36Sopenharmony_ci chain->flushing = false; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (refcnt == 0) 69562306a36Sopenharmony_ci free_block = tcf_chain_detach(chain); 69662306a36Sopenharmony_ci mutex_unlock(&block->lock); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (refcnt == 0) { 69962306a36Sopenharmony_ci tc_chain_tmplt_del(tmplt_ops, tmplt_priv); 70062306a36Sopenharmony_ci tcf_chain_destroy(chain, free_block); 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void tcf_chain_put(struct tcf_chain *chain) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci __tcf_chain_put(chain, false, false); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_civoid tcf_chain_put_by_act(struct tcf_chain *chain) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci __tcf_chain_put(chain, true, false); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_chain_put_by_act); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void tcf_chain_put_explicitly_created(struct tcf_chain *chain) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci __tcf_chain_put(chain, false, true); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct tcf_proto *tp, *tp_next; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 72562306a36Sopenharmony_ci tp = tcf_chain_dereference(chain->filter_chain, chain); 72662306a36Sopenharmony_ci while (tp) { 72762306a36Sopenharmony_ci tp_next = rcu_dereference_protected(tp->next, 1); 72862306a36Sopenharmony_ci tcf_proto_signal_destroying(chain, tp); 72962306a36Sopenharmony_ci tp = tp_next; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci tp = tcf_chain_dereference(chain->filter_chain, chain); 73262306a36Sopenharmony_ci RCU_INIT_POINTER(chain->filter_chain, NULL); 73362306a36Sopenharmony_ci tcf_chain0_head_change(chain, NULL); 73462306a36Sopenharmony_ci chain->flushing = true; 73562306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci while (tp) { 73862306a36Sopenharmony_ci tp_next = rcu_dereference_protected(tp->next, 1); 73962306a36Sopenharmony_ci tcf_proto_put(tp, rtnl_held, NULL); 74062306a36Sopenharmony_ci tp = tp_next; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int tcf_block_setup(struct tcf_block *block, 74562306a36Sopenharmony_ci struct flow_block_offload *bo); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic void tcf_block_offload_init(struct flow_block_offload *bo, 74862306a36Sopenharmony_ci struct net_device *dev, struct Qdisc *sch, 74962306a36Sopenharmony_ci enum flow_block_command command, 75062306a36Sopenharmony_ci enum flow_block_binder_type binder_type, 75162306a36Sopenharmony_ci struct flow_block *flow_block, 75262306a36Sopenharmony_ci bool shared, struct netlink_ext_ack *extack) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci bo->net = dev_net(dev); 75562306a36Sopenharmony_ci bo->command = command; 75662306a36Sopenharmony_ci bo->binder_type = binder_type; 75762306a36Sopenharmony_ci bo->block = flow_block; 75862306a36Sopenharmony_ci bo->block_shared = shared; 75962306a36Sopenharmony_ci bo->extack = extack; 76062306a36Sopenharmony_ci bo->sch = sch; 76162306a36Sopenharmony_ci bo->cb_list_head = &flow_block->cb_list; 76262306a36Sopenharmony_ci INIT_LIST_HEAD(&bo->cb_list); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void tcf_block_unbind(struct tcf_block *block, 76662306a36Sopenharmony_ci struct flow_block_offload *bo); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void tc_block_indr_cleanup(struct flow_block_cb *block_cb) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct tcf_block *block = block_cb->indr.data; 77162306a36Sopenharmony_ci struct net_device *dev = block_cb->indr.dev; 77262306a36Sopenharmony_ci struct Qdisc *sch = block_cb->indr.sch; 77362306a36Sopenharmony_ci struct netlink_ext_ack extack = {}; 77462306a36Sopenharmony_ci struct flow_block_offload bo = {}; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci tcf_block_offload_init(&bo, dev, sch, FLOW_BLOCK_UNBIND, 77762306a36Sopenharmony_ci block_cb->indr.binder_type, 77862306a36Sopenharmony_ci &block->flow_block, tcf_block_shared(block), 77962306a36Sopenharmony_ci &extack); 78062306a36Sopenharmony_ci rtnl_lock(); 78162306a36Sopenharmony_ci down_write(&block->cb_lock); 78262306a36Sopenharmony_ci list_del(&block_cb->driver_list); 78362306a36Sopenharmony_ci list_move(&block_cb->list, &bo.cb_list); 78462306a36Sopenharmony_ci tcf_block_unbind(block, &bo); 78562306a36Sopenharmony_ci up_write(&block->cb_lock); 78662306a36Sopenharmony_ci rtnl_unlock(); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic bool tcf_block_offload_in_use(struct tcf_block *block) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci return atomic_read(&block->offloadcnt); 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int tcf_block_offload_cmd(struct tcf_block *block, 79562306a36Sopenharmony_ci struct net_device *dev, struct Qdisc *sch, 79662306a36Sopenharmony_ci struct tcf_block_ext_info *ei, 79762306a36Sopenharmony_ci enum flow_block_command command, 79862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct flow_block_offload bo = {}; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci tcf_block_offload_init(&bo, dev, sch, command, ei->binder_type, 80362306a36Sopenharmony_ci &block->flow_block, tcf_block_shared(block), 80462306a36Sopenharmony_ci extack); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (dev->netdev_ops->ndo_setup_tc) { 80762306a36Sopenharmony_ci int err; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); 81062306a36Sopenharmony_ci if (err < 0) { 81162306a36Sopenharmony_ci if (err != -EOPNOTSUPP) 81262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Driver ndo_setup_tc failed"); 81362306a36Sopenharmony_ci return err; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return tcf_block_setup(block, &bo); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci flow_indr_dev_setup_offload(dev, sch, TC_SETUP_BLOCK, block, &bo, 82062306a36Sopenharmony_ci tc_block_indr_cleanup); 82162306a36Sopenharmony_ci tcf_block_setup(block, &bo); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return -EOPNOTSUPP; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, 82762306a36Sopenharmony_ci struct tcf_block_ext_info *ei, 82862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct net_device *dev = q->dev_queue->dev; 83162306a36Sopenharmony_ci int err; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci down_write(&block->cb_lock); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* If tc offload feature is disabled and the block we try to bind 83662306a36Sopenharmony_ci * to already has some offloaded filters, forbid to bind. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci if (dev->netdev_ops->ndo_setup_tc && 83962306a36Sopenharmony_ci !tc_can_offload(dev) && 84062306a36Sopenharmony_ci tcf_block_offload_in_use(block)) { 84162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled"); 84262306a36Sopenharmony_ci err = -EOPNOTSUPP; 84362306a36Sopenharmony_ci goto err_unlock; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_BIND, extack); 84762306a36Sopenharmony_ci if (err == -EOPNOTSUPP) 84862306a36Sopenharmony_ci goto no_offload_dev_inc; 84962306a36Sopenharmony_ci if (err) 85062306a36Sopenharmony_ci goto err_unlock; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci up_write(&block->cb_lock); 85362306a36Sopenharmony_ci return 0; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cino_offload_dev_inc: 85662306a36Sopenharmony_ci if (tcf_block_offload_in_use(block)) 85762306a36Sopenharmony_ci goto err_unlock; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci err = 0; 86062306a36Sopenharmony_ci block->nooffloaddevcnt++; 86162306a36Sopenharmony_cierr_unlock: 86262306a36Sopenharmony_ci up_write(&block->cb_lock); 86362306a36Sopenharmony_ci return err; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, 86762306a36Sopenharmony_ci struct tcf_block_ext_info *ei) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct net_device *dev = q->dev_queue->dev; 87062306a36Sopenharmony_ci int err; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci down_write(&block->cb_lock); 87362306a36Sopenharmony_ci err = tcf_block_offload_cmd(block, dev, q, ei, FLOW_BLOCK_UNBIND, NULL); 87462306a36Sopenharmony_ci if (err == -EOPNOTSUPP) 87562306a36Sopenharmony_ci goto no_offload_dev_dec; 87662306a36Sopenharmony_ci up_write(&block->cb_lock); 87762306a36Sopenharmony_ci return; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cino_offload_dev_dec: 88062306a36Sopenharmony_ci WARN_ON(block->nooffloaddevcnt-- == 0); 88162306a36Sopenharmony_ci up_write(&block->cb_lock); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int 88562306a36Sopenharmony_citcf_chain0_head_change_cb_add(struct tcf_block *block, 88662306a36Sopenharmony_ci struct tcf_block_ext_info *ei, 88762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct tcf_filter_chain_list_item *item; 89062306a36Sopenharmony_ci struct tcf_chain *chain0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci item = kmalloc(sizeof(*item), GFP_KERNEL); 89362306a36Sopenharmony_ci if (!item) { 89462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed"); 89562306a36Sopenharmony_ci return -ENOMEM; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci item->chain_head_change = ei->chain_head_change; 89862306a36Sopenharmony_ci item->chain_head_change_priv = ei->chain_head_change_priv; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci mutex_lock(&block->lock); 90162306a36Sopenharmony_ci chain0 = block->chain0.chain; 90262306a36Sopenharmony_ci if (chain0) 90362306a36Sopenharmony_ci tcf_chain_hold(chain0); 90462306a36Sopenharmony_ci else 90562306a36Sopenharmony_ci list_add(&item->list, &block->chain0.filter_chain_list); 90662306a36Sopenharmony_ci mutex_unlock(&block->lock); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (chain0) { 90962306a36Sopenharmony_ci struct tcf_proto *tp_head; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci mutex_lock(&chain0->filter_chain_lock); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci tp_head = tcf_chain_dereference(chain0->filter_chain, chain0); 91462306a36Sopenharmony_ci if (tp_head) 91562306a36Sopenharmony_ci tcf_chain_head_change_item(item, tp_head); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci mutex_lock(&block->lock); 91862306a36Sopenharmony_ci list_add(&item->list, &block->chain0.filter_chain_list); 91962306a36Sopenharmony_ci mutex_unlock(&block->lock); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci mutex_unlock(&chain0->filter_chain_lock); 92262306a36Sopenharmony_ci tcf_chain_put(chain0); 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return 0; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic void 92962306a36Sopenharmony_citcf_chain0_head_change_cb_del(struct tcf_block *block, 93062306a36Sopenharmony_ci struct tcf_block_ext_info *ei) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct tcf_filter_chain_list_item *item; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci mutex_lock(&block->lock); 93562306a36Sopenharmony_ci list_for_each_entry(item, &block->chain0.filter_chain_list, list) { 93662306a36Sopenharmony_ci if ((!ei->chain_head_change && !ei->chain_head_change_priv) || 93762306a36Sopenharmony_ci (item->chain_head_change == ei->chain_head_change && 93862306a36Sopenharmony_ci item->chain_head_change_priv == ei->chain_head_change_priv)) { 93962306a36Sopenharmony_ci if (block->chain0.chain) 94062306a36Sopenharmony_ci tcf_chain_head_change_item(item, NULL); 94162306a36Sopenharmony_ci list_del(&item->list); 94262306a36Sopenharmony_ci mutex_unlock(&block->lock); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci kfree(item); 94562306a36Sopenharmony_ci return; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci mutex_unlock(&block->lock); 94962306a36Sopenharmony_ci WARN_ON(1); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistruct tcf_net { 95362306a36Sopenharmony_ci spinlock_t idr_lock; /* Protects idr */ 95462306a36Sopenharmony_ci struct idr idr; 95562306a36Sopenharmony_ci}; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic unsigned int tcf_net_id; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int tcf_block_insert(struct tcf_block *block, struct net *net, 96062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct tcf_net *tn = net_generic(net, tcf_net_id); 96362306a36Sopenharmony_ci int err; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci idr_preload(GFP_KERNEL); 96662306a36Sopenharmony_ci spin_lock(&tn->idr_lock); 96762306a36Sopenharmony_ci err = idr_alloc_u32(&tn->idr, block, &block->index, block->index, 96862306a36Sopenharmony_ci GFP_NOWAIT); 96962306a36Sopenharmony_ci spin_unlock(&tn->idr_lock); 97062306a36Sopenharmony_ci idr_preload_end(); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return err; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void tcf_block_remove(struct tcf_block *block, struct net *net) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct tcf_net *tn = net_generic(net, tcf_net_id); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci spin_lock(&tn->idr_lock); 98062306a36Sopenharmony_ci idr_remove(&tn->idr, block->index); 98162306a36Sopenharmony_ci spin_unlock(&tn->idr_lock); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q, 98562306a36Sopenharmony_ci u32 block_index, 98662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct tcf_block *block; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci block = kzalloc(sizeof(*block), GFP_KERNEL); 99162306a36Sopenharmony_ci if (!block) { 99262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Memory allocation for block failed"); 99362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci mutex_init(&block->lock); 99662306a36Sopenharmony_ci mutex_init(&block->proto_destroy_lock); 99762306a36Sopenharmony_ci init_rwsem(&block->cb_lock); 99862306a36Sopenharmony_ci flow_block_init(&block->flow_block); 99962306a36Sopenharmony_ci INIT_LIST_HEAD(&block->chain_list); 100062306a36Sopenharmony_ci INIT_LIST_HEAD(&block->owner_list); 100162306a36Sopenharmony_ci INIT_LIST_HEAD(&block->chain0.filter_chain_list); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci refcount_set(&block->refcnt, 1); 100462306a36Sopenharmony_ci block->net = net; 100562306a36Sopenharmony_ci block->index = block_index; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* Don't store q pointer for blocks which are shared */ 100862306a36Sopenharmony_ci if (!tcf_block_shared(block)) 100962306a36Sopenharmony_ci block->q = q; 101062306a36Sopenharmony_ci return block; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct tcf_net *tn = net_generic(net, tcf_net_id); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return idr_find(&tn->idr, block_index); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct tcf_block *block; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci rcu_read_lock(); 102562306a36Sopenharmony_ci block = tcf_block_lookup(net, block_index); 102662306a36Sopenharmony_ci if (block && !refcount_inc_not_zero(&block->refcnt)) 102762306a36Sopenharmony_ci block = NULL; 102862306a36Sopenharmony_ci rcu_read_unlock(); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return block; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic struct tcf_chain * 103462306a36Sopenharmony_ci__tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci mutex_lock(&block->lock); 103762306a36Sopenharmony_ci if (chain) 103862306a36Sopenharmony_ci chain = list_is_last(&chain->list, &block->chain_list) ? 103962306a36Sopenharmony_ci NULL : list_next_entry(chain, list); 104062306a36Sopenharmony_ci else 104162306a36Sopenharmony_ci chain = list_first_entry_or_null(&block->chain_list, 104262306a36Sopenharmony_ci struct tcf_chain, list); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* skip all action-only chains */ 104562306a36Sopenharmony_ci while (chain && tcf_chain_held_by_acts_only(chain)) 104662306a36Sopenharmony_ci chain = list_is_last(&chain->list, &block->chain_list) ? 104762306a36Sopenharmony_ci NULL : list_next_entry(chain, list); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (chain) 105062306a36Sopenharmony_ci tcf_chain_hold(chain); 105162306a36Sopenharmony_ci mutex_unlock(&block->lock); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return chain; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci/* Function to be used by all clients that want to iterate over all chains on 105762306a36Sopenharmony_ci * block. It properly obtains block->lock and takes reference to chain before 105862306a36Sopenharmony_ci * returning it. Users of this function must be tolerant to concurrent chain 105962306a36Sopenharmony_ci * insertion/deletion or ensure that no concurrent chain modification is 106062306a36Sopenharmony_ci * possible. Note that all netlink dump callbacks cannot guarantee to provide 106162306a36Sopenharmony_ci * consistent dump because rtnl lock is released each time skb is filled with 106262306a36Sopenharmony_ci * data and sent to user-space. 106362306a36Sopenharmony_ci */ 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistruct tcf_chain * 106662306a36Sopenharmony_citcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (chain) 107162306a36Sopenharmony_ci tcf_chain_put(chain); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci return chain_next; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_get_next_chain); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic struct tcf_proto * 107862306a36Sopenharmony_ci__tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci u32 prio = 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci ASSERT_RTNL(); 108362306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (!tp) { 108662306a36Sopenharmony_ci tp = tcf_chain_dereference(chain->filter_chain, chain); 108762306a36Sopenharmony_ci } else if (tcf_proto_is_deleting(tp)) { 108862306a36Sopenharmony_ci /* 'deleting' flag is set and chain->filter_chain_lock was 108962306a36Sopenharmony_ci * unlocked, which means next pointer could be invalid. Restart 109062306a36Sopenharmony_ci * search. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci prio = tp->prio + 1; 109362306a36Sopenharmony_ci tp = tcf_chain_dereference(chain->filter_chain, chain); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci for (; tp; tp = tcf_chain_dereference(tp->next, chain)) 109662306a36Sopenharmony_ci if (!tp->deleting && tp->prio >= prio) 109762306a36Sopenharmony_ci break; 109862306a36Sopenharmony_ci } else { 109962306a36Sopenharmony_ci tp = tcf_chain_dereference(tp->next, chain); 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (tp) 110362306a36Sopenharmony_ci tcf_proto_get(tp); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci return tp; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* Function to be used by all clients that want to iterate over all tp's on 111162306a36Sopenharmony_ci * chain. Users of this function must be tolerant to concurrent tp 111262306a36Sopenharmony_ci * insertion/deletion or ensure that no concurrent chain modification is 111362306a36Sopenharmony_ci * possible. Note that all netlink dump callbacks cannot guarantee to provide 111462306a36Sopenharmony_ci * consistent dump because rtnl lock is released each time skb is filled with 111562306a36Sopenharmony_ci * data and sent to user-space. 111662306a36Sopenharmony_ci */ 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistruct tcf_proto * 111962306a36Sopenharmony_citcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (tp) 112462306a36Sopenharmony_ci tcf_proto_put(tp, true, NULL); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return tp_next; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_get_next_proto); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct tcf_chain *chain; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Last reference to block. At this point chains cannot be added or 113562306a36Sopenharmony_ci * removed concurrently. 113662306a36Sopenharmony_ci */ 113762306a36Sopenharmony_ci for (chain = tcf_get_next_chain(block, NULL); 113862306a36Sopenharmony_ci chain; 113962306a36Sopenharmony_ci chain = tcf_get_next_chain(block, chain)) { 114062306a36Sopenharmony_ci tcf_chain_put_explicitly_created(chain); 114162306a36Sopenharmony_ci tcf_chain_flush(chain, rtnl_held); 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci/* Lookup Qdisc and increments its reference counter. 114662306a36Sopenharmony_ci * Set parent, if necessary. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int __tcf_qdisc_find(struct net *net, struct Qdisc **q, 115062306a36Sopenharmony_ci u32 *parent, int ifindex, bool rtnl_held, 115162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci const struct Qdisc_class_ops *cops; 115462306a36Sopenharmony_ci struct net_device *dev; 115562306a36Sopenharmony_ci int err = 0; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci rcu_read_lock(); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* Find link */ 116362306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, ifindex); 116462306a36Sopenharmony_ci if (!dev) { 116562306a36Sopenharmony_ci rcu_read_unlock(); 116662306a36Sopenharmony_ci return -ENODEV; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Find qdisc */ 117062306a36Sopenharmony_ci if (!*parent) { 117162306a36Sopenharmony_ci *q = rcu_dereference(dev->qdisc); 117262306a36Sopenharmony_ci *parent = (*q)->handle; 117362306a36Sopenharmony_ci } else { 117462306a36Sopenharmony_ci *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent)); 117562306a36Sopenharmony_ci if (!*q) { 117662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 117762306a36Sopenharmony_ci err = -EINVAL; 117862306a36Sopenharmony_ci goto errout_rcu; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci *q = qdisc_refcount_inc_nz(*q); 118362306a36Sopenharmony_ci if (!*q) { 118462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists"); 118562306a36Sopenharmony_ci err = -EINVAL; 118662306a36Sopenharmony_ci goto errout_rcu; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* Is it classful? */ 119062306a36Sopenharmony_ci cops = (*q)->ops->cl_ops; 119162306a36Sopenharmony_ci if (!cops) { 119262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Qdisc not classful"); 119362306a36Sopenharmony_ci err = -EINVAL; 119462306a36Sopenharmony_ci goto errout_qdisc; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!cops->tcf_block) { 119862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Class doesn't support blocks"); 119962306a36Sopenharmony_ci err = -EOPNOTSUPP; 120062306a36Sopenharmony_ci goto errout_qdisc; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cierrout_rcu: 120462306a36Sopenharmony_ci /* At this point we know that qdisc is not noop_qdisc, 120562306a36Sopenharmony_ci * which means that qdisc holds a reference to net_device 120662306a36Sopenharmony_ci * and we hold a reference to qdisc, so it is safe to release 120762306a36Sopenharmony_ci * rcu read lock. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci rcu_read_unlock(); 121062306a36Sopenharmony_ci return err; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cierrout_qdisc: 121362306a36Sopenharmony_ci rcu_read_unlock(); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (rtnl_held) 121662306a36Sopenharmony_ci qdisc_put(*q); 121762306a36Sopenharmony_ci else 121862306a36Sopenharmony_ci qdisc_put_unlocked(*q); 121962306a36Sopenharmony_ci *q = NULL; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return err; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl, 122562306a36Sopenharmony_ci int ifindex, struct netlink_ext_ack *extack) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) 122862306a36Sopenharmony_ci return 0; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Do we search for filter, attached to class? */ 123162306a36Sopenharmony_ci if (TC_H_MIN(parent)) { 123262306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = q->ops->cl_ops; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci *cl = cops->find(q, parent); 123562306a36Sopenharmony_ci if (*cl == 0) { 123662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified class doesn't exist"); 123762306a36Sopenharmony_ci return -ENOENT; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return 0; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q, 124562306a36Sopenharmony_ci unsigned long cl, int ifindex, 124662306a36Sopenharmony_ci u32 block_index, 124762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci struct tcf_block *block; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 125262306a36Sopenharmony_ci block = tcf_block_refcnt_get(net, block_index); 125362306a36Sopenharmony_ci if (!block) { 125462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Block of given index was not found"); 125562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci } else { 125862306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = q->ops->cl_ops; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci block = cops->tcf_block(q, cl, extack); 126162306a36Sopenharmony_ci if (!block) 126262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (tcf_block_shared(block)) { 126562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters"); 126662306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* Always take reference to block in order to support execution 127062306a36Sopenharmony_ci * of rules update path of cls API without rtnl lock. Caller 127162306a36Sopenharmony_ci * must release block when it is finished using it. 'if' block 127262306a36Sopenharmony_ci * of this conditional obtain reference to block by calling 127362306a36Sopenharmony_ci * tcf_block_refcnt_get(). 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_ci refcount_inc(&block->refcnt); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return block; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic void __tcf_block_put(struct tcf_block *block, struct Qdisc *q, 128262306a36Sopenharmony_ci struct tcf_block_ext_info *ei, bool rtnl_held) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) { 128562306a36Sopenharmony_ci /* Flushing/putting all chains will cause the block to be 128662306a36Sopenharmony_ci * deallocated when last chain is freed. However, if chain_list 128762306a36Sopenharmony_ci * is empty, block has to be manually deallocated. After block 128862306a36Sopenharmony_ci * reference counter reached 0, it is no longer possible to 128962306a36Sopenharmony_ci * increment it or add new chains to block. 129062306a36Sopenharmony_ci */ 129162306a36Sopenharmony_ci bool free_block = list_empty(&block->chain_list); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci mutex_unlock(&block->lock); 129462306a36Sopenharmony_ci if (tcf_block_shared(block)) 129562306a36Sopenharmony_ci tcf_block_remove(block, block->net); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (q) 129862306a36Sopenharmony_ci tcf_block_offload_unbind(block, q, ei); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (free_block) 130162306a36Sopenharmony_ci tcf_block_destroy(block); 130262306a36Sopenharmony_ci else 130362306a36Sopenharmony_ci tcf_block_flush_all_chains(block, rtnl_held); 130462306a36Sopenharmony_ci } else if (q) { 130562306a36Sopenharmony_ci tcf_block_offload_unbind(block, q, ei); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci __tcf_block_put(block, NULL, NULL, rtnl_held); 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci/* Find tcf block. 131562306a36Sopenharmony_ci * Set q, parent, cl when appropriate. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q, 131962306a36Sopenharmony_ci u32 *parent, unsigned long *cl, 132062306a36Sopenharmony_ci int ifindex, u32 block_index, 132162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci struct tcf_block *block; 132462306a36Sopenharmony_ci int err = 0; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci ASSERT_RTNL(); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack); 132962306a36Sopenharmony_ci if (err) 133062306a36Sopenharmony_ci goto errout; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack); 133362306a36Sopenharmony_ci if (err) 133462306a36Sopenharmony_ci goto errout_qdisc; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack); 133762306a36Sopenharmony_ci if (IS_ERR(block)) { 133862306a36Sopenharmony_ci err = PTR_ERR(block); 133962306a36Sopenharmony_ci goto errout_qdisc; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return block; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_cierrout_qdisc: 134562306a36Sopenharmony_ci if (*q) 134662306a36Sopenharmony_ci qdisc_put(*q); 134762306a36Sopenharmony_cierrout: 134862306a36Sopenharmony_ci *q = NULL; 134962306a36Sopenharmony_ci return ERR_PTR(err); 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic void tcf_block_release(struct Qdisc *q, struct tcf_block *block, 135362306a36Sopenharmony_ci bool rtnl_held) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(block)) 135662306a36Sopenharmony_ci tcf_block_refcnt_put(block, rtnl_held); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (q) { 135962306a36Sopenharmony_ci if (rtnl_held) 136062306a36Sopenharmony_ci qdisc_put(q); 136162306a36Sopenharmony_ci else 136262306a36Sopenharmony_ci qdisc_put_unlocked(q); 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_cistruct tcf_block_owner_item { 136762306a36Sopenharmony_ci struct list_head list; 136862306a36Sopenharmony_ci struct Qdisc *q; 136962306a36Sopenharmony_ci enum flow_block_binder_type binder_type; 137062306a36Sopenharmony_ci}; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic void 137362306a36Sopenharmony_citcf_block_owner_netif_keep_dst(struct tcf_block *block, 137462306a36Sopenharmony_ci struct Qdisc *q, 137562306a36Sopenharmony_ci enum flow_block_binder_type binder_type) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci if (block->keep_dst && 137862306a36Sopenharmony_ci binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && 137962306a36Sopenharmony_ci binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 138062306a36Sopenharmony_ci netif_keep_dst(qdisc_dev(q)); 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_civoid tcf_block_netif_keep_dst(struct tcf_block *block) 138462306a36Sopenharmony_ci{ 138562306a36Sopenharmony_ci struct tcf_block_owner_item *item; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci block->keep_dst = true; 138862306a36Sopenharmony_ci list_for_each_entry(item, &block->owner_list, list) 138962306a36Sopenharmony_ci tcf_block_owner_netif_keep_dst(block, item->q, 139062306a36Sopenharmony_ci item->binder_type); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_netif_keep_dst); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic int tcf_block_owner_add(struct tcf_block *block, 139562306a36Sopenharmony_ci struct Qdisc *q, 139662306a36Sopenharmony_ci enum flow_block_binder_type binder_type) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct tcf_block_owner_item *item; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci item = kmalloc(sizeof(*item), GFP_KERNEL); 140162306a36Sopenharmony_ci if (!item) 140262306a36Sopenharmony_ci return -ENOMEM; 140362306a36Sopenharmony_ci item->q = q; 140462306a36Sopenharmony_ci item->binder_type = binder_type; 140562306a36Sopenharmony_ci list_add(&item->list, &block->owner_list); 140662306a36Sopenharmony_ci return 0; 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic void tcf_block_owner_del(struct tcf_block *block, 141062306a36Sopenharmony_ci struct Qdisc *q, 141162306a36Sopenharmony_ci enum flow_block_binder_type binder_type) 141262306a36Sopenharmony_ci{ 141362306a36Sopenharmony_ci struct tcf_block_owner_item *item; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci list_for_each_entry(item, &block->owner_list, list) { 141662306a36Sopenharmony_ci if (item->q == q && item->binder_type == binder_type) { 141762306a36Sopenharmony_ci list_del(&item->list); 141862306a36Sopenharmony_ci kfree(item); 141962306a36Sopenharmony_ci return; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci WARN_ON(1); 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ciint tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, 142662306a36Sopenharmony_ci struct tcf_block_ext_info *ei, 142762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci struct net *net = qdisc_net(q); 143062306a36Sopenharmony_ci struct tcf_block *block = NULL; 143162306a36Sopenharmony_ci int err; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (ei->block_index) 143462306a36Sopenharmony_ci /* block_index not 0 means the shared block is requested */ 143562306a36Sopenharmony_ci block = tcf_block_refcnt_get(net, ei->block_index); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (!block) { 143862306a36Sopenharmony_ci block = tcf_block_create(net, q, ei->block_index, extack); 143962306a36Sopenharmony_ci if (IS_ERR(block)) 144062306a36Sopenharmony_ci return PTR_ERR(block); 144162306a36Sopenharmony_ci if (tcf_block_shared(block)) { 144262306a36Sopenharmony_ci err = tcf_block_insert(block, net, extack); 144362306a36Sopenharmony_ci if (err) 144462306a36Sopenharmony_ci goto err_block_insert; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci err = tcf_block_owner_add(block, q, ei->binder_type); 144962306a36Sopenharmony_ci if (err) 145062306a36Sopenharmony_ci goto err_block_owner_add; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci tcf_block_owner_netif_keep_dst(block, q, ei->binder_type); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci err = tcf_chain0_head_change_cb_add(block, ei, extack); 145562306a36Sopenharmony_ci if (err) 145662306a36Sopenharmony_ci goto err_chain0_head_change_cb_add; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci err = tcf_block_offload_bind(block, q, ei, extack); 145962306a36Sopenharmony_ci if (err) 146062306a36Sopenharmony_ci goto err_block_offload_bind; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci *p_block = block; 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cierr_block_offload_bind: 146662306a36Sopenharmony_ci tcf_chain0_head_change_cb_del(block, ei); 146762306a36Sopenharmony_cierr_chain0_head_change_cb_add: 146862306a36Sopenharmony_ci tcf_block_owner_del(block, q, ei->binder_type); 146962306a36Sopenharmony_cierr_block_owner_add: 147062306a36Sopenharmony_cierr_block_insert: 147162306a36Sopenharmony_ci tcf_block_refcnt_put(block, true); 147262306a36Sopenharmony_ci return err; 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_get_ext); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct tcf_proto __rcu **p_filter_chain = priv; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci rcu_assign_pointer(*p_filter_chain, tp_head); 148162306a36Sopenharmony_ci} 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ciint tcf_block_get(struct tcf_block **p_block, 148462306a36Sopenharmony_ci struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, 148562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 148662306a36Sopenharmony_ci{ 148762306a36Sopenharmony_ci struct tcf_block_ext_info ei = { 148862306a36Sopenharmony_ci .chain_head_change = tcf_chain_head_change_dflt, 148962306a36Sopenharmony_ci .chain_head_change_priv = p_filter_chain, 149062306a36Sopenharmony_ci }; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci WARN_ON(!p_filter_chain); 149362306a36Sopenharmony_ci return tcf_block_get_ext(p_block, q, &ei, extack); 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_get); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci/* XXX: Standalone actions are not allowed to jump to any chain, and bound 149862306a36Sopenharmony_ci * actions should be all removed after flushing. 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_civoid tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, 150162306a36Sopenharmony_ci struct tcf_block_ext_info *ei) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci if (!block) 150462306a36Sopenharmony_ci return; 150562306a36Sopenharmony_ci tcf_chain0_head_change_cb_del(block, ei); 150662306a36Sopenharmony_ci tcf_block_owner_del(block, q, ei->binder_type); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci __tcf_block_put(block, q, ei, true); 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_put_ext); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_civoid tcf_block_put(struct tcf_block *block) 151362306a36Sopenharmony_ci{ 151462306a36Sopenharmony_ci struct tcf_block_ext_info ei = {0, }; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (!block) 151762306a36Sopenharmony_ci return; 151862306a36Sopenharmony_ci tcf_block_put_ext(block, block->q, &ei); 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_block_put); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic int 152462306a36Sopenharmony_citcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb, 152562306a36Sopenharmony_ci void *cb_priv, bool add, bool offload_in_use, 152662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci struct tcf_chain *chain, *chain_prev; 152962306a36Sopenharmony_ci struct tcf_proto *tp, *tp_prev; 153062306a36Sopenharmony_ci int err; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci lockdep_assert_held(&block->cb_lock); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci for (chain = __tcf_get_next_chain(block, NULL); 153562306a36Sopenharmony_ci chain; 153662306a36Sopenharmony_ci chain_prev = chain, 153762306a36Sopenharmony_ci chain = __tcf_get_next_chain(block, chain), 153862306a36Sopenharmony_ci tcf_chain_put(chain_prev)) { 153962306a36Sopenharmony_ci if (chain->tmplt_ops && add) 154062306a36Sopenharmony_ci chain->tmplt_ops->tmplt_reoffload(chain, true, cb, 154162306a36Sopenharmony_ci cb_priv); 154262306a36Sopenharmony_ci for (tp = __tcf_get_next_proto(chain, NULL); tp; 154362306a36Sopenharmony_ci tp_prev = tp, 154462306a36Sopenharmony_ci tp = __tcf_get_next_proto(chain, tp), 154562306a36Sopenharmony_ci tcf_proto_put(tp_prev, true, NULL)) { 154662306a36Sopenharmony_ci if (tp->ops->reoffload) { 154762306a36Sopenharmony_ci err = tp->ops->reoffload(tp, add, cb, cb_priv, 154862306a36Sopenharmony_ci extack); 154962306a36Sopenharmony_ci if (err && add) 155062306a36Sopenharmony_ci goto err_playback_remove; 155162306a36Sopenharmony_ci } else if (add && offload_in_use) { 155262306a36Sopenharmony_ci err = -EOPNOTSUPP; 155362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support"); 155462306a36Sopenharmony_ci goto err_playback_remove; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci if (chain->tmplt_ops && !add) 155862306a36Sopenharmony_ci chain->tmplt_ops->tmplt_reoffload(chain, false, cb, 155962306a36Sopenharmony_ci cb_priv); 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return 0; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cierr_playback_remove: 156562306a36Sopenharmony_ci tcf_proto_put(tp, true, NULL); 156662306a36Sopenharmony_ci tcf_chain_put(chain); 156762306a36Sopenharmony_ci tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use, 156862306a36Sopenharmony_ci extack); 156962306a36Sopenharmony_ci return err; 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_cistatic int tcf_block_bind(struct tcf_block *block, 157362306a36Sopenharmony_ci struct flow_block_offload *bo) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci struct flow_block_cb *block_cb, *next; 157662306a36Sopenharmony_ci int err, i = 0; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci lockdep_assert_held(&block->cb_lock); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci list_for_each_entry(block_cb, &bo->cb_list, list) { 158162306a36Sopenharmony_ci err = tcf_block_playback_offloads(block, block_cb->cb, 158262306a36Sopenharmony_ci block_cb->cb_priv, true, 158362306a36Sopenharmony_ci tcf_block_offload_in_use(block), 158462306a36Sopenharmony_ci bo->extack); 158562306a36Sopenharmony_ci if (err) 158662306a36Sopenharmony_ci goto err_unroll; 158762306a36Sopenharmony_ci if (!bo->unlocked_driver_cb) 158862306a36Sopenharmony_ci block->lockeddevcnt++; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci i++; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci list_splice(&bo->cb_list, &block->flow_block.cb_list); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci return 0; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cierr_unroll: 159762306a36Sopenharmony_ci list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 159862306a36Sopenharmony_ci list_del(&block_cb->driver_list); 159962306a36Sopenharmony_ci if (i-- > 0) { 160062306a36Sopenharmony_ci list_del(&block_cb->list); 160162306a36Sopenharmony_ci tcf_block_playback_offloads(block, block_cb->cb, 160262306a36Sopenharmony_ci block_cb->cb_priv, false, 160362306a36Sopenharmony_ci tcf_block_offload_in_use(block), 160462306a36Sopenharmony_ci NULL); 160562306a36Sopenharmony_ci if (!bo->unlocked_driver_cb) 160662306a36Sopenharmony_ci block->lockeddevcnt--; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci flow_block_cb_free(block_cb); 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci return err; 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_cistatic void tcf_block_unbind(struct tcf_block *block, 161562306a36Sopenharmony_ci struct flow_block_offload *bo) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct flow_block_cb *block_cb, *next; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci lockdep_assert_held(&block->cb_lock); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { 162262306a36Sopenharmony_ci tcf_block_playback_offloads(block, block_cb->cb, 162362306a36Sopenharmony_ci block_cb->cb_priv, false, 162462306a36Sopenharmony_ci tcf_block_offload_in_use(block), 162562306a36Sopenharmony_ci NULL); 162662306a36Sopenharmony_ci list_del(&block_cb->list); 162762306a36Sopenharmony_ci flow_block_cb_free(block_cb); 162862306a36Sopenharmony_ci if (!bo->unlocked_driver_cb) 162962306a36Sopenharmony_ci block->lockeddevcnt--; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci} 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_cistatic int tcf_block_setup(struct tcf_block *block, 163462306a36Sopenharmony_ci struct flow_block_offload *bo) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci int err; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci switch (bo->command) { 163962306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 164062306a36Sopenharmony_ci err = tcf_block_bind(block, bo); 164162306a36Sopenharmony_ci break; 164262306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 164362306a36Sopenharmony_ci err = 0; 164462306a36Sopenharmony_ci tcf_block_unbind(block, bo); 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci default: 164762306a36Sopenharmony_ci WARN_ON_ONCE(1); 164862306a36Sopenharmony_ci err = -EOPNOTSUPP; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci return err; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci/* Main classifier routine: scans classifier chain attached 165562306a36Sopenharmony_ci * to this qdisc, (optionally) tests for protocol and asks 165662306a36Sopenharmony_ci * specific classifiers. 165762306a36Sopenharmony_ci */ 165862306a36Sopenharmony_cistatic inline int __tcf_classify(struct sk_buff *skb, 165962306a36Sopenharmony_ci const struct tcf_proto *tp, 166062306a36Sopenharmony_ci const struct tcf_proto *orig_tp, 166162306a36Sopenharmony_ci struct tcf_result *res, 166262306a36Sopenharmony_ci bool compat_mode, 166362306a36Sopenharmony_ci struct tcf_exts_miss_cookie_node *n, 166462306a36Sopenharmony_ci int act_index, 166562306a36Sopenharmony_ci u32 *last_executed_chain) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 166862306a36Sopenharmony_ci const int max_reclassify_loop = 16; 166962306a36Sopenharmony_ci const struct tcf_proto *first_tp; 167062306a36Sopenharmony_ci int limit = 0; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cireclassify: 167362306a36Sopenharmony_ci#endif 167462306a36Sopenharmony_ci for (; tp; tp = rcu_dereference_bh(tp->next)) { 167562306a36Sopenharmony_ci __be16 protocol = skb_protocol(skb, false); 167662306a36Sopenharmony_ci int err = 0; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (n) { 167962306a36Sopenharmony_ci struct tcf_exts *exts; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (n->tp_prio != tp->prio) 168262306a36Sopenharmony_ci continue; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* We re-lookup the tp and chain based on index instead 168562306a36Sopenharmony_ci * of having hard refs and locks to them, so do a sanity 168662306a36Sopenharmony_ci * check if any of tp,chain,exts was replaced by the 168762306a36Sopenharmony_ci * time we got here with a cookie from hardware. 168862306a36Sopenharmony_ci */ 168962306a36Sopenharmony_ci if (unlikely(n->tp != tp || n->tp->chain != n->chain || 169062306a36Sopenharmony_ci !tp->ops->get_exts)) 169162306a36Sopenharmony_ci return TC_ACT_SHOT; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci exts = tp->ops->get_exts(tp, n->handle); 169462306a36Sopenharmony_ci if (unlikely(!exts || n->exts != exts)) 169562306a36Sopenharmony_ci return TC_ACT_SHOT; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci n = NULL; 169862306a36Sopenharmony_ci err = tcf_exts_exec_ex(skb, exts, act_index, res); 169962306a36Sopenharmony_ci } else { 170062306a36Sopenharmony_ci if (tp->protocol != protocol && 170162306a36Sopenharmony_ci tp->protocol != htons(ETH_P_ALL)) 170262306a36Sopenharmony_ci continue; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci err = tc_classify(skb, tp, res); 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 170762306a36Sopenharmony_ci if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { 170862306a36Sopenharmony_ci first_tp = orig_tp; 170962306a36Sopenharmony_ci *last_executed_chain = first_tp->chain->index; 171062306a36Sopenharmony_ci goto reset; 171162306a36Sopenharmony_ci } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { 171262306a36Sopenharmony_ci first_tp = res->goto_tp; 171362306a36Sopenharmony_ci *last_executed_chain = err & TC_ACT_EXT_VAL_MASK; 171462306a36Sopenharmony_ci goto reset; 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci#endif 171762306a36Sopenharmony_ci if (err >= 0) 171862306a36Sopenharmony_ci return err; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (unlikely(n)) 172262306a36Sopenharmony_ci return TC_ACT_SHOT; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci return TC_ACT_UNSPEC; /* signal: continue lookup */ 172562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 172662306a36Sopenharmony_cireset: 172762306a36Sopenharmony_ci if (unlikely(limit++ >= max_reclassify_loop)) { 172862306a36Sopenharmony_ci net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n", 172962306a36Sopenharmony_ci tp->chain->block->index, 173062306a36Sopenharmony_ci tp->prio & 0xffff, 173162306a36Sopenharmony_ci ntohs(tp->protocol)); 173262306a36Sopenharmony_ci return TC_ACT_SHOT; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci tp = first_tp; 173662306a36Sopenharmony_ci goto reclassify; 173762306a36Sopenharmony_ci#endif 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ciint tcf_classify(struct sk_buff *skb, 174162306a36Sopenharmony_ci const struct tcf_block *block, 174262306a36Sopenharmony_ci const struct tcf_proto *tp, 174362306a36Sopenharmony_ci struct tcf_result *res, bool compat_mode) 174462306a36Sopenharmony_ci{ 174562306a36Sopenharmony_ci#if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT) 174662306a36Sopenharmony_ci u32 last_executed_chain = 0; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci return __tcf_classify(skb, tp, tp, res, compat_mode, NULL, 0, 174962306a36Sopenharmony_ci &last_executed_chain); 175062306a36Sopenharmony_ci#else 175162306a36Sopenharmony_ci u32 last_executed_chain = tp ? tp->chain->index : 0; 175262306a36Sopenharmony_ci struct tcf_exts_miss_cookie_node *n = NULL; 175362306a36Sopenharmony_ci const struct tcf_proto *orig_tp = tp; 175462306a36Sopenharmony_ci struct tc_skb_ext *ext; 175562306a36Sopenharmony_ci int act_index = 0; 175662306a36Sopenharmony_ci int ret; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (block) { 175962306a36Sopenharmony_ci ext = skb_ext_find(skb, TC_SKB_EXT); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci if (ext && (ext->chain || ext->act_miss)) { 176262306a36Sopenharmony_ci struct tcf_chain *fchain; 176362306a36Sopenharmony_ci u32 chain; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci if (ext->act_miss) { 176662306a36Sopenharmony_ci n = tcf_exts_miss_cookie_lookup(ext->act_miss_cookie, 176762306a36Sopenharmony_ci &act_index); 176862306a36Sopenharmony_ci if (!n) 176962306a36Sopenharmony_ci return TC_ACT_SHOT; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci chain = n->chain_index; 177262306a36Sopenharmony_ci } else { 177362306a36Sopenharmony_ci chain = ext->chain; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci fchain = tcf_chain_lookup_rcu(block, chain); 177762306a36Sopenharmony_ci if (!fchain) 177862306a36Sopenharmony_ci return TC_ACT_SHOT; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci /* Consume, so cloned/redirect skbs won't inherit ext */ 178162306a36Sopenharmony_ci skb_ext_del(skb, TC_SKB_EXT); 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci tp = rcu_dereference_bh(fchain->filter_chain); 178462306a36Sopenharmony_ci last_executed_chain = fchain->index; 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode, n, act_index, 178962306a36Sopenharmony_ci &last_executed_chain); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci if (tc_skb_ext_tc_enabled()) { 179262306a36Sopenharmony_ci /* If we missed on some chain */ 179362306a36Sopenharmony_ci if (ret == TC_ACT_UNSPEC && last_executed_chain) { 179462306a36Sopenharmony_ci struct tc_skb_cb *cb = tc_skb_cb(skb); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci ext = tc_skb_ext_alloc(skb); 179762306a36Sopenharmony_ci if (WARN_ON_ONCE(!ext)) 179862306a36Sopenharmony_ci return TC_ACT_SHOT; 179962306a36Sopenharmony_ci ext->chain = last_executed_chain; 180062306a36Sopenharmony_ci ext->mru = cb->mru; 180162306a36Sopenharmony_ci ext->post_ct = cb->post_ct; 180262306a36Sopenharmony_ci ext->post_ct_snat = cb->post_ct_snat; 180362306a36Sopenharmony_ci ext->post_ct_dnat = cb->post_ct_dnat; 180462306a36Sopenharmony_ci ext->zone = cb->zone; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci return ret; 180962306a36Sopenharmony_ci#endif 181062306a36Sopenharmony_ci} 181162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_classify); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_cistruct tcf_chain_info { 181462306a36Sopenharmony_ci struct tcf_proto __rcu **pprev; 181562306a36Sopenharmony_ci struct tcf_proto __rcu *next; 181662306a36Sopenharmony_ci}; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain, 181962306a36Sopenharmony_ci struct tcf_chain_info *chain_info) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci return tcf_chain_dereference(*chain_info->pprev, chain); 182262306a36Sopenharmony_ci} 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cistatic int tcf_chain_tp_insert(struct tcf_chain *chain, 182562306a36Sopenharmony_ci struct tcf_chain_info *chain_info, 182662306a36Sopenharmony_ci struct tcf_proto *tp) 182762306a36Sopenharmony_ci{ 182862306a36Sopenharmony_ci if (chain->flushing) 182962306a36Sopenharmony_ci return -EAGAIN; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info)); 183262306a36Sopenharmony_ci if (*chain_info->pprev == chain->filter_chain) 183362306a36Sopenharmony_ci tcf_chain0_head_change(chain, tp); 183462306a36Sopenharmony_ci tcf_proto_get(tp); 183562306a36Sopenharmony_ci rcu_assign_pointer(*chain_info->pprev, tp); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci return 0; 183862306a36Sopenharmony_ci} 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_cistatic void tcf_chain_tp_remove(struct tcf_chain *chain, 184162306a36Sopenharmony_ci struct tcf_chain_info *chain_info, 184262306a36Sopenharmony_ci struct tcf_proto *tp) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci tcf_proto_mark_delete(tp); 184762306a36Sopenharmony_ci if (tp == chain->filter_chain) 184862306a36Sopenharmony_ci tcf_chain0_head_change(chain, next); 184962306a36Sopenharmony_ci RCU_INIT_POINTER(*chain_info->pprev, next); 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 185362306a36Sopenharmony_ci struct tcf_chain_info *chain_info, 185462306a36Sopenharmony_ci u32 protocol, u32 prio, 185562306a36Sopenharmony_ci bool prio_allocate); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci/* Try to insert new proto. 185862306a36Sopenharmony_ci * If proto with specified priority already exists, free new proto 185962306a36Sopenharmony_ci * and return existing one. 186062306a36Sopenharmony_ci */ 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain, 186362306a36Sopenharmony_ci struct tcf_proto *tp_new, 186462306a36Sopenharmony_ci u32 protocol, u32 prio, 186562306a36Sopenharmony_ci bool rtnl_held) 186662306a36Sopenharmony_ci{ 186762306a36Sopenharmony_ci struct tcf_chain_info chain_info; 186862306a36Sopenharmony_ci struct tcf_proto *tp; 186962306a36Sopenharmony_ci int err = 0; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (tcf_proto_exists_destroying(chain, tp_new)) { 187462306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 187562306a36Sopenharmony_ci tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 187662306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci tp = tcf_chain_tp_find(chain, &chain_info, 188062306a36Sopenharmony_ci protocol, prio, false); 188162306a36Sopenharmony_ci if (!tp) 188262306a36Sopenharmony_ci err = tcf_chain_tp_insert(chain, &chain_info, tp_new); 188362306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci if (tp) { 188662306a36Sopenharmony_ci tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 188762306a36Sopenharmony_ci tp_new = tp; 188862306a36Sopenharmony_ci } else if (err) { 188962306a36Sopenharmony_ci tcf_proto_destroy(tp_new, rtnl_held, false, NULL); 189062306a36Sopenharmony_ci tp_new = ERR_PTR(err); 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci return tp_new; 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic void tcf_chain_tp_delete_empty(struct tcf_chain *chain, 189762306a36Sopenharmony_ci struct tcf_proto *tp, bool rtnl_held, 189862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci struct tcf_chain_info chain_info; 190162306a36Sopenharmony_ci struct tcf_proto *tp_iter; 190262306a36Sopenharmony_ci struct tcf_proto **pprev; 190362306a36Sopenharmony_ci struct tcf_proto *next; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci /* Atomically find and remove tp from chain. */ 190862306a36Sopenharmony_ci for (pprev = &chain->filter_chain; 190962306a36Sopenharmony_ci (tp_iter = tcf_chain_dereference(*pprev, chain)); 191062306a36Sopenharmony_ci pprev = &tp_iter->next) { 191162306a36Sopenharmony_ci if (tp_iter == tp) { 191262306a36Sopenharmony_ci chain_info.pprev = pprev; 191362306a36Sopenharmony_ci chain_info.next = tp_iter->next; 191462306a36Sopenharmony_ci WARN_ON(tp_iter->deleting); 191562306a36Sopenharmony_ci break; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci /* Verify that tp still exists and no new filters were inserted 191962306a36Sopenharmony_ci * concurrently. 192062306a36Sopenharmony_ci * Mark tp for deletion if it is empty. 192162306a36Sopenharmony_ci */ 192262306a36Sopenharmony_ci if (!tp_iter || !tcf_proto_check_delete(tp)) { 192362306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 192462306a36Sopenharmony_ci return; 192562306a36Sopenharmony_ci } 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci tcf_proto_signal_destroying(chain, tp); 192862306a36Sopenharmony_ci next = tcf_chain_dereference(chain_info.next, chain); 192962306a36Sopenharmony_ci if (tp == chain->filter_chain) 193062306a36Sopenharmony_ci tcf_chain0_head_change(chain, next); 193162306a36Sopenharmony_ci RCU_INIT_POINTER(*chain_info.pprev, next); 193262306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci tcf_proto_put(tp, rtnl_held, extack); 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, 193862306a36Sopenharmony_ci struct tcf_chain_info *chain_info, 193962306a36Sopenharmony_ci u32 protocol, u32 prio, 194062306a36Sopenharmony_ci bool prio_allocate) 194162306a36Sopenharmony_ci{ 194262306a36Sopenharmony_ci struct tcf_proto **pprev; 194362306a36Sopenharmony_ci struct tcf_proto *tp; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* Check the chain for existence of proto-tcf with this priority */ 194662306a36Sopenharmony_ci for (pprev = &chain->filter_chain; 194762306a36Sopenharmony_ci (tp = tcf_chain_dereference(*pprev, chain)); 194862306a36Sopenharmony_ci pprev = &tp->next) { 194962306a36Sopenharmony_ci if (tp->prio >= prio) { 195062306a36Sopenharmony_ci if (tp->prio == prio) { 195162306a36Sopenharmony_ci if (prio_allocate || 195262306a36Sopenharmony_ci (tp->protocol != protocol && protocol)) 195362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 195462306a36Sopenharmony_ci } else { 195562306a36Sopenharmony_ci tp = NULL; 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci break; 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci } 196062306a36Sopenharmony_ci chain_info->pprev = pprev; 196162306a36Sopenharmony_ci if (tp) { 196262306a36Sopenharmony_ci chain_info->next = tp->next; 196362306a36Sopenharmony_ci tcf_proto_get(tp); 196462306a36Sopenharmony_ci } else { 196562306a36Sopenharmony_ci chain_info->next = NULL; 196662306a36Sopenharmony_ci } 196762306a36Sopenharmony_ci return tp; 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_cistatic int tcf_fill_node(struct net *net, struct sk_buff *skb, 197162306a36Sopenharmony_ci struct tcf_proto *tp, struct tcf_block *block, 197262306a36Sopenharmony_ci struct Qdisc *q, u32 parent, void *fh, 197362306a36Sopenharmony_ci u32 portid, u32 seq, u16 flags, int event, 197462306a36Sopenharmony_ci bool terse_dump, bool rtnl_held, 197562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 197662306a36Sopenharmony_ci{ 197762306a36Sopenharmony_ci struct tcmsg *tcm; 197862306a36Sopenharmony_ci struct nlmsghdr *nlh; 197962306a36Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 198262306a36Sopenharmony_ci if (!nlh) 198362306a36Sopenharmony_ci goto out_nlmsg_trim; 198462306a36Sopenharmony_ci tcm = nlmsg_data(nlh); 198562306a36Sopenharmony_ci tcm->tcm_family = AF_UNSPEC; 198662306a36Sopenharmony_ci tcm->tcm__pad1 = 0; 198762306a36Sopenharmony_ci tcm->tcm__pad2 = 0; 198862306a36Sopenharmony_ci if (q) { 198962306a36Sopenharmony_ci tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 199062306a36Sopenharmony_ci tcm->tcm_parent = parent; 199162306a36Sopenharmony_ci } else { 199262306a36Sopenharmony_ci tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 199362306a36Sopenharmony_ci tcm->tcm_block_index = block->index; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); 199662306a36Sopenharmony_ci if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) 199762306a36Sopenharmony_ci goto nla_put_failure; 199862306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) 199962306a36Sopenharmony_ci goto nla_put_failure; 200062306a36Sopenharmony_ci if (!fh) { 200162306a36Sopenharmony_ci tcm->tcm_handle = 0; 200262306a36Sopenharmony_ci } else if (terse_dump) { 200362306a36Sopenharmony_ci if (tp->ops->terse_dump) { 200462306a36Sopenharmony_ci if (tp->ops->terse_dump(net, tp, fh, skb, tcm, 200562306a36Sopenharmony_ci rtnl_held) < 0) 200662306a36Sopenharmony_ci goto nla_put_failure; 200762306a36Sopenharmony_ci } else { 200862306a36Sopenharmony_ci goto cls_op_not_supp; 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci } else { 201162306a36Sopenharmony_ci if (tp->ops->dump && 201262306a36Sopenharmony_ci tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0) 201362306a36Sopenharmony_ci goto nla_put_failure; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (extack && extack->_msg && 201762306a36Sopenharmony_ci nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) 201862306a36Sopenharmony_ci goto nla_put_failure; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci return skb->len; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ciout_nlmsg_trim: 202562306a36Sopenharmony_cinla_put_failure: 202662306a36Sopenharmony_cicls_op_not_supp: 202762306a36Sopenharmony_ci nlmsg_trim(skb, b); 202862306a36Sopenharmony_ci return -1; 202962306a36Sopenharmony_ci} 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_cistatic int tfilter_notify(struct net *net, struct sk_buff *oskb, 203262306a36Sopenharmony_ci struct nlmsghdr *n, struct tcf_proto *tp, 203362306a36Sopenharmony_ci struct tcf_block *block, struct Qdisc *q, 203462306a36Sopenharmony_ci u32 parent, void *fh, int event, bool unicast, 203562306a36Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 203662306a36Sopenharmony_ci{ 203762306a36Sopenharmony_ci struct sk_buff *skb; 203862306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 203962306a36Sopenharmony_ci int err = 0; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 204262306a36Sopenharmony_ci if (!skb) 204362306a36Sopenharmony_ci return -ENOBUFS; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 204662306a36Sopenharmony_ci n->nlmsg_seq, n->nlmsg_flags, event, 204762306a36Sopenharmony_ci false, rtnl_held, extack) <= 0) { 204862306a36Sopenharmony_ci kfree_skb(skb); 204962306a36Sopenharmony_ci return -EINVAL; 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci if (unicast) 205362306a36Sopenharmony_ci err = rtnl_unicast(skb, net, portid); 205462306a36Sopenharmony_ci else 205562306a36Sopenharmony_ci err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 205662306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 205762306a36Sopenharmony_ci return err; 205862306a36Sopenharmony_ci} 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_cistatic int tfilter_del_notify(struct net *net, struct sk_buff *oskb, 206162306a36Sopenharmony_ci struct nlmsghdr *n, struct tcf_proto *tp, 206262306a36Sopenharmony_ci struct tcf_block *block, struct Qdisc *q, 206362306a36Sopenharmony_ci u32 parent, void *fh, bool unicast, bool *last, 206462306a36Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 206562306a36Sopenharmony_ci{ 206662306a36Sopenharmony_ci struct sk_buff *skb; 206762306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 206862306a36Sopenharmony_ci int err; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 207162306a36Sopenharmony_ci if (!skb) 207262306a36Sopenharmony_ci return -ENOBUFS; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, 207562306a36Sopenharmony_ci n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER, 207662306a36Sopenharmony_ci false, rtnl_held, extack) <= 0) { 207762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to build del event notification"); 207862306a36Sopenharmony_ci kfree_skb(skb); 207962306a36Sopenharmony_ci return -EINVAL; 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci err = tp->ops->delete(tp, fh, last, rtnl_held, extack); 208362306a36Sopenharmony_ci if (err) { 208462306a36Sopenharmony_ci kfree_skb(skb); 208562306a36Sopenharmony_ci return err; 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci if (unicast) 208962306a36Sopenharmony_ci err = rtnl_unicast(skb, net, portid); 209062306a36Sopenharmony_ci else 209162306a36Sopenharmony_ci err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 209262306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 209362306a36Sopenharmony_ci if (err < 0) 209462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to send filter delete notification"); 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci return err; 209762306a36Sopenharmony_ci} 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_cistatic void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, 210062306a36Sopenharmony_ci struct tcf_block *block, struct Qdisc *q, 210162306a36Sopenharmony_ci u32 parent, struct nlmsghdr *n, 210262306a36Sopenharmony_ci struct tcf_chain *chain, int event, 210362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci struct tcf_proto *tp; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci for (tp = tcf_get_next_proto(chain, NULL); 210862306a36Sopenharmony_ci tp; tp = tcf_get_next_proto(chain, tp)) 210962306a36Sopenharmony_ci tfilter_notify(net, oskb, n, tp, block, q, parent, NULL, 211062306a36Sopenharmony_ci event, false, true, extack); 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic void tfilter_put(struct tcf_proto *tp, void *fh) 211462306a36Sopenharmony_ci{ 211562306a36Sopenharmony_ci if (tp->ops->put && fh) 211662306a36Sopenharmony_ci tp->ops->put(tp, fh); 211762306a36Sopenharmony_ci} 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_cistatic bool is_qdisc_ingress(__u32 classid) 212062306a36Sopenharmony_ci{ 212162306a36Sopenharmony_ci return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS)); 212262306a36Sopenharmony_ci} 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_cistatic int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 212562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 212862306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 212962306a36Sopenharmony_ci char name[IFNAMSIZ]; 213062306a36Sopenharmony_ci struct tcmsg *t; 213162306a36Sopenharmony_ci u32 protocol; 213262306a36Sopenharmony_ci u32 prio; 213362306a36Sopenharmony_ci bool prio_allocate; 213462306a36Sopenharmony_ci u32 parent; 213562306a36Sopenharmony_ci u32 chain_index; 213662306a36Sopenharmony_ci struct Qdisc *q; 213762306a36Sopenharmony_ci struct tcf_chain_info chain_info; 213862306a36Sopenharmony_ci struct tcf_chain *chain; 213962306a36Sopenharmony_ci struct tcf_block *block; 214062306a36Sopenharmony_ci struct tcf_proto *tp; 214162306a36Sopenharmony_ci unsigned long cl; 214262306a36Sopenharmony_ci void *fh; 214362306a36Sopenharmony_ci int err; 214462306a36Sopenharmony_ci int tp_created; 214562306a36Sopenharmony_ci bool rtnl_held = false; 214662306a36Sopenharmony_ci u32 flags; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_cireplay: 214962306a36Sopenharmony_ci tp_created = 0; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 215262306a36Sopenharmony_ci rtm_tca_policy, extack); 215362306a36Sopenharmony_ci if (err < 0) 215462306a36Sopenharmony_ci return err; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci t = nlmsg_data(n); 215762306a36Sopenharmony_ci protocol = TC_H_MIN(t->tcm_info); 215862306a36Sopenharmony_ci prio = TC_H_MAJ(t->tcm_info); 215962306a36Sopenharmony_ci prio_allocate = false; 216062306a36Sopenharmony_ci parent = t->tcm_parent; 216162306a36Sopenharmony_ci tp = NULL; 216262306a36Sopenharmony_ci cl = 0; 216362306a36Sopenharmony_ci block = NULL; 216462306a36Sopenharmony_ci q = NULL; 216562306a36Sopenharmony_ci chain = NULL; 216662306a36Sopenharmony_ci flags = 0; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci if (prio == 0) { 216962306a36Sopenharmony_ci /* If no priority is provided by the user, 217062306a36Sopenharmony_ci * we allocate one. 217162306a36Sopenharmony_ci */ 217262306a36Sopenharmony_ci if (n->nlmsg_flags & NLM_F_CREATE) { 217362306a36Sopenharmony_ci prio = TC_H_MAKE(0x80000000U, 0U); 217462306a36Sopenharmony_ci prio_allocate = true; 217562306a36Sopenharmony_ci } else { 217662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 217762306a36Sopenharmony_ci return -ENOENT; 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci /* Find head of filter chain. */ 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 218462306a36Sopenharmony_ci if (err) 218562306a36Sopenharmony_ci return err; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 218862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 218962306a36Sopenharmony_ci err = -EINVAL; 219062306a36Sopenharmony_ci goto errout; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci /* Take rtnl mutex if rtnl_held was set to true on previous iteration, 219462306a36Sopenharmony_ci * block is shared (no qdisc found), qdisc is not unlocked, classifier 219562306a36Sopenharmony_ci * type is not specified, classifier is not unlocked. 219662306a36Sopenharmony_ci */ 219762306a36Sopenharmony_ci if (rtnl_held || 219862306a36Sopenharmony_ci (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 219962306a36Sopenharmony_ci !tcf_proto_is_unlocked(name)) { 220062306a36Sopenharmony_ci rtnl_held = true; 220162306a36Sopenharmony_ci rtnl_lock(); 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 220562306a36Sopenharmony_ci if (err) 220662306a36Sopenharmony_ci goto errout; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 220962306a36Sopenharmony_ci extack); 221062306a36Sopenharmony_ci if (IS_ERR(block)) { 221162306a36Sopenharmony_ci err = PTR_ERR(block); 221262306a36Sopenharmony_ci goto errout; 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci block->classid = parent; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 221762306a36Sopenharmony_ci if (chain_index > TC_ACT_EXT_VAL_MASK) { 221862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 221962306a36Sopenharmony_ci err = -EINVAL; 222062306a36Sopenharmony_ci goto errout; 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci chain = tcf_chain_get(block, chain_index, true); 222362306a36Sopenharmony_ci if (!chain) { 222462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot create specified filter chain"); 222562306a36Sopenharmony_ci err = -ENOMEM; 222662306a36Sopenharmony_ci goto errout; 222762306a36Sopenharmony_ci } 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 223062306a36Sopenharmony_ci tp = tcf_chain_tp_find(chain, &chain_info, protocol, 223162306a36Sopenharmony_ci prio, prio_allocate); 223262306a36Sopenharmony_ci if (IS_ERR(tp)) { 223362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 223462306a36Sopenharmony_ci err = PTR_ERR(tp); 223562306a36Sopenharmony_ci goto errout_locked; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (tp == NULL) { 223962306a36Sopenharmony_ci struct tcf_proto *tp_new = NULL; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci if (chain->flushing) { 224262306a36Sopenharmony_ci err = -EAGAIN; 224362306a36Sopenharmony_ci goto errout_locked; 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci /* Proto-tcf does not exist, create new one */ 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci if (tca[TCA_KIND] == NULL || !protocol) { 224962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified"); 225062306a36Sopenharmony_ci err = -EINVAL; 225162306a36Sopenharmony_ci goto errout_locked; 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci if (!(n->nlmsg_flags & NLM_F_CREATE)) { 225562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 225662306a36Sopenharmony_ci err = -ENOENT; 225762306a36Sopenharmony_ci goto errout_locked; 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci if (prio_allocate) 226162306a36Sopenharmony_ci prio = tcf_auto_prio(tcf_chain_tp_prev(chain, 226262306a36Sopenharmony_ci &chain_info)); 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 226562306a36Sopenharmony_ci tp_new = tcf_proto_create(name, protocol, prio, chain, 226662306a36Sopenharmony_ci rtnl_held, extack); 226762306a36Sopenharmony_ci if (IS_ERR(tp_new)) { 226862306a36Sopenharmony_ci err = PTR_ERR(tp_new); 226962306a36Sopenharmony_ci goto errout_tp; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci tp_created = 1; 227362306a36Sopenharmony_ci tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio, 227462306a36Sopenharmony_ci rtnl_held); 227562306a36Sopenharmony_ci if (IS_ERR(tp)) { 227662306a36Sopenharmony_ci err = PTR_ERR(tp); 227762306a36Sopenharmony_ci goto errout_tp; 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci } else { 228062306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 228162306a36Sopenharmony_ci } 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 228462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 228562306a36Sopenharmony_ci err = -EINVAL; 228662306a36Sopenharmony_ci goto errout; 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci fh = tp->ops->get(tp, t->tcm_handle); 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci if (!fh) { 229262306a36Sopenharmony_ci if (!(n->nlmsg_flags & NLM_F_CREATE)) { 229362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter"); 229462306a36Sopenharmony_ci err = -ENOENT; 229562306a36Sopenharmony_ci goto errout; 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci } else if (n->nlmsg_flags & NLM_F_EXCL) { 229862306a36Sopenharmony_ci tfilter_put(tp, fh); 229962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter already exists"); 230062306a36Sopenharmony_ci err = -EEXIST; 230162306a36Sopenharmony_ci goto errout; 230262306a36Sopenharmony_ci } 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) { 230562306a36Sopenharmony_ci tfilter_put(tp, fh); 230662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind"); 230762306a36Sopenharmony_ci err = -EINVAL; 230862306a36Sopenharmony_ci goto errout; 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci if (!(n->nlmsg_flags & NLM_F_CREATE)) 231262306a36Sopenharmony_ci flags |= TCA_ACT_FLAGS_REPLACE; 231362306a36Sopenharmony_ci if (!rtnl_held) 231462306a36Sopenharmony_ci flags |= TCA_ACT_FLAGS_NO_RTNL; 231562306a36Sopenharmony_ci if (is_qdisc_ingress(parent)) 231662306a36Sopenharmony_ci flags |= TCA_ACT_FLAGS_AT_INGRESS; 231762306a36Sopenharmony_ci err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, 231862306a36Sopenharmony_ci flags, extack); 231962306a36Sopenharmony_ci if (err == 0) { 232062306a36Sopenharmony_ci tfilter_notify(net, skb, n, tp, block, q, parent, fh, 232162306a36Sopenharmony_ci RTM_NEWTFILTER, false, rtnl_held, extack); 232262306a36Sopenharmony_ci tfilter_put(tp, fh); 232362306a36Sopenharmony_ci /* q pointer is NULL for shared blocks */ 232462306a36Sopenharmony_ci if (q) 232562306a36Sopenharmony_ci q->flags &= ~TCQ_F_CAN_BYPASS; 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_cierrout: 232962306a36Sopenharmony_ci if (err && tp_created) 233062306a36Sopenharmony_ci tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL); 233162306a36Sopenharmony_cierrout_tp: 233262306a36Sopenharmony_ci if (chain) { 233362306a36Sopenharmony_ci if (tp && !IS_ERR(tp)) 233462306a36Sopenharmony_ci tcf_proto_put(tp, rtnl_held, NULL); 233562306a36Sopenharmony_ci if (!tp_created) 233662306a36Sopenharmony_ci tcf_chain_put(chain); 233762306a36Sopenharmony_ci } 233862306a36Sopenharmony_ci tcf_block_release(q, block, rtnl_held); 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci if (rtnl_held) 234162306a36Sopenharmony_ci rtnl_unlock(); 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci if (err == -EAGAIN) { 234462306a36Sopenharmony_ci /* Take rtnl lock in case EAGAIN is caused by concurrent flush 234562306a36Sopenharmony_ci * of target chain. 234662306a36Sopenharmony_ci */ 234762306a36Sopenharmony_ci rtnl_held = true; 234862306a36Sopenharmony_ci /* Replay the request. */ 234962306a36Sopenharmony_ci goto replay; 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci return err; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_cierrout_locked: 235462306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 235562306a36Sopenharmony_ci goto errout; 235662306a36Sopenharmony_ci} 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_cistatic int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 235962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 236062306a36Sopenharmony_ci{ 236162306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 236262306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 236362306a36Sopenharmony_ci char name[IFNAMSIZ]; 236462306a36Sopenharmony_ci struct tcmsg *t; 236562306a36Sopenharmony_ci u32 protocol; 236662306a36Sopenharmony_ci u32 prio; 236762306a36Sopenharmony_ci u32 parent; 236862306a36Sopenharmony_ci u32 chain_index; 236962306a36Sopenharmony_ci struct Qdisc *q = NULL; 237062306a36Sopenharmony_ci struct tcf_chain_info chain_info; 237162306a36Sopenharmony_ci struct tcf_chain *chain = NULL; 237262306a36Sopenharmony_ci struct tcf_block *block = NULL; 237362306a36Sopenharmony_ci struct tcf_proto *tp = NULL; 237462306a36Sopenharmony_ci unsigned long cl = 0; 237562306a36Sopenharmony_ci void *fh = NULL; 237662306a36Sopenharmony_ci int err; 237762306a36Sopenharmony_ci bool rtnl_held = false; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 238062306a36Sopenharmony_ci rtm_tca_policy, extack); 238162306a36Sopenharmony_ci if (err < 0) 238262306a36Sopenharmony_ci return err; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci t = nlmsg_data(n); 238562306a36Sopenharmony_ci protocol = TC_H_MIN(t->tcm_info); 238662306a36Sopenharmony_ci prio = TC_H_MAJ(t->tcm_info); 238762306a36Sopenharmony_ci parent = t->tcm_parent; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) { 239062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set"); 239162306a36Sopenharmony_ci return -ENOENT; 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci /* Find head of filter chain. */ 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 239762306a36Sopenharmony_ci if (err) 239862306a36Sopenharmony_ci return err; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 240162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 240262306a36Sopenharmony_ci err = -EINVAL; 240362306a36Sopenharmony_ci goto errout; 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc 240662306a36Sopenharmony_ci * found), qdisc is not unlocked, classifier type is not specified, 240762306a36Sopenharmony_ci * classifier is not unlocked. 240862306a36Sopenharmony_ci */ 240962306a36Sopenharmony_ci if (!prio || 241062306a36Sopenharmony_ci (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 241162306a36Sopenharmony_ci !tcf_proto_is_unlocked(name)) { 241262306a36Sopenharmony_ci rtnl_held = true; 241362306a36Sopenharmony_ci rtnl_lock(); 241462306a36Sopenharmony_ci } 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 241762306a36Sopenharmony_ci if (err) 241862306a36Sopenharmony_ci goto errout; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 242162306a36Sopenharmony_ci extack); 242262306a36Sopenharmony_ci if (IS_ERR(block)) { 242362306a36Sopenharmony_ci err = PTR_ERR(block); 242462306a36Sopenharmony_ci goto errout; 242562306a36Sopenharmony_ci } 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 242862306a36Sopenharmony_ci if (chain_index > TC_ACT_EXT_VAL_MASK) { 242962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 243062306a36Sopenharmony_ci err = -EINVAL; 243162306a36Sopenharmony_ci goto errout; 243262306a36Sopenharmony_ci } 243362306a36Sopenharmony_ci chain = tcf_chain_get(block, chain_index, false); 243462306a36Sopenharmony_ci if (!chain) { 243562306a36Sopenharmony_ci /* User requested flush on non-existent chain. Nothing to do, 243662306a36Sopenharmony_ci * so just return success. 243762306a36Sopenharmony_ci */ 243862306a36Sopenharmony_ci if (prio == 0) { 243962306a36Sopenharmony_ci err = 0; 244062306a36Sopenharmony_ci goto errout; 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 244362306a36Sopenharmony_ci err = -ENOENT; 244462306a36Sopenharmony_ci goto errout; 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci if (prio == 0) { 244862306a36Sopenharmony_ci tfilter_notify_chain(net, skb, block, q, parent, n, 244962306a36Sopenharmony_ci chain, RTM_DELTFILTER, extack); 245062306a36Sopenharmony_ci tcf_chain_flush(chain, rtnl_held); 245162306a36Sopenharmony_ci err = 0; 245262306a36Sopenharmony_ci goto errout; 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 245662306a36Sopenharmony_ci tp = tcf_chain_tp_find(chain, &chain_info, protocol, 245762306a36Sopenharmony_ci prio, false); 245862306a36Sopenharmony_ci if (!tp || IS_ERR(tp)) { 245962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 246062306a36Sopenharmony_ci err = tp ? PTR_ERR(tp) : -ENOENT; 246162306a36Sopenharmony_ci goto errout_locked; 246262306a36Sopenharmony_ci } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 246362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 246462306a36Sopenharmony_ci err = -EINVAL; 246562306a36Sopenharmony_ci goto errout_locked; 246662306a36Sopenharmony_ci } else if (t->tcm_handle == 0) { 246762306a36Sopenharmony_ci tcf_proto_signal_destroying(chain, tp); 246862306a36Sopenharmony_ci tcf_chain_tp_remove(chain, &chain_info, tp); 246962306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci tcf_proto_put(tp, rtnl_held, NULL); 247262306a36Sopenharmony_ci tfilter_notify(net, skb, n, tp, block, q, parent, fh, 247362306a36Sopenharmony_ci RTM_DELTFILTER, false, rtnl_held, extack); 247462306a36Sopenharmony_ci err = 0; 247562306a36Sopenharmony_ci goto errout; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci fh = tp->ops->get(tp, t->tcm_handle); 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci if (!fh) { 248262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 248362306a36Sopenharmony_ci err = -ENOENT; 248462306a36Sopenharmony_ci } else { 248562306a36Sopenharmony_ci bool last; 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci err = tfilter_del_notify(net, skb, n, tp, block, 248862306a36Sopenharmony_ci q, parent, fh, false, &last, 248962306a36Sopenharmony_ci rtnl_held, extack); 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci if (err) 249262306a36Sopenharmony_ci goto errout; 249362306a36Sopenharmony_ci if (last) 249462306a36Sopenharmony_ci tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack); 249562306a36Sopenharmony_ci } 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_cierrout: 249862306a36Sopenharmony_ci if (chain) { 249962306a36Sopenharmony_ci if (tp && !IS_ERR(tp)) 250062306a36Sopenharmony_ci tcf_proto_put(tp, rtnl_held, NULL); 250162306a36Sopenharmony_ci tcf_chain_put(chain); 250262306a36Sopenharmony_ci } 250362306a36Sopenharmony_ci tcf_block_release(q, block, rtnl_held); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci if (rtnl_held) 250662306a36Sopenharmony_ci rtnl_unlock(); 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci return err; 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_cierrout_locked: 251162306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 251262306a36Sopenharmony_ci goto errout; 251362306a36Sopenharmony_ci} 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_cistatic int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, 251662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 251762306a36Sopenharmony_ci{ 251862306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 251962306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 252062306a36Sopenharmony_ci char name[IFNAMSIZ]; 252162306a36Sopenharmony_ci struct tcmsg *t; 252262306a36Sopenharmony_ci u32 protocol; 252362306a36Sopenharmony_ci u32 prio; 252462306a36Sopenharmony_ci u32 parent; 252562306a36Sopenharmony_ci u32 chain_index; 252662306a36Sopenharmony_ci struct Qdisc *q = NULL; 252762306a36Sopenharmony_ci struct tcf_chain_info chain_info; 252862306a36Sopenharmony_ci struct tcf_chain *chain = NULL; 252962306a36Sopenharmony_ci struct tcf_block *block = NULL; 253062306a36Sopenharmony_ci struct tcf_proto *tp = NULL; 253162306a36Sopenharmony_ci unsigned long cl = 0; 253262306a36Sopenharmony_ci void *fh = NULL; 253362306a36Sopenharmony_ci int err; 253462306a36Sopenharmony_ci bool rtnl_held = false; 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 253762306a36Sopenharmony_ci rtm_tca_policy, extack); 253862306a36Sopenharmony_ci if (err < 0) 253962306a36Sopenharmony_ci return err; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci t = nlmsg_data(n); 254262306a36Sopenharmony_ci protocol = TC_H_MIN(t->tcm_info); 254362306a36Sopenharmony_ci prio = TC_H_MAJ(t->tcm_info); 254462306a36Sopenharmony_ci parent = t->tcm_parent; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci if (prio == 0) { 254762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero"); 254862306a36Sopenharmony_ci return -ENOENT; 254962306a36Sopenharmony_ci } 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci /* Find head of filter chain. */ 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack); 255462306a36Sopenharmony_ci if (err) 255562306a36Sopenharmony_ci return err; 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 255862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified TC filter name too long"); 255962306a36Sopenharmony_ci err = -EINVAL; 256062306a36Sopenharmony_ci goto errout; 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not 256362306a36Sopenharmony_ci * unlocked, classifier type is not specified, classifier is not 256462306a36Sopenharmony_ci * unlocked. 256562306a36Sopenharmony_ci */ 256662306a36Sopenharmony_ci if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) || 256762306a36Sopenharmony_ci !tcf_proto_is_unlocked(name)) { 256862306a36Sopenharmony_ci rtnl_held = true; 256962306a36Sopenharmony_ci rtnl_lock(); 257062306a36Sopenharmony_ci } 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack); 257362306a36Sopenharmony_ci if (err) 257462306a36Sopenharmony_ci goto errout; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index, 257762306a36Sopenharmony_ci extack); 257862306a36Sopenharmony_ci if (IS_ERR(block)) { 257962306a36Sopenharmony_ci err = PTR_ERR(block); 258062306a36Sopenharmony_ci goto errout; 258162306a36Sopenharmony_ci } 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 258462306a36Sopenharmony_ci if (chain_index > TC_ACT_EXT_VAL_MASK) { 258562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 258662306a36Sopenharmony_ci err = -EINVAL; 258762306a36Sopenharmony_ci goto errout; 258862306a36Sopenharmony_ci } 258962306a36Sopenharmony_ci chain = tcf_chain_get(block, chain_index, false); 259062306a36Sopenharmony_ci if (!chain) { 259162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 259262306a36Sopenharmony_ci err = -EINVAL; 259362306a36Sopenharmony_ci goto errout; 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci mutex_lock(&chain->filter_chain_lock); 259762306a36Sopenharmony_ci tp = tcf_chain_tp_find(chain, &chain_info, protocol, 259862306a36Sopenharmony_ci prio, false); 259962306a36Sopenharmony_ci mutex_unlock(&chain->filter_chain_lock); 260062306a36Sopenharmony_ci if (!tp || IS_ERR(tp)) { 260162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found"); 260262306a36Sopenharmony_ci err = tp ? PTR_ERR(tp) : -ENOENT; 260362306a36Sopenharmony_ci goto errout; 260462306a36Sopenharmony_ci } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) { 260562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one"); 260662306a36Sopenharmony_ci err = -EINVAL; 260762306a36Sopenharmony_ci goto errout; 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci fh = tp->ops->get(tp, t->tcm_handle); 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci if (!fh) { 261362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified filter handle not found"); 261462306a36Sopenharmony_ci err = -ENOENT; 261562306a36Sopenharmony_ci } else { 261662306a36Sopenharmony_ci err = tfilter_notify(net, skb, n, tp, block, q, parent, 261762306a36Sopenharmony_ci fh, RTM_NEWTFILTER, true, rtnl_held, NULL); 261862306a36Sopenharmony_ci if (err < 0) 261962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); 262062306a36Sopenharmony_ci } 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci tfilter_put(tp, fh); 262362306a36Sopenharmony_cierrout: 262462306a36Sopenharmony_ci if (chain) { 262562306a36Sopenharmony_ci if (tp && !IS_ERR(tp)) 262662306a36Sopenharmony_ci tcf_proto_put(tp, rtnl_held, NULL); 262762306a36Sopenharmony_ci tcf_chain_put(chain); 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci tcf_block_release(q, block, rtnl_held); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci if (rtnl_held) 263262306a36Sopenharmony_ci rtnl_unlock(); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci return err; 263562306a36Sopenharmony_ci} 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_cistruct tcf_dump_args { 263862306a36Sopenharmony_ci struct tcf_walker w; 263962306a36Sopenharmony_ci struct sk_buff *skb; 264062306a36Sopenharmony_ci struct netlink_callback *cb; 264162306a36Sopenharmony_ci struct tcf_block *block; 264262306a36Sopenharmony_ci struct Qdisc *q; 264362306a36Sopenharmony_ci u32 parent; 264462306a36Sopenharmony_ci bool terse_dump; 264562306a36Sopenharmony_ci}; 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_cistatic int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 264862306a36Sopenharmony_ci{ 264962306a36Sopenharmony_ci struct tcf_dump_args *a = (void *)arg; 265062306a36Sopenharmony_ci struct net *net = sock_net(a->skb->sk); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, 265362306a36Sopenharmony_ci n, NETLINK_CB(a->cb->skb).portid, 265462306a36Sopenharmony_ci a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 265562306a36Sopenharmony_ci RTM_NEWTFILTER, a->terse_dump, true, NULL); 265662306a36Sopenharmony_ci} 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_cistatic bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, 265962306a36Sopenharmony_ci struct sk_buff *skb, struct netlink_callback *cb, 266062306a36Sopenharmony_ci long index_start, long *p_index, bool terse) 266162306a36Sopenharmony_ci{ 266262306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 266362306a36Sopenharmony_ci struct tcf_block *block = chain->block; 266462306a36Sopenharmony_ci struct tcmsg *tcm = nlmsg_data(cb->nlh); 266562306a36Sopenharmony_ci struct tcf_proto *tp, *tp_prev; 266662306a36Sopenharmony_ci struct tcf_dump_args arg; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci for (tp = __tcf_get_next_proto(chain, NULL); 266962306a36Sopenharmony_ci tp; 267062306a36Sopenharmony_ci tp_prev = tp, 267162306a36Sopenharmony_ci tp = __tcf_get_next_proto(chain, tp), 267262306a36Sopenharmony_ci tcf_proto_put(tp_prev, true, NULL), 267362306a36Sopenharmony_ci (*p_index)++) { 267462306a36Sopenharmony_ci if (*p_index < index_start) 267562306a36Sopenharmony_ci continue; 267662306a36Sopenharmony_ci if (TC_H_MAJ(tcm->tcm_info) && 267762306a36Sopenharmony_ci TC_H_MAJ(tcm->tcm_info) != tp->prio) 267862306a36Sopenharmony_ci continue; 267962306a36Sopenharmony_ci if (TC_H_MIN(tcm->tcm_info) && 268062306a36Sopenharmony_ci TC_H_MIN(tcm->tcm_info) != tp->protocol) 268162306a36Sopenharmony_ci continue; 268262306a36Sopenharmony_ci if (*p_index > index_start) 268362306a36Sopenharmony_ci memset(&cb->args[1], 0, 268462306a36Sopenharmony_ci sizeof(cb->args) - sizeof(cb->args[0])); 268562306a36Sopenharmony_ci if (cb->args[1] == 0) { 268662306a36Sopenharmony_ci if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, 268762306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 268862306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 268962306a36Sopenharmony_ci RTM_NEWTFILTER, false, true, NULL) <= 0) 269062306a36Sopenharmony_ci goto errout; 269162306a36Sopenharmony_ci cb->args[1] = 1; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci if (!tp->ops->walk) 269462306a36Sopenharmony_ci continue; 269562306a36Sopenharmony_ci arg.w.fn = tcf_node_dump; 269662306a36Sopenharmony_ci arg.skb = skb; 269762306a36Sopenharmony_ci arg.cb = cb; 269862306a36Sopenharmony_ci arg.block = block; 269962306a36Sopenharmony_ci arg.q = q; 270062306a36Sopenharmony_ci arg.parent = parent; 270162306a36Sopenharmony_ci arg.w.stop = 0; 270262306a36Sopenharmony_ci arg.w.skip = cb->args[1] - 1; 270362306a36Sopenharmony_ci arg.w.count = 0; 270462306a36Sopenharmony_ci arg.w.cookie = cb->args[2]; 270562306a36Sopenharmony_ci arg.terse_dump = terse; 270662306a36Sopenharmony_ci tp->ops->walk(tp, &arg.w, true); 270762306a36Sopenharmony_ci cb->args[2] = arg.w.cookie; 270862306a36Sopenharmony_ci cb->args[1] = arg.w.count + 1; 270962306a36Sopenharmony_ci if (arg.w.stop) 271062306a36Sopenharmony_ci goto errout; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci return true; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_cierrout: 271562306a36Sopenharmony_ci tcf_proto_put(tp, true, NULL); 271662306a36Sopenharmony_ci return false; 271762306a36Sopenharmony_ci} 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_cistatic const struct nla_policy tcf_tfilter_dump_policy[TCA_MAX + 1] = { 272062306a36Sopenharmony_ci [TCA_DUMP_FLAGS] = NLA_POLICY_BITFIELD32(TCA_DUMP_FLAGS_TERSE), 272162306a36Sopenharmony_ci}; 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci/* called with RTNL */ 272462306a36Sopenharmony_cistatic int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) 272562306a36Sopenharmony_ci{ 272662306a36Sopenharmony_ci struct tcf_chain *chain, *chain_prev; 272762306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 272862306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 272962306a36Sopenharmony_ci struct Qdisc *q = NULL; 273062306a36Sopenharmony_ci struct tcf_block *block; 273162306a36Sopenharmony_ci struct tcmsg *tcm = nlmsg_data(cb->nlh); 273262306a36Sopenharmony_ci bool terse_dump = false; 273362306a36Sopenharmony_ci long index_start; 273462306a36Sopenharmony_ci long index; 273562306a36Sopenharmony_ci u32 parent; 273662306a36Sopenharmony_ci int err; 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 273962306a36Sopenharmony_ci return skb->len; 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 274262306a36Sopenharmony_ci tcf_tfilter_dump_policy, cb->extack); 274362306a36Sopenharmony_ci if (err) 274462306a36Sopenharmony_ci return err; 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci if (tca[TCA_DUMP_FLAGS]) { 274762306a36Sopenharmony_ci struct nla_bitfield32 flags = 274862306a36Sopenharmony_ci nla_get_bitfield32(tca[TCA_DUMP_FLAGS]); 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci terse_dump = flags.value & TCA_DUMP_FLAGS_TERSE; 275162306a36Sopenharmony_ci } 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 275462306a36Sopenharmony_ci block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 275562306a36Sopenharmony_ci if (!block) 275662306a36Sopenharmony_ci goto out; 275762306a36Sopenharmony_ci /* If we work with block index, q is NULL and parent value 275862306a36Sopenharmony_ci * will never be used in the following code. The check 275962306a36Sopenharmony_ci * in tcf_fill_node prevents it. However, compiler does not 276062306a36Sopenharmony_ci * see that far, so set parent to zero to silence the warning 276162306a36Sopenharmony_ci * about parent being uninitialized. 276262306a36Sopenharmony_ci */ 276362306a36Sopenharmony_ci parent = 0; 276462306a36Sopenharmony_ci } else { 276562306a36Sopenharmony_ci const struct Qdisc_class_ops *cops; 276662306a36Sopenharmony_ci struct net_device *dev; 276762306a36Sopenharmony_ci unsigned long cl = 0; 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci dev = __dev_get_by_index(net, tcm->tcm_ifindex); 277062306a36Sopenharmony_ci if (!dev) 277162306a36Sopenharmony_ci return skb->len; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci parent = tcm->tcm_parent; 277462306a36Sopenharmony_ci if (!parent) 277562306a36Sopenharmony_ci q = rtnl_dereference(dev->qdisc); 277662306a36Sopenharmony_ci else 277762306a36Sopenharmony_ci q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 277862306a36Sopenharmony_ci if (!q) 277962306a36Sopenharmony_ci goto out; 278062306a36Sopenharmony_ci cops = q->ops->cl_ops; 278162306a36Sopenharmony_ci if (!cops) 278262306a36Sopenharmony_ci goto out; 278362306a36Sopenharmony_ci if (!cops->tcf_block) 278462306a36Sopenharmony_ci goto out; 278562306a36Sopenharmony_ci if (TC_H_MIN(tcm->tcm_parent)) { 278662306a36Sopenharmony_ci cl = cops->find(q, tcm->tcm_parent); 278762306a36Sopenharmony_ci if (cl == 0) 278862306a36Sopenharmony_ci goto out; 278962306a36Sopenharmony_ci } 279062306a36Sopenharmony_ci block = cops->tcf_block(q, cl, NULL); 279162306a36Sopenharmony_ci if (!block) 279262306a36Sopenharmony_ci goto out; 279362306a36Sopenharmony_ci parent = block->classid; 279462306a36Sopenharmony_ci if (tcf_block_shared(block)) 279562306a36Sopenharmony_ci q = NULL; 279662306a36Sopenharmony_ci } 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci index_start = cb->args[0]; 279962306a36Sopenharmony_ci index = 0; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci for (chain = __tcf_get_next_chain(block, NULL); 280262306a36Sopenharmony_ci chain; 280362306a36Sopenharmony_ci chain_prev = chain, 280462306a36Sopenharmony_ci chain = __tcf_get_next_chain(block, chain), 280562306a36Sopenharmony_ci tcf_chain_put(chain_prev)) { 280662306a36Sopenharmony_ci if (tca[TCA_CHAIN] && 280762306a36Sopenharmony_ci nla_get_u32(tca[TCA_CHAIN]) != chain->index) 280862306a36Sopenharmony_ci continue; 280962306a36Sopenharmony_ci if (!tcf_chain_dump(chain, q, parent, skb, cb, 281062306a36Sopenharmony_ci index_start, &index, terse_dump)) { 281162306a36Sopenharmony_ci tcf_chain_put(chain); 281262306a36Sopenharmony_ci err = -EMSGSIZE; 281362306a36Sopenharmony_ci break; 281462306a36Sopenharmony_ci } 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 281862306a36Sopenharmony_ci tcf_block_refcnt_put(block, true); 281962306a36Sopenharmony_ci cb->args[0] = index; 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ciout: 282262306a36Sopenharmony_ci /* If we did no progress, the error (EMSGSIZE) is real */ 282362306a36Sopenharmony_ci if (skb->len == 0 && err) 282462306a36Sopenharmony_ci return err; 282562306a36Sopenharmony_ci return skb->len; 282662306a36Sopenharmony_ci} 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_cistatic int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, 282962306a36Sopenharmony_ci void *tmplt_priv, u32 chain_index, 283062306a36Sopenharmony_ci struct net *net, struct sk_buff *skb, 283162306a36Sopenharmony_ci struct tcf_block *block, 283262306a36Sopenharmony_ci u32 portid, u32 seq, u16 flags, int event, 283362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 283462306a36Sopenharmony_ci{ 283562306a36Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 283662306a36Sopenharmony_ci const struct tcf_proto_ops *ops; 283762306a36Sopenharmony_ci struct nlmsghdr *nlh; 283862306a36Sopenharmony_ci struct tcmsg *tcm; 283962306a36Sopenharmony_ci void *priv; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci ops = tmplt_ops; 284262306a36Sopenharmony_ci priv = tmplt_priv; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 284562306a36Sopenharmony_ci if (!nlh) 284662306a36Sopenharmony_ci goto out_nlmsg_trim; 284762306a36Sopenharmony_ci tcm = nlmsg_data(nlh); 284862306a36Sopenharmony_ci tcm->tcm_family = AF_UNSPEC; 284962306a36Sopenharmony_ci tcm->tcm__pad1 = 0; 285062306a36Sopenharmony_ci tcm->tcm__pad2 = 0; 285162306a36Sopenharmony_ci tcm->tcm_handle = 0; 285262306a36Sopenharmony_ci if (block->q) { 285362306a36Sopenharmony_ci tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; 285462306a36Sopenharmony_ci tcm->tcm_parent = block->q->handle; 285562306a36Sopenharmony_ci } else { 285662306a36Sopenharmony_ci tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK; 285762306a36Sopenharmony_ci tcm->tcm_block_index = block->index; 285862306a36Sopenharmony_ci } 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_CHAIN, chain_index)) 286162306a36Sopenharmony_ci goto nla_put_failure; 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci if (ops) { 286462306a36Sopenharmony_ci if (nla_put_string(skb, TCA_KIND, ops->kind)) 286562306a36Sopenharmony_ci goto nla_put_failure; 286662306a36Sopenharmony_ci if (ops->tmplt_dump(skb, net, priv) < 0) 286762306a36Sopenharmony_ci goto nla_put_failure; 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci if (extack && extack->_msg && 287162306a36Sopenharmony_ci nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) 287262306a36Sopenharmony_ci goto out_nlmsg_trim; 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci return skb->len; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ciout_nlmsg_trim: 287962306a36Sopenharmony_cinla_put_failure: 288062306a36Sopenharmony_ci nlmsg_trim(skb, b); 288162306a36Sopenharmony_ci return -EMSGSIZE; 288262306a36Sopenharmony_ci} 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_cistatic int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, 288562306a36Sopenharmony_ci u32 seq, u16 flags, int event, bool unicast, 288662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 288762306a36Sopenharmony_ci{ 288862306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 288962306a36Sopenharmony_ci struct tcf_block *block = chain->block; 289062306a36Sopenharmony_ci struct net *net = block->net; 289162306a36Sopenharmony_ci struct sk_buff *skb; 289262306a36Sopenharmony_ci int err = 0; 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 289562306a36Sopenharmony_ci if (!skb) 289662306a36Sopenharmony_ci return -ENOBUFS; 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 289962306a36Sopenharmony_ci chain->index, net, skb, block, portid, 290062306a36Sopenharmony_ci seq, flags, event, extack) <= 0) { 290162306a36Sopenharmony_ci kfree_skb(skb); 290262306a36Sopenharmony_ci return -EINVAL; 290362306a36Sopenharmony_ci } 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci if (unicast) 290662306a36Sopenharmony_ci err = rtnl_unicast(skb, net, portid); 290762306a36Sopenharmony_ci else 290862306a36Sopenharmony_ci err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 290962306a36Sopenharmony_ci flags & NLM_F_ECHO); 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci return err; 291262306a36Sopenharmony_ci} 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_cistatic int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, 291562306a36Sopenharmony_ci void *tmplt_priv, u32 chain_index, 291662306a36Sopenharmony_ci struct tcf_block *block, struct sk_buff *oskb, 291762306a36Sopenharmony_ci u32 seq, u16 flags, bool unicast) 291862306a36Sopenharmony_ci{ 291962306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 292062306a36Sopenharmony_ci struct net *net = block->net; 292162306a36Sopenharmony_ci struct sk_buff *skb; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 292462306a36Sopenharmony_ci if (!skb) 292562306a36Sopenharmony_ci return -ENOBUFS; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb, 292862306a36Sopenharmony_ci block, portid, seq, flags, RTM_DELCHAIN, NULL) <= 0) { 292962306a36Sopenharmony_ci kfree_skb(skb); 293062306a36Sopenharmony_ci return -EINVAL; 293162306a36Sopenharmony_ci } 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci if (unicast) 293462306a36Sopenharmony_ci return rtnl_unicast(skb, net, portid); 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO); 293762306a36Sopenharmony_ci} 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_cistatic int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net, 294062306a36Sopenharmony_ci struct nlattr **tca, 294162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 294262306a36Sopenharmony_ci{ 294362306a36Sopenharmony_ci const struct tcf_proto_ops *ops; 294462306a36Sopenharmony_ci char name[IFNAMSIZ]; 294562306a36Sopenharmony_ci void *tmplt_priv; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci /* If kind is not set, user did not specify template. */ 294862306a36Sopenharmony_ci if (!tca[TCA_KIND]) 294962306a36Sopenharmony_ci return 0; 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci if (tcf_proto_check_kind(tca[TCA_KIND], name)) { 295262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified TC chain template name too long"); 295362306a36Sopenharmony_ci return -EINVAL; 295462306a36Sopenharmony_ci } 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci ops = tcf_proto_lookup_ops(name, true, extack); 295762306a36Sopenharmony_ci if (IS_ERR(ops)) 295862306a36Sopenharmony_ci return PTR_ERR(ops); 295962306a36Sopenharmony_ci if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump || 296062306a36Sopenharmony_ci !ops->tmplt_reoffload) { 296162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier"); 296262306a36Sopenharmony_ci module_put(ops->owner); 296362306a36Sopenharmony_ci return -EOPNOTSUPP; 296462306a36Sopenharmony_ci } 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci tmplt_priv = ops->tmplt_create(net, chain, tca, extack); 296762306a36Sopenharmony_ci if (IS_ERR(tmplt_priv)) { 296862306a36Sopenharmony_ci module_put(ops->owner); 296962306a36Sopenharmony_ci return PTR_ERR(tmplt_priv); 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci chain->tmplt_ops = ops; 297262306a36Sopenharmony_ci chain->tmplt_priv = tmplt_priv; 297362306a36Sopenharmony_ci return 0; 297462306a36Sopenharmony_ci} 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_cistatic void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops, 297762306a36Sopenharmony_ci void *tmplt_priv) 297862306a36Sopenharmony_ci{ 297962306a36Sopenharmony_ci /* If template ops are set, no work to do for us. */ 298062306a36Sopenharmony_ci if (!tmplt_ops) 298162306a36Sopenharmony_ci return; 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci tmplt_ops->tmplt_destroy(tmplt_priv); 298462306a36Sopenharmony_ci module_put(tmplt_ops->owner); 298562306a36Sopenharmony_ci} 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci/* Add/delete/get a chain */ 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_cistatic int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, 299062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 299162306a36Sopenharmony_ci{ 299262306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 299362306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 299462306a36Sopenharmony_ci struct tcmsg *t; 299562306a36Sopenharmony_ci u32 parent; 299662306a36Sopenharmony_ci u32 chain_index; 299762306a36Sopenharmony_ci struct Qdisc *q; 299862306a36Sopenharmony_ci struct tcf_chain *chain; 299962306a36Sopenharmony_ci struct tcf_block *block; 300062306a36Sopenharmony_ci unsigned long cl; 300162306a36Sopenharmony_ci int err; 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_cireplay: 300462306a36Sopenharmony_ci q = NULL; 300562306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX, 300662306a36Sopenharmony_ci rtm_tca_policy, extack); 300762306a36Sopenharmony_ci if (err < 0) 300862306a36Sopenharmony_ci return err; 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci t = nlmsg_data(n); 301162306a36Sopenharmony_ci parent = t->tcm_parent; 301262306a36Sopenharmony_ci cl = 0; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci block = tcf_block_find(net, &q, &parent, &cl, 301562306a36Sopenharmony_ci t->tcm_ifindex, t->tcm_block_index, extack); 301662306a36Sopenharmony_ci if (IS_ERR(block)) 301762306a36Sopenharmony_ci return PTR_ERR(block); 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; 302062306a36Sopenharmony_ci if (chain_index > TC_ACT_EXT_VAL_MASK) { 302162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit"); 302262306a36Sopenharmony_ci err = -EINVAL; 302362306a36Sopenharmony_ci goto errout_block; 302462306a36Sopenharmony_ci } 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci mutex_lock(&block->lock); 302762306a36Sopenharmony_ci chain = tcf_chain_lookup(block, chain_index); 302862306a36Sopenharmony_ci if (n->nlmsg_type == RTM_NEWCHAIN) { 302962306a36Sopenharmony_ci if (chain) { 303062306a36Sopenharmony_ci if (tcf_chain_held_by_acts_only(chain)) { 303162306a36Sopenharmony_ci /* The chain exists only because there is 303262306a36Sopenharmony_ci * some action referencing it. 303362306a36Sopenharmony_ci */ 303462306a36Sopenharmony_ci tcf_chain_hold(chain); 303562306a36Sopenharmony_ci } else { 303662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Filter chain already exists"); 303762306a36Sopenharmony_ci err = -EEXIST; 303862306a36Sopenharmony_ci goto errout_block_locked; 303962306a36Sopenharmony_ci } 304062306a36Sopenharmony_ci } else { 304162306a36Sopenharmony_ci if (!(n->nlmsg_flags & NLM_F_CREATE)) { 304262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain"); 304362306a36Sopenharmony_ci err = -ENOENT; 304462306a36Sopenharmony_ci goto errout_block_locked; 304562306a36Sopenharmony_ci } 304662306a36Sopenharmony_ci chain = tcf_chain_create(block, chain_index); 304762306a36Sopenharmony_ci if (!chain) { 304862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to create filter chain"); 304962306a36Sopenharmony_ci err = -ENOMEM; 305062306a36Sopenharmony_ci goto errout_block_locked; 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci } 305362306a36Sopenharmony_ci } else { 305462306a36Sopenharmony_ci if (!chain || tcf_chain_held_by_acts_only(chain)) { 305562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot find specified filter chain"); 305662306a36Sopenharmony_ci err = -EINVAL; 305762306a36Sopenharmony_ci goto errout_block_locked; 305862306a36Sopenharmony_ci } 305962306a36Sopenharmony_ci tcf_chain_hold(chain); 306062306a36Sopenharmony_ci } 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci if (n->nlmsg_type == RTM_NEWCHAIN) { 306362306a36Sopenharmony_ci /* Modifying chain requires holding parent block lock. In case 306462306a36Sopenharmony_ci * the chain was successfully added, take a reference to the 306562306a36Sopenharmony_ci * chain. This ensures that an empty chain does not disappear at 306662306a36Sopenharmony_ci * the end of this function. 306762306a36Sopenharmony_ci */ 306862306a36Sopenharmony_ci tcf_chain_hold(chain); 306962306a36Sopenharmony_ci chain->explicitly_created = true; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci mutex_unlock(&block->lock); 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci switch (n->nlmsg_type) { 307462306a36Sopenharmony_ci case RTM_NEWCHAIN: 307562306a36Sopenharmony_ci err = tc_chain_tmplt_add(chain, net, tca, extack); 307662306a36Sopenharmony_ci if (err) { 307762306a36Sopenharmony_ci tcf_chain_put_explicitly_created(chain); 307862306a36Sopenharmony_ci goto errout; 307962306a36Sopenharmony_ci } 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, 308262306a36Sopenharmony_ci RTM_NEWCHAIN, false, extack); 308362306a36Sopenharmony_ci break; 308462306a36Sopenharmony_ci case RTM_DELCHAIN: 308562306a36Sopenharmony_ci tfilter_notify_chain(net, skb, block, q, parent, n, 308662306a36Sopenharmony_ci chain, RTM_DELTFILTER, extack); 308762306a36Sopenharmony_ci /* Flush the chain first as the user requested chain removal. */ 308862306a36Sopenharmony_ci tcf_chain_flush(chain, true); 308962306a36Sopenharmony_ci /* In case the chain was successfully deleted, put a reference 309062306a36Sopenharmony_ci * to the chain previously taken during addition. 309162306a36Sopenharmony_ci */ 309262306a36Sopenharmony_ci tcf_chain_put_explicitly_created(chain); 309362306a36Sopenharmony_ci break; 309462306a36Sopenharmony_ci case RTM_GETCHAIN: 309562306a36Sopenharmony_ci err = tc_chain_notify(chain, skb, n->nlmsg_seq, 309662306a36Sopenharmony_ci n->nlmsg_flags, n->nlmsg_type, true, extack); 309762306a36Sopenharmony_ci if (err < 0) 309862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); 309962306a36Sopenharmony_ci break; 310062306a36Sopenharmony_ci default: 310162306a36Sopenharmony_ci err = -EOPNOTSUPP; 310262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unsupported message type"); 310362306a36Sopenharmony_ci goto errout; 310462306a36Sopenharmony_ci } 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_cierrout: 310762306a36Sopenharmony_ci tcf_chain_put(chain); 310862306a36Sopenharmony_cierrout_block: 310962306a36Sopenharmony_ci tcf_block_release(q, block, true); 311062306a36Sopenharmony_ci if (err == -EAGAIN) 311162306a36Sopenharmony_ci /* Replay the request. */ 311262306a36Sopenharmony_ci goto replay; 311362306a36Sopenharmony_ci return err; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_cierrout_block_locked: 311662306a36Sopenharmony_ci mutex_unlock(&block->lock); 311762306a36Sopenharmony_ci goto errout_block; 311862306a36Sopenharmony_ci} 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci/* called with RTNL */ 312162306a36Sopenharmony_cistatic int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) 312262306a36Sopenharmony_ci{ 312362306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 312462306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 312562306a36Sopenharmony_ci struct Qdisc *q = NULL; 312662306a36Sopenharmony_ci struct tcf_block *block; 312762306a36Sopenharmony_ci struct tcmsg *tcm = nlmsg_data(cb->nlh); 312862306a36Sopenharmony_ci struct tcf_chain *chain; 312962306a36Sopenharmony_ci long index_start; 313062306a36Sopenharmony_ci long index; 313162306a36Sopenharmony_ci int err; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 313462306a36Sopenharmony_ci return skb->len; 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX, 313762306a36Sopenharmony_ci rtm_tca_policy, cb->extack); 313862306a36Sopenharmony_ci if (err) 313962306a36Sopenharmony_ci return err; 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_ci if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) { 314262306a36Sopenharmony_ci block = tcf_block_refcnt_get(net, tcm->tcm_block_index); 314362306a36Sopenharmony_ci if (!block) 314462306a36Sopenharmony_ci goto out; 314562306a36Sopenharmony_ci } else { 314662306a36Sopenharmony_ci const struct Qdisc_class_ops *cops; 314762306a36Sopenharmony_ci struct net_device *dev; 314862306a36Sopenharmony_ci unsigned long cl = 0; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci dev = __dev_get_by_index(net, tcm->tcm_ifindex); 315162306a36Sopenharmony_ci if (!dev) 315262306a36Sopenharmony_ci return skb->len; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci if (!tcm->tcm_parent) 315562306a36Sopenharmony_ci q = rtnl_dereference(dev->qdisc); 315662306a36Sopenharmony_ci else 315762306a36Sopenharmony_ci q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci if (!q) 316062306a36Sopenharmony_ci goto out; 316162306a36Sopenharmony_ci cops = q->ops->cl_ops; 316262306a36Sopenharmony_ci if (!cops) 316362306a36Sopenharmony_ci goto out; 316462306a36Sopenharmony_ci if (!cops->tcf_block) 316562306a36Sopenharmony_ci goto out; 316662306a36Sopenharmony_ci if (TC_H_MIN(tcm->tcm_parent)) { 316762306a36Sopenharmony_ci cl = cops->find(q, tcm->tcm_parent); 316862306a36Sopenharmony_ci if (cl == 0) 316962306a36Sopenharmony_ci goto out; 317062306a36Sopenharmony_ci } 317162306a36Sopenharmony_ci block = cops->tcf_block(q, cl, NULL); 317262306a36Sopenharmony_ci if (!block) 317362306a36Sopenharmony_ci goto out; 317462306a36Sopenharmony_ci if (tcf_block_shared(block)) 317562306a36Sopenharmony_ci q = NULL; 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci index_start = cb->args[0]; 317962306a36Sopenharmony_ci index = 0; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci mutex_lock(&block->lock); 318262306a36Sopenharmony_ci list_for_each_entry(chain, &block->chain_list, list) { 318362306a36Sopenharmony_ci if ((tca[TCA_CHAIN] && 318462306a36Sopenharmony_ci nla_get_u32(tca[TCA_CHAIN]) != chain->index)) 318562306a36Sopenharmony_ci continue; 318662306a36Sopenharmony_ci if (index < index_start) { 318762306a36Sopenharmony_ci index++; 318862306a36Sopenharmony_ci continue; 318962306a36Sopenharmony_ci } 319062306a36Sopenharmony_ci if (tcf_chain_held_by_acts_only(chain)) 319162306a36Sopenharmony_ci continue; 319262306a36Sopenharmony_ci err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, 319362306a36Sopenharmony_ci chain->index, net, skb, block, 319462306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 319562306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 319662306a36Sopenharmony_ci RTM_NEWCHAIN, NULL); 319762306a36Sopenharmony_ci if (err <= 0) 319862306a36Sopenharmony_ci break; 319962306a36Sopenharmony_ci index++; 320062306a36Sopenharmony_ci } 320162306a36Sopenharmony_ci mutex_unlock(&block->lock); 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) 320462306a36Sopenharmony_ci tcf_block_refcnt_put(block, true); 320562306a36Sopenharmony_ci cb->args[0] = index; 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ciout: 320862306a36Sopenharmony_ci /* If we did no progress, the error (EMSGSIZE) is real */ 320962306a36Sopenharmony_ci if (skb->len == 0 && err) 321062306a36Sopenharmony_ci return err; 321162306a36Sopenharmony_ci return skb->len; 321262306a36Sopenharmony_ci} 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ciint tcf_exts_init_ex(struct tcf_exts *exts, struct net *net, int action, 321562306a36Sopenharmony_ci int police, struct tcf_proto *tp, u32 handle, 321662306a36Sopenharmony_ci bool use_action_miss) 321762306a36Sopenharmony_ci{ 321862306a36Sopenharmony_ci int err = 0; 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 322162306a36Sopenharmony_ci exts->type = 0; 322262306a36Sopenharmony_ci exts->nr_actions = 0; 322362306a36Sopenharmony_ci exts->miss_cookie_node = NULL; 322462306a36Sopenharmony_ci /* Note: we do not own yet a reference on net. 322562306a36Sopenharmony_ci * This reference might be taken later from tcf_exts_get_net(). 322662306a36Sopenharmony_ci */ 322762306a36Sopenharmony_ci exts->net = net; 322862306a36Sopenharmony_ci exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *), 322962306a36Sopenharmony_ci GFP_KERNEL); 323062306a36Sopenharmony_ci if (!exts->actions) 323162306a36Sopenharmony_ci return -ENOMEM; 323262306a36Sopenharmony_ci#endif 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci exts->action = action; 323562306a36Sopenharmony_ci exts->police = police; 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci if (!use_action_miss) 323862306a36Sopenharmony_ci return 0; 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci err = tcf_exts_miss_cookie_base_alloc(exts, tp, handle); 324162306a36Sopenharmony_ci if (err) 324262306a36Sopenharmony_ci goto err_miss_alloc; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci return 0; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_cierr_miss_alloc: 324762306a36Sopenharmony_ci tcf_exts_destroy(exts); 324862306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 324962306a36Sopenharmony_ci exts->actions = NULL; 325062306a36Sopenharmony_ci#endif 325162306a36Sopenharmony_ci return err; 325262306a36Sopenharmony_ci} 325362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_init_ex); 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_civoid tcf_exts_destroy(struct tcf_exts *exts) 325662306a36Sopenharmony_ci{ 325762306a36Sopenharmony_ci tcf_exts_miss_cookie_base_destroy(exts); 325862306a36Sopenharmony_ci 325962306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 326062306a36Sopenharmony_ci if (exts->actions) { 326162306a36Sopenharmony_ci tcf_action_destroy(exts->actions, TCA_ACT_UNBIND); 326262306a36Sopenharmony_ci kfree(exts->actions); 326362306a36Sopenharmony_ci } 326462306a36Sopenharmony_ci exts->nr_actions = 0; 326562306a36Sopenharmony_ci#endif 326662306a36Sopenharmony_ci} 326762306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_destroy); 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ciint tcf_exts_validate_ex(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 327062306a36Sopenharmony_ci struct nlattr *rate_tlv, struct tcf_exts *exts, 327162306a36Sopenharmony_ci u32 flags, u32 fl_flags, struct netlink_ext_ack *extack) 327262306a36Sopenharmony_ci{ 327362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 327462306a36Sopenharmony_ci { 327562306a36Sopenharmony_ci int init_res[TCA_ACT_MAX_PRIO] = {}; 327662306a36Sopenharmony_ci struct tc_action *act; 327762306a36Sopenharmony_ci size_t attr_size = 0; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci if (exts->police && tb[exts->police]) { 328062306a36Sopenharmony_ci struct tc_action_ops *a_o; 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci a_o = tc_action_load_ops(tb[exts->police], true, 328362306a36Sopenharmony_ci !(flags & TCA_ACT_FLAGS_NO_RTNL), 328462306a36Sopenharmony_ci extack); 328562306a36Sopenharmony_ci if (IS_ERR(a_o)) 328662306a36Sopenharmony_ci return PTR_ERR(a_o); 328762306a36Sopenharmony_ci flags |= TCA_ACT_FLAGS_POLICE | TCA_ACT_FLAGS_BIND; 328862306a36Sopenharmony_ci act = tcf_action_init_1(net, tp, tb[exts->police], 328962306a36Sopenharmony_ci rate_tlv, a_o, init_res, flags, 329062306a36Sopenharmony_ci extack); 329162306a36Sopenharmony_ci module_put(a_o->owner); 329262306a36Sopenharmony_ci if (IS_ERR(act)) 329362306a36Sopenharmony_ci return PTR_ERR(act); 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci act->type = exts->type = TCA_OLD_COMPAT; 329662306a36Sopenharmony_ci exts->actions[0] = act; 329762306a36Sopenharmony_ci exts->nr_actions = 1; 329862306a36Sopenharmony_ci tcf_idr_insert_many(exts->actions); 329962306a36Sopenharmony_ci } else if (exts->action && tb[exts->action]) { 330062306a36Sopenharmony_ci int err; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci flags |= TCA_ACT_FLAGS_BIND; 330362306a36Sopenharmony_ci err = tcf_action_init(net, tp, tb[exts->action], 330462306a36Sopenharmony_ci rate_tlv, exts->actions, init_res, 330562306a36Sopenharmony_ci &attr_size, flags, fl_flags, 330662306a36Sopenharmony_ci extack); 330762306a36Sopenharmony_ci if (err < 0) 330862306a36Sopenharmony_ci return err; 330962306a36Sopenharmony_ci exts->nr_actions = err; 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci } 331262306a36Sopenharmony_ci#else 331362306a36Sopenharmony_ci if ((exts->action && tb[exts->action]) || 331462306a36Sopenharmony_ci (exts->police && tb[exts->police])) { 331562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)"); 331662306a36Sopenharmony_ci return -EOPNOTSUPP; 331762306a36Sopenharmony_ci } 331862306a36Sopenharmony_ci#endif 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci return 0; 332162306a36Sopenharmony_ci} 332262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_validate_ex); 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ciint tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, 332562306a36Sopenharmony_ci struct nlattr *rate_tlv, struct tcf_exts *exts, 332662306a36Sopenharmony_ci u32 flags, struct netlink_ext_ack *extack) 332762306a36Sopenharmony_ci{ 332862306a36Sopenharmony_ci return tcf_exts_validate_ex(net, tp, tb, rate_tlv, exts, 332962306a36Sopenharmony_ci flags, 0, extack); 333062306a36Sopenharmony_ci} 333162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_validate); 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_civoid tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) 333462306a36Sopenharmony_ci{ 333562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 333662306a36Sopenharmony_ci struct tcf_exts old = *dst; 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci *dst = *src; 333962306a36Sopenharmony_ci tcf_exts_destroy(&old); 334062306a36Sopenharmony_ci#endif 334162306a36Sopenharmony_ci} 334262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_change); 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 334562306a36Sopenharmony_cistatic struct tc_action *tcf_exts_first_act(struct tcf_exts *exts) 334662306a36Sopenharmony_ci{ 334762306a36Sopenharmony_ci if (exts->nr_actions == 0) 334862306a36Sopenharmony_ci return NULL; 334962306a36Sopenharmony_ci else 335062306a36Sopenharmony_ci return exts->actions[0]; 335162306a36Sopenharmony_ci} 335262306a36Sopenharmony_ci#endif 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ciint tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) 335562306a36Sopenharmony_ci{ 335662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 335762306a36Sopenharmony_ci struct nlattr *nest; 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci if (exts->action && tcf_exts_has_actions(exts)) { 336062306a36Sopenharmony_ci /* 336162306a36Sopenharmony_ci * again for backward compatible mode - we want 336262306a36Sopenharmony_ci * to work with both old and new modes of entering 336362306a36Sopenharmony_ci * tc data even if iproute2 was newer - jhs 336462306a36Sopenharmony_ci */ 336562306a36Sopenharmony_ci if (exts->type != TCA_OLD_COMPAT) { 336662306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, exts->action); 336762306a36Sopenharmony_ci if (nest == NULL) 336862306a36Sopenharmony_ci goto nla_put_failure; 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci if (tcf_action_dump(skb, exts->actions, 0, 0, false) 337162306a36Sopenharmony_ci < 0) 337262306a36Sopenharmony_ci goto nla_put_failure; 337362306a36Sopenharmony_ci nla_nest_end(skb, nest); 337462306a36Sopenharmony_ci } else if (exts->police) { 337562306a36Sopenharmony_ci struct tc_action *act = tcf_exts_first_act(exts); 337662306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, exts->police); 337762306a36Sopenharmony_ci if (nest == NULL || !act) 337862306a36Sopenharmony_ci goto nla_put_failure; 337962306a36Sopenharmony_ci if (tcf_action_dump_old(skb, act, 0, 0) < 0) 338062306a36Sopenharmony_ci goto nla_put_failure; 338162306a36Sopenharmony_ci nla_nest_end(skb, nest); 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci } 338462306a36Sopenharmony_ci return 0; 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_cinla_put_failure: 338762306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 338862306a36Sopenharmony_ci return -1; 338962306a36Sopenharmony_ci#else 339062306a36Sopenharmony_ci return 0; 339162306a36Sopenharmony_ci#endif 339262306a36Sopenharmony_ci} 339362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_dump); 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ciint tcf_exts_terse_dump(struct sk_buff *skb, struct tcf_exts *exts) 339662306a36Sopenharmony_ci{ 339762306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 339862306a36Sopenharmony_ci struct nlattr *nest; 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci if (!exts->action || !tcf_exts_has_actions(exts)) 340162306a36Sopenharmony_ci return 0; 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, exts->action); 340462306a36Sopenharmony_ci if (!nest) 340562306a36Sopenharmony_ci goto nla_put_failure; 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci if (tcf_action_dump(skb, exts->actions, 0, 0, true) < 0) 340862306a36Sopenharmony_ci goto nla_put_failure; 340962306a36Sopenharmony_ci nla_nest_end(skb, nest); 341062306a36Sopenharmony_ci return 0; 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_cinla_put_failure: 341362306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 341462306a36Sopenharmony_ci return -1; 341562306a36Sopenharmony_ci#else 341662306a36Sopenharmony_ci return 0; 341762306a36Sopenharmony_ci#endif 341862306a36Sopenharmony_ci} 341962306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_terse_dump); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ciint tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) 342262306a36Sopenharmony_ci{ 342362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 342462306a36Sopenharmony_ci struct tc_action *a = tcf_exts_first_act(exts); 342562306a36Sopenharmony_ci if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) 342662306a36Sopenharmony_ci return -1; 342762306a36Sopenharmony_ci#endif 342862306a36Sopenharmony_ci return 0; 342962306a36Sopenharmony_ci} 343062306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_dump_stats); 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_cistatic void tcf_block_offload_inc(struct tcf_block *block, u32 *flags) 343362306a36Sopenharmony_ci{ 343462306a36Sopenharmony_ci if (*flags & TCA_CLS_FLAGS_IN_HW) 343562306a36Sopenharmony_ci return; 343662306a36Sopenharmony_ci *flags |= TCA_CLS_FLAGS_IN_HW; 343762306a36Sopenharmony_ci atomic_inc(&block->offloadcnt); 343862306a36Sopenharmony_ci} 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_cistatic void tcf_block_offload_dec(struct tcf_block *block, u32 *flags) 344162306a36Sopenharmony_ci{ 344262306a36Sopenharmony_ci if (!(*flags & TCA_CLS_FLAGS_IN_HW)) 344362306a36Sopenharmony_ci return; 344462306a36Sopenharmony_ci *flags &= ~TCA_CLS_FLAGS_IN_HW; 344562306a36Sopenharmony_ci atomic_dec(&block->offloadcnt); 344662306a36Sopenharmony_ci} 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_cistatic void tc_cls_offload_cnt_update(struct tcf_block *block, 344962306a36Sopenharmony_ci struct tcf_proto *tp, u32 *cnt, 345062306a36Sopenharmony_ci u32 *flags, u32 diff, bool add) 345162306a36Sopenharmony_ci{ 345262306a36Sopenharmony_ci lockdep_assert_held(&block->cb_lock); 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci spin_lock(&tp->lock); 345562306a36Sopenharmony_ci if (add) { 345662306a36Sopenharmony_ci if (!*cnt) 345762306a36Sopenharmony_ci tcf_block_offload_inc(block, flags); 345862306a36Sopenharmony_ci *cnt += diff; 345962306a36Sopenharmony_ci } else { 346062306a36Sopenharmony_ci *cnt -= diff; 346162306a36Sopenharmony_ci if (!*cnt) 346262306a36Sopenharmony_ci tcf_block_offload_dec(block, flags); 346362306a36Sopenharmony_ci } 346462306a36Sopenharmony_ci spin_unlock(&tp->lock); 346562306a36Sopenharmony_ci} 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_cistatic void 346862306a36Sopenharmony_citc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp, 346962306a36Sopenharmony_ci u32 *cnt, u32 *flags) 347062306a36Sopenharmony_ci{ 347162306a36Sopenharmony_ci lockdep_assert_held(&block->cb_lock); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci spin_lock(&tp->lock); 347462306a36Sopenharmony_ci tcf_block_offload_dec(block, flags); 347562306a36Sopenharmony_ci *cnt = 0; 347662306a36Sopenharmony_ci spin_unlock(&tp->lock); 347762306a36Sopenharmony_ci} 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_cistatic int 348062306a36Sopenharmony_ci__tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 348162306a36Sopenharmony_ci void *type_data, bool err_stop) 348262306a36Sopenharmony_ci{ 348362306a36Sopenharmony_ci struct flow_block_cb *block_cb; 348462306a36Sopenharmony_ci int ok_count = 0; 348562306a36Sopenharmony_ci int err; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci list_for_each_entry(block_cb, &block->flow_block.cb_list, list) { 348862306a36Sopenharmony_ci err = block_cb->cb(type, type_data, block_cb->cb_priv); 348962306a36Sopenharmony_ci if (err) { 349062306a36Sopenharmony_ci if (err_stop) 349162306a36Sopenharmony_ci return err; 349262306a36Sopenharmony_ci } else { 349362306a36Sopenharmony_ci ok_count++; 349462306a36Sopenharmony_ci } 349562306a36Sopenharmony_ci } 349662306a36Sopenharmony_ci return ok_count; 349762306a36Sopenharmony_ci} 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ciint tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, 350062306a36Sopenharmony_ci void *type_data, bool err_stop, bool rtnl_held) 350162306a36Sopenharmony_ci{ 350262306a36Sopenharmony_ci bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 350362306a36Sopenharmony_ci int ok_count; 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ciretry: 350662306a36Sopenharmony_ci if (take_rtnl) 350762306a36Sopenharmony_ci rtnl_lock(); 350862306a36Sopenharmony_ci down_read(&block->cb_lock); 350962306a36Sopenharmony_ci /* Need to obtain rtnl lock if block is bound to devs that require it. 351062306a36Sopenharmony_ci * In block bind code cb_lock is obtained while holding rtnl, so we must 351162306a36Sopenharmony_ci * obtain the locks in same order here. 351262306a36Sopenharmony_ci */ 351362306a36Sopenharmony_ci if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 351462306a36Sopenharmony_ci up_read(&block->cb_lock); 351562306a36Sopenharmony_ci take_rtnl = true; 351662306a36Sopenharmony_ci goto retry; 351762306a36Sopenharmony_ci } 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_ci ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci up_read(&block->cb_lock); 352262306a36Sopenharmony_ci if (take_rtnl) 352362306a36Sopenharmony_ci rtnl_unlock(); 352462306a36Sopenharmony_ci return ok_count; 352562306a36Sopenharmony_ci} 352662306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_call); 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci/* Non-destructive filter add. If filter that wasn't already in hardware is 352962306a36Sopenharmony_ci * successfully offloaded, increment block offloads counter. On failure, 353062306a36Sopenharmony_ci * previously offloaded filter is considered to be intact and offloads counter 353162306a36Sopenharmony_ci * is not decremented. 353262306a36Sopenharmony_ci */ 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ciint tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp, 353562306a36Sopenharmony_ci enum tc_setup_type type, void *type_data, bool err_stop, 353662306a36Sopenharmony_ci u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 353762306a36Sopenharmony_ci{ 353862306a36Sopenharmony_ci bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 353962306a36Sopenharmony_ci int ok_count; 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ciretry: 354262306a36Sopenharmony_ci if (take_rtnl) 354362306a36Sopenharmony_ci rtnl_lock(); 354462306a36Sopenharmony_ci down_read(&block->cb_lock); 354562306a36Sopenharmony_ci /* Need to obtain rtnl lock if block is bound to devs that require it. 354662306a36Sopenharmony_ci * In block bind code cb_lock is obtained while holding rtnl, so we must 354762306a36Sopenharmony_ci * obtain the locks in same order here. 354862306a36Sopenharmony_ci */ 354962306a36Sopenharmony_ci if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 355062306a36Sopenharmony_ci up_read(&block->cb_lock); 355162306a36Sopenharmony_ci take_rtnl = true; 355262306a36Sopenharmony_ci goto retry; 355362306a36Sopenharmony_ci } 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci /* Make sure all netdevs sharing this block are offload-capable. */ 355662306a36Sopenharmony_ci if (block->nooffloaddevcnt && err_stop) { 355762306a36Sopenharmony_ci ok_count = -EOPNOTSUPP; 355862306a36Sopenharmony_ci goto err_unlock; 355962306a36Sopenharmony_ci } 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 356262306a36Sopenharmony_ci if (ok_count < 0) 356362306a36Sopenharmony_ci goto err_unlock; 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_ci if (tp->ops->hw_add) 356662306a36Sopenharmony_ci tp->ops->hw_add(tp, type_data); 356762306a36Sopenharmony_ci if (ok_count > 0) 356862306a36Sopenharmony_ci tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 356962306a36Sopenharmony_ci ok_count, true); 357062306a36Sopenharmony_cierr_unlock: 357162306a36Sopenharmony_ci up_read(&block->cb_lock); 357262306a36Sopenharmony_ci if (take_rtnl) 357362306a36Sopenharmony_ci rtnl_unlock(); 357462306a36Sopenharmony_ci return min(ok_count, 0); 357562306a36Sopenharmony_ci} 357662306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_add); 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci/* Destructive filter replace. If filter that wasn't already in hardware is 357962306a36Sopenharmony_ci * successfully offloaded, increment block offload counter. On failure, 358062306a36Sopenharmony_ci * previously offloaded filter is considered to be destroyed and offload counter 358162306a36Sopenharmony_ci * is decremented. 358262306a36Sopenharmony_ci */ 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ciint tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp, 358562306a36Sopenharmony_ci enum tc_setup_type type, void *type_data, bool err_stop, 358662306a36Sopenharmony_ci u32 *old_flags, unsigned int *old_in_hw_count, 358762306a36Sopenharmony_ci u32 *new_flags, unsigned int *new_in_hw_count, 358862306a36Sopenharmony_ci bool rtnl_held) 358962306a36Sopenharmony_ci{ 359062306a36Sopenharmony_ci bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 359162306a36Sopenharmony_ci int ok_count; 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ciretry: 359462306a36Sopenharmony_ci if (take_rtnl) 359562306a36Sopenharmony_ci rtnl_lock(); 359662306a36Sopenharmony_ci down_read(&block->cb_lock); 359762306a36Sopenharmony_ci /* Need to obtain rtnl lock if block is bound to devs that require it. 359862306a36Sopenharmony_ci * In block bind code cb_lock is obtained while holding rtnl, so we must 359962306a36Sopenharmony_ci * obtain the locks in same order here. 360062306a36Sopenharmony_ci */ 360162306a36Sopenharmony_ci if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 360262306a36Sopenharmony_ci up_read(&block->cb_lock); 360362306a36Sopenharmony_ci take_rtnl = true; 360462306a36Sopenharmony_ci goto retry; 360562306a36Sopenharmony_ci } 360662306a36Sopenharmony_ci 360762306a36Sopenharmony_ci /* Make sure all netdevs sharing this block are offload-capable. */ 360862306a36Sopenharmony_ci if (block->nooffloaddevcnt && err_stop) { 360962306a36Sopenharmony_ci ok_count = -EOPNOTSUPP; 361062306a36Sopenharmony_ci goto err_unlock; 361162306a36Sopenharmony_ci } 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags); 361462306a36Sopenharmony_ci if (tp->ops->hw_del) 361562306a36Sopenharmony_ci tp->ops->hw_del(tp, type_data); 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 361862306a36Sopenharmony_ci if (ok_count < 0) 361962306a36Sopenharmony_ci goto err_unlock; 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_ci if (tp->ops->hw_add) 362262306a36Sopenharmony_ci tp->ops->hw_add(tp, type_data); 362362306a36Sopenharmony_ci if (ok_count > 0) 362462306a36Sopenharmony_ci tc_cls_offload_cnt_update(block, tp, new_in_hw_count, 362562306a36Sopenharmony_ci new_flags, ok_count, true); 362662306a36Sopenharmony_cierr_unlock: 362762306a36Sopenharmony_ci up_read(&block->cb_lock); 362862306a36Sopenharmony_ci if (take_rtnl) 362962306a36Sopenharmony_ci rtnl_unlock(); 363062306a36Sopenharmony_ci return min(ok_count, 0); 363162306a36Sopenharmony_ci} 363262306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_replace); 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci/* Destroy filter and decrement block offload counter, if filter was previously 363562306a36Sopenharmony_ci * offloaded. 363662306a36Sopenharmony_ci */ 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_ciint tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp, 363962306a36Sopenharmony_ci enum tc_setup_type type, void *type_data, bool err_stop, 364062306a36Sopenharmony_ci u32 *flags, unsigned int *in_hw_count, bool rtnl_held) 364162306a36Sopenharmony_ci{ 364262306a36Sopenharmony_ci bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held; 364362306a36Sopenharmony_ci int ok_count; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ciretry: 364662306a36Sopenharmony_ci if (take_rtnl) 364762306a36Sopenharmony_ci rtnl_lock(); 364862306a36Sopenharmony_ci down_read(&block->cb_lock); 364962306a36Sopenharmony_ci /* Need to obtain rtnl lock if block is bound to devs that require it. 365062306a36Sopenharmony_ci * In block bind code cb_lock is obtained while holding rtnl, so we must 365162306a36Sopenharmony_ci * obtain the locks in same order here. 365262306a36Sopenharmony_ci */ 365362306a36Sopenharmony_ci if (!rtnl_held && !take_rtnl && block->lockeddevcnt) { 365462306a36Sopenharmony_ci up_read(&block->cb_lock); 365562306a36Sopenharmony_ci take_rtnl = true; 365662306a36Sopenharmony_ci goto retry; 365762306a36Sopenharmony_ci } 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci ok_count = __tc_setup_cb_call(block, type, type_data, err_stop); 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags); 366262306a36Sopenharmony_ci if (tp->ops->hw_del) 366362306a36Sopenharmony_ci tp->ops->hw_del(tp, type_data); 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci up_read(&block->cb_lock); 366662306a36Sopenharmony_ci if (take_rtnl) 366762306a36Sopenharmony_ci rtnl_unlock(); 366862306a36Sopenharmony_ci return min(ok_count, 0); 366962306a36Sopenharmony_ci} 367062306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_destroy); 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ciint tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp, 367362306a36Sopenharmony_ci bool add, flow_setup_cb_t *cb, 367462306a36Sopenharmony_ci enum tc_setup_type type, void *type_data, 367562306a36Sopenharmony_ci void *cb_priv, u32 *flags, unsigned int *in_hw_count) 367662306a36Sopenharmony_ci{ 367762306a36Sopenharmony_ci int err = cb(type, type_data, cb_priv); 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_ci if (err) { 368062306a36Sopenharmony_ci if (add && tc_skip_sw(*flags)) 368162306a36Sopenharmony_ci return err; 368262306a36Sopenharmony_ci } else { 368362306a36Sopenharmony_ci tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1, 368462306a36Sopenharmony_ci add); 368562306a36Sopenharmony_ci } 368662306a36Sopenharmony_ci 368762306a36Sopenharmony_ci return 0; 368862306a36Sopenharmony_ci} 368962306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_cb_reoffload); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_cistatic int tcf_act_get_user_cookie(struct flow_action_entry *entry, 369262306a36Sopenharmony_ci const struct tc_action *act) 369362306a36Sopenharmony_ci{ 369462306a36Sopenharmony_ci struct tc_cookie *user_cookie; 369562306a36Sopenharmony_ci int err = 0; 369662306a36Sopenharmony_ci 369762306a36Sopenharmony_ci rcu_read_lock(); 369862306a36Sopenharmony_ci user_cookie = rcu_dereference(act->user_cookie); 369962306a36Sopenharmony_ci if (user_cookie) { 370062306a36Sopenharmony_ci entry->user_cookie = flow_action_cookie_create(user_cookie->data, 370162306a36Sopenharmony_ci user_cookie->len, 370262306a36Sopenharmony_ci GFP_ATOMIC); 370362306a36Sopenharmony_ci if (!entry->user_cookie) 370462306a36Sopenharmony_ci err = -ENOMEM; 370562306a36Sopenharmony_ci } 370662306a36Sopenharmony_ci rcu_read_unlock(); 370762306a36Sopenharmony_ci return err; 370862306a36Sopenharmony_ci} 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_cistatic void tcf_act_put_user_cookie(struct flow_action_entry *entry) 371162306a36Sopenharmony_ci{ 371262306a36Sopenharmony_ci flow_action_cookie_destroy(entry->user_cookie); 371362306a36Sopenharmony_ci} 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_civoid tc_cleanup_offload_action(struct flow_action *flow_action) 371662306a36Sopenharmony_ci{ 371762306a36Sopenharmony_ci struct flow_action_entry *entry; 371862306a36Sopenharmony_ci int i; 371962306a36Sopenharmony_ci 372062306a36Sopenharmony_ci flow_action_for_each(i, entry, flow_action) { 372162306a36Sopenharmony_ci tcf_act_put_user_cookie(entry); 372262306a36Sopenharmony_ci if (entry->destructor) 372362306a36Sopenharmony_ci entry->destructor(entry->destructor_priv); 372462306a36Sopenharmony_ci } 372562306a36Sopenharmony_ci} 372662306a36Sopenharmony_ciEXPORT_SYMBOL(tc_cleanup_offload_action); 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_cistatic int tc_setup_offload_act(struct tc_action *act, 372962306a36Sopenharmony_ci struct flow_action_entry *entry, 373062306a36Sopenharmony_ci u32 *index_inc, 373162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 373262306a36Sopenharmony_ci{ 373362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 373462306a36Sopenharmony_ci if (act->ops->offload_act_setup) { 373562306a36Sopenharmony_ci return act->ops->offload_act_setup(act, entry, index_inc, true, 373662306a36Sopenharmony_ci extack); 373762306a36Sopenharmony_ci } else { 373862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Action does not support offload"); 373962306a36Sopenharmony_ci return -EOPNOTSUPP; 374062306a36Sopenharmony_ci } 374162306a36Sopenharmony_ci#else 374262306a36Sopenharmony_ci return 0; 374362306a36Sopenharmony_ci#endif 374462306a36Sopenharmony_ci} 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ciint tc_setup_action(struct flow_action *flow_action, 374762306a36Sopenharmony_ci struct tc_action *actions[], 374862306a36Sopenharmony_ci u32 miss_cookie_base, 374962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 375062306a36Sopenharmony_ci{ 375162306a36Sopenharmony_ci int i, j, k, index, err = 0; 375262306a36Sopenharmony_ci struct tc_action *act; 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci BUILD_BUG_ON(TCA_ACT_HW_STATS_ANY != FLOW_ACTION_HW_STATS_ANY); 375562306a36Sopenharmony_ci BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE); 375662306a36Sopenharmony_ci BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED); 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_ci if (!actions) 375962306a36Sopenharmony_ci return 0; 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci j = 0; 376262306a36Sopenharmony_ci tcf_act_for_each_action(i, act, actions) { 376362306a36Sopenharmony_ci struct flow_action_entry *entry; 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci entry = &flow_action->entries[j]; 376662306a36Sopenharmony_ci spin_lock_bh(&act->tcfa_lock); 376762306a36Sopenharmony_ci err = tcf_act_get_user_cookie(entry, act); 376862306a36Sopenharmony_ci if (err) 376962306a36Sopenharmony_ci goto err_out_locked; 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci index = 0; 377262306a36Sopenharmony_ci err = tc_setup_offload_act(act, entry, &index, extack); 377362306a36Sopenharmony_ci if (err) 377462306a36Sopenharmony_ci goto err_out_locked; 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci for (k = 0; k < index ; k++) { 377762306a36Sopenharmony_ci entry[k].hw_stats = tc_act_hw_stats(act->hw_stats); 377862306a36Sopenharmony_ci entry[k].hw_index = act->tcfa_index; 377962306a36Sopenharmony_ci entry[k].cookie = (unsigned long)act; 378062306a36Sopenharmony_ci entry[k].miss_cookie = 378162306a36Sopenharmony_ci tcf_exts_miss_cookie_get(miss_cookie_base, i); 378262306a36Sopenharmony_ci } 378362306a36Sopenharmony_ci 378462306a36Sopenharmony_ci j += index; 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_ci spin_unlock_bh(&act->tcfa_lock); 378762306a36Sopenharmony_ci } 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_cierr_out: 379062306a36Sopenharmony_ci if (err) 379162306a36Sopenharmony_ci tc_cleanup_offload_action(flow_action); 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_ci return err; 379462306a36Sopenharmony_cierr_out_locked: 379562306a36Sopenharmony_ci spin_unlock_bh(&act->tcfa_lock); 379662306a36Sopenharmony_ci goto err_out; 379762306a36Sopenharmony_ci} 379862306a36Sopenharmony_ci 379962306a36Sopenharmony_ciint tc_setup_offload_action(struct flow_action *flow_action, 380062306a36Sopenharmony_ci const struct tcf_exts *exts, 380162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 380262306a36Sopenharmony_ci{ 380362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 380462306a36Sopenharmony_ci u32 miss_cookie_base; 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci if (!exts) 380762306a36Sopenharmony_ci return 0; 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci miss_cookie_base = exts->miss_cookie_node ? 381062306a36Sopenharmony_ci exts->miss_cookie_node->miss_cookie_base : 0; 381162306a36Sopenharmony_ci return tc_setup_action(flow_action, exts->actions, miss_cookie_base, 381262306a36Sopenharmony_ci extack); 381362306a36Sopenharmony_ci#else 381462306a36Sopenharmony_ci return 0; 381562306a36Sopenharmony_ci#endif 381662306a36Sopenharmony_ci} 381762306a36Sopenharmony_ciEXPORT_SYMBOL(tc_setup_offload_action); 381862306a36Sopenharmony_ci 381962306a36Sopenharmony_ciunsigned int tcf_exts_num_actions(struct tcf_exts *exts) 382062306a36Sopenharmony_ci{ 382162306a36Sopenharmony_ci unsigned int num_acts = 0; 382262306a36Sopenharmony_ci struct tc_action *act; 382362306a36Sopenharmony_ci int i; 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci tcf_exts_for_each_action(i, act, exts) { 382662306a36Sopenharmony_ci if (is_tcf_pedit(act)) 382762306a36Sopenharmony_ci num_acts += tcf_pedit_nkeys(act); 382862306a36Sopenharmony_ci else 382962306a36Sopenharmony_ci num_acts++; 383062306a36Sopenharmony_ci } 383162306a36Sopenharmony_ci return num_acts; 383262306a36Sopenharmony_ci} 383362306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_exts_num_actions); 383462306a36Sopenharmony_ci 383562306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 383662306a36Sopenharmony_cistatic int tcf_qevent_parse_block_index(struct nlattr *block_index_attr, 383762306a36Sopenharmony_ci u32 *p_block_index, 383862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 383962306a36Sopenharmony_ci{ 384062306a36Sopenharmony_ci *p_block_index = nla_get_u32(block_index_attr); 384162306a36Sopenharmony_ci if (!*p_block_index) { 384262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Block number may not be zero"); 384362306a36Sopenharmony_ci return -EINVAL; 384462306a36Sopenharmony_ci } 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_ci return 0; 384762306a36Sopenharmony_ci} 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ciint tcf_qevent_init(struct tcf_qevent *qe, struct Qdisc *sch, 385062306a36Sopenharmony_ci enum flow_block_binder_type binder_type, 385162306a36Sopenharmony_ci struct nlattr *block_index_attr, 385262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 385362306a36Sopenharmony_ci{ 385462306a36Sopenharmony_ci u32 block_index; 385562306a36Sopenharmony_ci int err; 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci if (!block_index_attr) 385862306a36Sopenharmony_ci return 0; 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ci err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack); 386162306a36Sopenharmony_ci if (err) 386262306a36Sopenharmony_ci return err; 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci qe->info.binder_type = binder_type; 386562306a36Sopenharmony_ci qe->info.chain_head_change = tcf_chain_head_change_dflt; 386662306a36Sopenharmony_ci qe->info.chain_head_change_priv = &qe->filter_chain; 386762306a36Sopenharmony_ci qe->info.block_index = block_index; 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_ci return tcf_block_get_ext(&qe->block, sch, &qe->info, extack); 387062306a36Sopenharmony_ci} 387162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_init); 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_civoid tcf_qevent_destroy(struct tcf_qevent *qe, struct Qdisc *sch) 387462306a36Sopenharmony_ci{ 387562306a36Sopenharmony_ci if (qe->info.block_index) 387662306a36Sopenharmony_ci tcf_block_put_ext(qe->block, sch, &qe->info); 387762306a36Sopenharmony_ci} 387862306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_destroy); 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ciint tcf_qevent_validate_change(struct tcf_qevent *qe, struct nlattr *block_index_attr, 388162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 388262306a36Sopenharmony_ci{ 388362306a36Sopenharmony_ci u32 block_index; 388462306a36Sopenharmony_ci int err; 388562306a36Sopenharmony_ci 388662306a36Sopenharmony_ci if (!block_index_attr) 388762306a36Sopenharmony_ci return 0; 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci err = tcf_qevent_parse_block_index(block_index_attr, &block_index, extack); 389062306a36Sopenharmony_ci if (err) 389162306a36Sopenharmony_ci return err; 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci /* Bounce newly-configured block or change in block. */ 389462306a36Sopenharmony_ci if (block_index != qe->info.block_index) { 389562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Change of blocks is not supported"); 389662306a36Sopenharmony_ci return -EINVAL; 389762306a36Sopenharmony_ci } 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_ci return 0; 390062306a36Sopenharmony_ci} 390162306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_validate_change); 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_cistruct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, struct sk_buff *skb, 390462306a36Sopenharmony_ci struct sk_buff **to_free, int *ret) 390562306a36Sopenharmony_ci{ 390662306a36Sopenharmony_ci struct tcf_result cl_res; 390762306a36Sopenharmony_ci struct tcf_proto *fl; 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci if (!qe->info.block_index) 391062306a36Sopenharmony_ci return skb; 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci fl = rcu_dereference_bh(qe->filter_chain); 391362306a36Sopenharmony_ci 391462306a36Sopenharmony_ci switch (tcf_classify(skb, NULL, fl, &cl_res, false)) { 391562306a36Sopenharmony_ci case TC_ACT_SHOT: 391662306a36Sopenharmony_ci qdisc_qstats_drop(sch); 391762306a36Sopenharmony_ci __qdisc_drop(skb, to_free); 391862306a36Sopenharmony_ci *ret = __NET_XMIT_BYPASS; 391962306a36Sopenharmony_ci return NULL; 392062306a36Sopenharmony_ci case TC_ACT_STOLEN: 392162306a36Sopenharmony_ci case TC_ACT_QUEUED: 392262306a36Sopenharmony_ci case TC_ACT_TRAP: 392362306a36Sopenharmony_ci __qdisc_drop(skb, to_free); 392462306a36Sopenharmony_ci *ret = __NET_XMIT_STOLEN; 392562306a36Sopenharmony_ci return NULL; 392662306a36Sopenharmony_ci case TC_ACT_REDIRECT: 392762306a36Sopenharmony_ci skb_do_redirect(skb); 392862306a36Sopenharmony_ci *ret = __NET_XMIT_STOLEN; 392962306a36Sopenharmony_ci return NULL; 393062306a36Sopenharmony_ci } 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci return skb; 393362306a36Sopenharmony_ci} 393462306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_handle); 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ciint tcf_qevent_dump(struct sk_buff *skb, int attr_name, struct tcf_qevent *qe) 393762306a36Sopenharmony_ci{ 393862306a36Sopenharmony_ci if (!qe->info.block_index) 393962306a36Sopenharmony_ci return 0; 394062306a36Sopenharmony_ci return nla_put_u32(skb, attr_name, qe->info.block_index); 394162306a36Sopenharmony_ci} 394262306a36Sopenharmony_ciEXPORT_SYMBOL(tcf_qevent_dump); 394362306a36Sopenharmony_ci#endif 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_cistatic __net_init int tcf_net_init(struct net *net) 394662306a36Sopenharmony_ci{ 394762306a36Sopenharmony_ci struct tcf_net *tn = net_generic(net, tcf_net_id); 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci spin_lock_init(&tn->idr_lock); 395062306a36Sopenharmony_ci idr_init(&tn->idr); 395162306a36Sopenharmony_ci return 0; 395262306a36Sopenharmony_ci} 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_cistatic void __net_exit tcf_net_exit(struct net *net) 395562306a36Sopenharmony_ci{ 395662306a36Sopenharmony_ci struct tcf_net *tn = net_generic(net, tcf_net_id); 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci idr_destroy(&tn->idr); 395962306a36Sopenharmony_ci} 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_cistatic struct pernet_operations tcf_net_ops = { 396262306a36Sopenharmony_ci .init = tcf_net_init, 396362306a36Sopenharmony_ci .exit = tcf_net_exit, 396462306a36Sopenharmony_ci .id = &tcf_net_id, 396562306a36Sopenharmony_ci .size = sizeof(struct tcf_net), 396662306a36Sopenharmony_ci}; 396762306a36Sopenharmony_ci 396862306a36Sopenharmony_cistatic int __init tc_filter_init(void) 396962306a36Sopenharmony_ci{ 397062306a36Sopenharmony_ci int err; 397162306a36Sopenharmony_ci 397262306a36Sopenharmony_ci tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0); 397362306a36Sopenharmony_ci if (!tc_filter_wq) 397462306a36Sopenharmony_ci return -ENOMEM; 397562306a36Sopenharmony_ci 397662306a36Sopenharmony_ci err = register_pernet_subsys(&tcf_net_ops); 397762306a36Sopenharmony_ci if (err) 397862306a36Sopenharmony_ci goto err_register_pernet_subsys; 397962306a36Sopenharmony_ci 398062306a36Sopenharmony_ci xa_init_flags(&tcf_exts_miss_cookies_xa, XA_FLAGS_ALLOC1); 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 398362306a36Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 398462306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 398562306a36Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 398662306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter, 398762306a36Sopenharmony_ci tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED); 398862306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0); 398962306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0); 399062306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain, 399162306a36Sopenharmony_ci tc_dump_chain, 0); 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci return 0; 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_cierr_register_pernet_subsys: 399662306a36Sopenharmony_ci destroy_workqueue(tc_filter_wq); 399762306a36Sopenharmony_ci return err; 399862306a36Sopenharmony_ci} 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_cisubsys_initcall(tc_filter_init); 4001