18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/act_api.c Packet action API. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Jamal Hadi Salim 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/kmod.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 198c2ecf20Sopenharmony_ci#include <net/sock.h> 208c2ecf20Sopenharmony_ci#include <net/sch_generic.h> 218c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 228c2ecf20Sopenharmony_ci#include <net/act_api.h> 238c2ecf20Sopenharmony_ci#include <net/netlink.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void tcf_action_goto_chain_exec(const struct tc_action *a, 268c2ecf20Sopenharmony_ci struct tcf_result *res) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci res->goto_tp = rcu_dereference_bh(chain->filter_chain); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void tcf_free_cookie_rcu(struct rcu_head *p) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct tc_cookie *cookie = container_of(p, struct tc_cookie, rcu); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci kfree(cookie->data); 388c2ecf20Sopenharmony_ci kfree(cookie); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie, 428c2ecf20Sopenharmony_ci struct tc_cookie *new_cookie) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct tc_cookie *old; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci old = xchg((__force struct tc_cookie **)old_cookie, new_cookie); 478c2ecf20Sopenharmony_ci if (old) 488c2ecf20Sopenharmony_ci call_rcu(&old->rcu, tcf_free_cookie_rcu); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciint tcf_action_check_ctrlact(int action, struct tcf_proto *tp, 528c2ecf20Sopenharmony_ci struct tcf_chain **newchain, 538c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int opcode = TC_ACT_EXT_OPCODE(action), ret = -EINVAL; 568c2ecf20Sopenharmony_ci u32 chain_index; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (!opcode) 598c2ecf20Sopenharmony_ci ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0; 608c2ecf20Sopenharmony_ci else if (opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC) 618c2ecf20Sopenharmony_ci ret = 0; 628c2ecf20Sopenharmony_ci if (ret) { 638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "invalid control action"); 648c2ecf20Sopenharmony_ci goto end; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) { 688c2ecf20Sopenharmony_ci chain_index = action & TC_ACT_EXT_VAL_MASK; 698c2ecf20Sopenharmony_ci if (!tp || !newchain) { 708c2ecf20Sopenharmony_ci ret = -EINVAL; 718c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 728c2ecf20Sopenharmony_ci "can't goto NULL proto/chain"); 738c2ecf20Sopenharmony_ci goto end; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci *newchain = tcf_chain_get_by_act(tp->chain->block, chain_index); 768c2ecf20Sopenharmony_ci if (!*newchain) { 778c2ecf20Sopenharmony_ci ret = -ENOMEM; 788c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 798c2ecf20Sopenharmony_ci "can't allocate goto_chain"); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ciend: 838c2ecf20Sopenharmony_ci return ret; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_action_check_ctrlact); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, 888c2ecf20Sopenharmony_ci struct tcf_chain *goto_chain) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci a->tcfa_action = action; 918c2ecf20Sopenharmony_ci goto_chain = rcu_replace_pointer(a->goto_chain, goto_chain, 1); 928c2ecf20Sopenharmony_ci return goto_chain; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_action_set_ctrlact); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* XXX: For standalone actions, we don't need a RCU grace period either, because 978c2ecf20Sopenharmony_ci * actions are always connected to filters and filters are already destroyed in 988c2ecf20Sopenharmony_ci * RCU callbacks, so after a RCU grace period actions are already disconnected 998c2ecf20Sopenharmony_ci * from filters. Readers later can not find us. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic void free_tcf(struct tc_action *p) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci free_percpu(p->cpu_bstats); 1068c2ecf20Sopenharmony_ci free_percpu(p->cpu_bstats_hw); 1078c2ecf20Sopenharmony_ci free_percpu(p->cpu_qstats); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci tcf_set_action_cookie(&p->act_cookie, NULL); 1108c2ecf20Sopenharmony_ci if (chain) 1118c2ecf20Sopenharmony_ci tcf_chain_put_by_act(chain); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci kfree(p); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void tcf_action_cleanup(struct tc_action *p) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci if (p->ops->cleanup) 1198c2ecf20Sopenharmony_ci p->ops->cleanup(p); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci gen_kill_estimator(&p->tcfa_rate_est); 1228c2ecf20Sopenharmony_ci free_tcf(p); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int __tcf_action_put(struct tc_action *p, bool bind) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = p->idrinfo; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (refcount_dec_and_mutex_lock(&p->tcfa_refcnt, &idrinfo->lock)) { 1308c2ecf20Sopenharmony_ci if (bind) 1318c2ecf20Sopenharmony_ci atomic_dec(&p->tcfa_bindcnt); 1328c2ecf20Sopenharmony_ci idr_remove(&idrinfo->action_idr, p->tcfa_index); 1338c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci tcf_action_cleanup(p); 1368c2ecf20Sopenharmony_ci return 1; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (bind) 1408c2ecf20Sopenharmony_ci atomic_dec(&p->tcfa_bindcnt); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int __tcf_idr_release(struct tc_action *p, bool bind, bool strict) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int ret = 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Release with strict==1 and bind==0 is only called through act API 1508c2ecf20Sopenharmony_ci * interface (classifiers always bind). Only case when action with 1518c2ecf20Sopenharmony_ci * positive reference count and zero bind count can exist is when it was 1528c2ecf20Sopenharmony_ci * also created with act API (unbinding last classifier will destroy the 1538c2ecf20Sopenharmony_ci * action if it was created by classifier). So only case when bind count 1548c2ecf20Sopenharmony_ci * can be changed after initial check is when unbound action is 1558c2ecf20Sopenharmony_ci * destroyed by act API while classifier binds to action with same id 1568c2ecf20Sopenharmony_ci * concurrently. This result either creation of new action(same behavior 1578c2ecf20Sopenharmony_ci * as before), or reusing existing action if concurrent process 1588c2ecf20Sopenharmony_ci * increments reference count before action is deleted. Both scenarios 1598c2ecf20Sopenharmony_ci * are acceptable. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci if (p) { 1628c2ecf20Sopenharmony_ci if (!bind && strict && atomic_read(&p->tcfa_bindcnt) > 0) 1638c2ecf20Sopenharmony_ci return -EPERM; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (__tcf_action_put(p, bind)) 1668c2ecf20Sopenharmony_ci ret = ACT_P_DELETED; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint tcf_idr_release(struct tc_action *a, bool bind) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci const struct tc_action_ops *ops = a->ops; 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ret = __tcf_idr_release(a, bind, false); 1788c2ecf20Sopenharmony_ci if (ret == ACT_P_DELETED) 1798c2ecf20Sopenharmony_ci module_put(ops->owner); 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idr_release); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic size_t tcf_action_shared_attrs_size(const struct tc_action *act) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct tc_cookie *act_cookie; 1878c2ecf20Sopenharmony_ci u32 cookie_len = 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci rcu_read_lock(); 1908c2ecf20Sopenharmony_ci act_cookie = rcu_dereference(act->act_cookie); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (act_cookie) 1938c2ecf20Sopenharmony_ci cookie_len = nla_total_size(act_cookie->len); 1948c2ecf20Sopenharmony_ci rcu_read_unlock(); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return nla_total_size(0) /* action number nested */ 1978c2ecf20Sopenharmony_ci + nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */ 1988c2ecf20Sopenharmony_ci + cookie_len /* TCA_ACT_COOKIE */ 1998c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */ 2008c2ecf20Sopenharmony_ci + nla_total_size(0) /* TCA_ACT_STATS nested */ 2018c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */ 2028c2ecf20Sopenharmony_ci /* TCA_STATS_BASIC */ 2038c2ecf20Sopenharmony_ci + nla_total_size_64bit(sizeof(struct gnet_stats_basic)) 2048c2ecf20Sopenharmony_ci /* TCA_STATS_PKT64 */ 2058c2ecf20Sopenharmony_ci + nla_total_size_64bit(sizeof(u64)) 2068c2ecf20Sopenharmony_ci /* TCA_STATS_QUEUE */ 2078c2ecf20Sopenharmony_ci + nla_total_size_64bit(sizeof(struct gnet_stats_queue)) 2088c2ecf20Sopenharmony_ci + nla_total_size(0) /* TCA_OPTIONS nested */ 2098c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct tcf_t)); /* TCA_GACT_TM */ 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic size_t tcf_action_full_attrs_size(size_t sz) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return NLMSG_HDRLEN /* struct nlmsghdr */ 2158c2ecf20Sopenharmony_ci + sizeof(struct tcamsg) 2168c2ecf20Sopenharmony_ci + nla_total_size(0) /* TCA_ACT_TAB nested */ 2178c2ecf20Sopenharmony_ci + sz; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic size_t tcf_action_fill_size(const struct tc_action *act) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci size_t sz = tcf_action_shared_attrs_size(act); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (act->ops->get_fill_size) 2258c2ecf20Sopenharmony_ci return act->ops->get_fill_size(act) + sz; 2268c2ecf20Sopenharmony_ci return sz; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, 2308c2ecf20Sopenharmony_ci struct netlink_callback *cb) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci int err = 0, index = -1, s_i = 0, n_i = 0; 2338c2ecf20Sopenharmony_ci u32 act_flags = cb->args[2]; 2348c2ecf20Sopenharmony_ci unsigned long jiffy_since = cb->args[3]; 2358c2ecf20Sopenharmony_ci struct nlattr *nest; 2368c2ecf20Sopenharmony_ci struct idr *idr = &idrinfo->action_idr; 2378c2ecf20Sopenharmony_ci struct tc_action *p; 2388c2ecf20Sopenharmony_ci unsigned long id = 1; 2398c2ecf20Sopenharmony_ci unsigned long tmp; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci s_i = cb->args[0]; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci idr_for_each_entry_ul(idr, p, tmp, id) { 2468c2ecf20Sopenharmony_ci index++; 2478c2ecf20Sopenharmony_ci if (index < s_i) 2488c2ecf20Sopenharmony_ci continue; 2498c2ecf20Sopenharmony_ci if (IS_ERR(p)) 2508c2ecf20Sopenharmony_ci continue; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (jiffy_since && 2538c2ecf20Sopenharmony_ci time_after(jiffy_since, 2548c2ecf20Sopenharmony_ci (unsigned long)p->tcfa_tm.lastuse)) 2558c2ecf20Sopenharmony_ci continue; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, n_i); 2588c2ecf20Sopenharmony_ci if (!nest) { 2598c2ecf20Sopenharmony_ci index--; 2608c2ecf20Sopenharmony_ci goto nla_put_failure; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci err = tcf_action_dump_1(skb, p, 0, 0); 2638c2ecf20Sopenharmony_ci if (err < 0) { 2648c2ecf20Sopenharmony_ci index--; 2658c2ecf20Sopenharmony_ci nlmsg_trim(skb, nest); 2668c2ecf20Sopenharmony_ci goto done; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 2698c2ecf20Sopenharmony_ci n_i++; 2708c2ecf20Sopenharmony_ci if (!(act_flags & TCA_FLAG_LARGE_DUMP_ON) && 2718c2ecf20Sopenharmony_ci n_i >= TCA_ACT_MAX_PRIO) 2728c2ecf20Sopenharmony_ci goto done; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_cidone: 2758c2ecf20Sopenharmony_ci if (index >= 0) 2768c2ecf20Sopenharmony_ci cb->args[0] = index + 1; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 2798c2ecf20Sopenharmony_ci if (n_i) { 2808c2ecf20Sopenharmony_ci if (act_flags & TCA_FLAG_LARGE_DUMP_ON) 2818c2ecf20Sopenharmony_ci cb->args[1] = n_i; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci return n_i; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cinla_put_failure: 2868c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 2878c2ecf20Sopenharmony_ci goto done; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int tcf_idr_release_unsafe(struct tc_action *p) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci if (atomic_read(&p->tcfa_bindcnt) > 0) 2938c2ecf20Sopenharmony_ci return -EPERM; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&p->tcfa_refcnt)) { 2968c2ecf20Sopenharmony_ci idr_remove(&p->idrinfo->action_idr, p->tcfa_index); 2978c2ecf20Sopenharmony_ci tcf_action_cleanup(p); 2988c2ecf20Sopenharmony_ci return ACT_P_DELETED; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, 3058c2ecf20Sopenharmony_ci const struct tc_action_ops *ops, 3068c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct nlattr *nest; 3098c2ecf20Sopenharmony_ci int n_i = 0; 3108c2ecf20Sopenharmony_ci int ret = -EINVAL; 3118c2ecf20Sopenharmony_ci struct idr *idr = &idrinfo->action_idr; 3128c2ecf20Sopenharmony_ci struct tc_action *p; 3138c2ecf20Sopenharmony_ci unsigned long id = 1; 3148c2ecf20Sopenharmony_ci unsigned long tmp; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, 0); 3178c2ecf20Sopenharmony_ci if (nest == NULL) 3188c2ecf20Sopenharmony_ci goto nla_put_failure; 3198c2ecf20Sopenharmony_ci if (nla_put_string(skb, TCA_KIND, ops->kind)) 3208c2ecf20Sopenharmony_ci goto nla_put_failure; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ret = 0; 3238c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 3248c2ecf20Sopenharmony_ci idr_for_each_entry_ul(idr, p, tmp, id) { 3258c2ecf20Sopenharmony_ci if (IS_ERR(p)) 3268c2ecf20Sopenharmony_ci continue; 3278c2ecf20Sopenharmony_ci ret = tcf_idr_release_unsafe(p); 3288c2ecf20Sopenharmony_ci if (ret == ACT_P_DELETED) 3298c2ecf20Sopenharmony_ci module_put(ops->owner); 3308c2ecf20Sopenharmony_ci else if (ret < 0) 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci n_i++; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 3358c2ecf20Sopenharmony_ci if (ret < 0) { 3368c2ecf20Sopenharmony_ci if (n_i) 3378c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unable to flush all TC actions"); 3388c2ecf20Sopenharmony_ci else 3398c2ecf20Sopenharmony_ci goto nla_put_failure; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ret = nla_put_u32(skb, TCA_FCNT, n_i); 3438c2ecf20Sopenharmony_ci if (ret) 3448c2ecf20Sopenharmony_ci goto nla_put_failure; 3458c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return n_i; 3488c2ecf20Sopenharmony_cinla_put_failure: 3498c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciint tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb, 3548c2ecf20Sopenharmony_ci struct netlink_callback *cb, int type, 3558c2ecf20Sopenharmony_ci const struct tc_action_ops *ops, 3568c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = tn->idrinfo; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (type == RTM_DELACTION) { 3618c2ecf20Sopenharmony_ci return tcf_del_walker(idrinfo, skb, ops, extack); 3628c2ecf20Sopenharmony_ci } else if (type == RTM_GETACTION) { 3638c2ecf20Sopenharmony_ci return tcf_dump_walker(idrinfo, skb, cb); 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci WARN(1, "tcf_generic_walker: unknown command %d\n", type); 3668c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "tcf_generic_walker: unknown command"); 3678c2ecf20Sopenharmony_ci return -EINVAL; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_generic_walker); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciint tcf_idr_search(struct tc_action_net *tn, struct tc_action **a, u32 index) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = tn->idrinfo; 3758c2ecf20Sopenharmony_ci struct tc_action *p; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 3788c2ecf20Sopenharmony_ci p = idr_find(&idrinfo->action_idr, index); 3798c2ecf20Sopenharmony_ci if (IS_ERR(p)) 3808c2ecf20Sopenharmony_ci p = NULL; 3818c2ecf20Sopenharmony_ci else if (p) 3828c2ecf20Sopenharmony_ci refcount_inc(&p->tcfa_refcnt); 3838c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (p) { 3868c2ecf20Sopenharmony_ci *a = p; 3878c2ecf20Sopenharmony_ci return true; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci return false; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idr_search); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int tcf_idr_delete_index(struct tcf_idrinfo *idrinfo, u32 index) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct tc_action *p; 3968c2ecf20Sopenharmony_ci int ret = 0; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 3998c2ecf20Sopenharmony_ci p = idr_find(&idrinfo->action_idr, index); 4008c2ecf20Sopenharmony_ci if (!p) { 4018c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 4028c2ecf20Sopenharmony_ci return -ENOENT; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!atomic_read(&p->tcfa_bindcnt)) { 4068c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&p->tcfa_refcnt)) { 4078c2ecf20Sopenharmony_ci struct module *owner = p->ops->owner; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci WARN_ON(p != idr_remove(&idrinfo->action_idr, 4108c2ecf20Sopenharmony_ci p->tcfa_index)); 4118c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci tcf_action_cleanup(p); 4148c2ecf20Sopenharmony_ci module_put(owner); 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci ret = 0; 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci ret = -EPERM; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ciint tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est, 4278c2ecf20Sopenharmony_ci struct tc_action **a, const struct tc_action_ops *ops, 4288c2ecf20Sopenharmony_ci int bind, bool cpustats, u32 flags) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct tc_action *p = kzalloc(ops->size, GFP_KERNEL); 4318c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = tn->idrinfo; 4328c2ecf20Sopenharmony_ci int err = -ENOMEM; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (unlikely(!p)) 4358c2ecf20Sopenharmony_ci return -ENOMEM; 4368c2ecf20Sopenharmony_ci refcount_set(&p->tcfa_refcnt, 1); 4378c2ecf20Sopenharmony_ci if (bind) 4388c2ecf20Sopenharmony_ci atomic_set(&p->tcfa_bindcnt, 1); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (cpustats) { 4418c2ecf20Sopenharmony_ci p->cpu_bstats = netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu); 4428c2ecf20Sopenharmony_ci if (!p->cpu_bstats) 4438c2ecf20Sopenharmony_ci goto err1; 4448c2ecf20Sopenharmony_ci p->cpu_bstats_hw = netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu); 4458c2ecf20Sopenharmony_ci if (!p->cpu_bstats_hw) 4468c2ecf20Sopenharmony_ci goto err2; 4478c2ecf20Sopenharmony_ci p->cpu_qstats = alloc_percpu(struct gnet_stats_queue); 4488c2ecf20Sopenharmony_ci if (!p->cpu_qstats) 4498c2ecf20Sopenharmony_ci goto err3; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci spin_lock_init(&p->tcfa_lock); 4528c2ecf20Sopenharmony_ci p->tcfa_index = index; 4538c2ecf20Sopenharmony_ci p->tcfa_tm.install = jiffies; 4548c2ecf20Sopenharmony_ci p->tcfa_tm.lastuse = jiffies; 4558c2ecf20Sopenharmony_ci p->tcfa_tm.firstuse = 0; 4568c2ecf20Sopenharmony_ci p->tcfa_flags = flags; 4578c2ecf20Sopenharmony_ci if (est) { 4588c2ecf20Sopenharmony_ci err = gen_new_estimator(&p->tcfa_bstats, p->cpu_bstats, 4598c2ecf20Sopenharmony_ci &p->tcfa_rate_est, 4608c2ecf20Sopenharmony_ci &p->tcfa_lock, NULL, est); 4618c2ecf20Sopenharmony_ci if (err) 4628c2ecf20Sopenharmony_ci goto err4; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci p->idrinfo = idrinfo; 4668c2ecf20Sopenharmony_ci __module_get(ops->owner); 4678c2ecf20Sopenharmony_ci p->ops = ops; 4688c2ecf20Sopenharmony_ci *a = p; 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_cierr4: 4718c2ecf20Sopenharmony_ci free_percpu(p->cpu_qstats); 4728c2ecf20Sopenharmony_cierr3: 4738c2ecf20Sopenharmony_ci free_percpu(p->cpu_bstats_hw); 4748c2ecf20Sopenharmony_cierr2: 4758c2ecf20Sopenharmony_ci free_percpu(p->cpu_bstats); 4768c2ecf20Sopenharmony_cierr1: 4778c2ecf20Sopenharmony_ci kfree(p); 4788c2ecf20Sopenharmony_ci return err; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idr_create); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciint tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index, 4838c2ecf20Sopenharmony_ci struct nlattr *est, struct tc_action **a, 4848c2ecf20Sopenharmony_ci const struct tc_action_ops *ops, int bind, 4858c2ecf20Sopenharmony_ci u32 flags) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci /* Set cpustats according to actions flags. */ 4888c2ecf20Sopenharmony_ci return tcf_idr_create(tn, index, est, a, ops, bind, 4898c2ecf20Sopenharmony_ci !(flags & TCA_ACT_FLAGS_NO_PERCPU_STATS), flags); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idr_create_from_flags); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* Cleanup idr index that was allocated but not initialized. */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_civoid tcf_idr_cleanup(struct tc_action_net *tn, u32 index) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = tn->idrinfo; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 5008c2ecf20Sopenharmony_ci /* Remove ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */ 5018c2ecf20Sopenharmony_ci WARN_ON(!IS_ERR(idr_remove(&idrinfo->action_idr, index))); 5028c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idr_cleanup); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* Check if action with specified index exists. If actions is found, increments 5078c2ecf20Sopenharmony_ci * its reference and bind counters, and return 1. Otherwise insert temporary 5088c2ecf20Sopenharmony_ci * error pointer (to prevent concurrent users from inserting actions with same 5098c2ecf20Sopenharmony_ci * index) and return 0. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ciint tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index, 5138c2ecf20Sopenharmony_ci struct tc_action **a, int bind) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = tn->idrinfo; 5168c2ecf20Sopenharmony_ci struct tc_action *p; 5178c2ecf20Sopenharmony_ci int ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ciagain: 5208c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 5218c2ecf20Sopenharmony_ci if (*index) { 5228c2ecf20Sopenharmony_ci p = idr_find(&idrinfo->action_idr, *index); 5238c2ecf20Sopenharmony_ci if (IS_ERR(p)) { 5248c2ecf20Sopenharmony_ci /* This means that another process allocated 5258c2ecf20Sopenharmony_ci * index but did not assign the pointer yet. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 5288c2ecf20Sopenharmony_ci goto again; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (p) { 5328c2ecf20Sopenharmony_ci refcount_inc(&p->tcfa_refcnt); 5338c2ecf20Sopenharmony_ci if (bind) 5348c2ecf20Sopenharmony_ci atomic_inc(&p->tcfa_bindcnt); 5358c2ecf20Sopenharmony_ci *a = p; 5368c2ecf20Sopenharmony_ci ret = 1; 5378c2ecf20Sopenharmony_ci } else { 5388c2ecf20Sopenharmony_ci *a = NULL; 5398c2ecf20Sopenharmony_ci ret = idr_alloc_u32(&idrinfo->action_idr, NULL, index, 5408c2ecf20Sopenharmony_ci *index, GFP_KERNEL); 5418c2ecf20Sopenharmony_ci if (!ret) 5428c2ecf20Sopenharmony_ci idr_replace(&idrinfo->action_idr, 5438c2ecf20Sopenharmony_ci ERR_PTR(-EBUSY), *index); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } else { 5468c2ecf20Sopenharmony_ci *index = 1; 5478c2ecf20Sopenharmony_ci *a = NULL; 5488c2ecf20Sopenharmony_ci ret = idr_alloc_u32(&idrinfo->action_idr, NULL, index, 5498c2ecf20Sopenharmony_ci UINT_MAX, GFP_KERNEL); 5508c2ecf20Sopenharmony_ci if (!ret) 5518c2ecf20Sopenharmony_ci idr_replace(&idrinfo->action_idr, ERR_PTR(-EBUSY), 5528c2ecf20Sopenharmony_ci *index); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 5558c2ecf20Sopenharmony_ci return ret; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idr_check_alloc); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_civoid tcf_idrinfo_destroy(const struct tc_action_ops *ops, 5608c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct idr *idr = &idrinfo->action_idr; 5638c2ecf20Sopenharmony_ci struct tc_action *p; 5648c2ecf20Sopenharmony_ci int ret; 5658c2ecf20Sopenharmony_ci unsigned long id = 1; 5668c2ecf20Sopenharmony_ci unsigned long tmp; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci idr_for_each_entry_ul(idr, p, tmp, id) { 5698c2ecf20Sopenharmony_ci ret = __tcf_idr_release(p, false, true); 5708c2ecf20Sopenharmony_ci if (ret == ACT_P_DELETED) 5718c2ecf20Sopenharmony_ci module_put(ops->owner); 5728c2ecf20Sopenharmony_ci else if (ret < 0) 5738c2ecf20Sopenharmony_ci return; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci idr_destroy(&idrinfo->action_idr); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_idrinfo_destroy); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic LIST_HEAD(act_base); 5808c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(act_mod_lock); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ciint tcf_register_action(struct tc_action_ops *act, 5838c2ecf20Sopenharmony_ci struct pernet_operations *ops) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct tc_action_ops *a; 5868c2ecf20Sopenharmony_ci int ret; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup) 5898c2ecf20Sopenharmony_ci return -EINVAL; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* We have to register pernet ops before making the action ops visible, 5928c2ecf20Sopenharmony_ci * otherwise tcf_action_init_1() could get a partially initialized 5938c2ecf20Sopenharmony_ci * netns. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci ret = register_pernet_subsys(ops); 5968c2ecf20Sopenharmony_ci if (ret) 5978c2ecf20Sopenharmony_ci return ret; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci write_lock(&act_mod_lock); 6008c2ecf20Sopenharmony_ci list_for_each_entry(a, &act_base, head) { 6018c2ecf20Sopenharmony_ci if (act->id == a->id || (strcmp(act->kind, a->kind) == 0)) { 6028c2ecf20Sopenharmony_ci write_unlock(&act_mod_lock); 6038c2ecf20Sopenharmony_ci unregister_pernet_subsys(ops); 6048c2ecf20Sopenharmony_ci return -EEXIST; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci list_add_tail(&act->head, &act_base); 6088c2ecf20Sopenharmony_ci write_unlock(&act_mod_lock); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_register_action); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciint tcf_unregister_action(struct tc_action_ops *act, 6158c2ecf20Sopenharmony_ci struct pernet_operations *ops) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct tc_action_ops *a; 6188c2ecf20Sopenharmony_ci int err = -ENOENT; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci write_lock(&act_mod_lock); 6218c2ecf20Sopenharmony_ci list_for_each_entry(a, &act_base, head) { 6228c2ecf20Sopenharmony_ci if (a == act) { 6238c2ecf20Sopenharmony_ci list_del(&act->head); 6248c2ecf20Sopenharmony_ci err = 0; 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci write_unlock(&act_mod_lock); 6298c2ecf20Sopenharmony_ci if (!err) 6308c2ecf20Sopenharmony_ci unregister_pernet_subsys(ops); 6318c2ecf20Sopenharmony_ci return err; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_unregister_action); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* lookup by name */ 6368c2ecf20Sopenharmony_cistatic struct tc_action_ops *tc_lookup_action_n(char *kind) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct tc_action_ops *a, *res = NULL; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (kind) { 6418c2ecf20Sopenharmony_ci read_lock(&act_mod_lock); 6428c2ecf20Sopenharmony_ci list_for_each_entry(a, &act_base, head) { 6438c2ecf20Sopenharmony_ci if (strcmp(kind, a->kind) == 0) { 6448c2ecf20Sopenharmony_ci if (try_module_get(a->owner)) 6458c2ecf20Sopenharmony_ci res = a; 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci read_unlock(&act_mod_lock); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci return res; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* lookup by nlattr */ 6558c2ecf20Sopenharmony_cistatic struct tc_action_ops *tc_lookup_action(struct nlattr *kind) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct tc_action_ops *a, *res = NULL; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (kind) { 6608c2ecf20Sopenharmony_ci read_lock(&act_mod_lock); 6618c2ecf20Sopenharmony_ci list_for_each_entry(a, &act_base, head) { 6628c2ecf20Sopenharmony_ci if (nla_strcmp(kind, a->kind) == 0) { 6638c2ecf20Sopenharmony_ci if (try_module_get(a->owner)) 6648c2ecf20Sopenharmony_ci res = a; 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci read_unlock(&act_mod_lock); 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci return res; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/*TCA_ACT_MAX_PRIO is 32, there count upto 32 */ 6748c2ecf20Sopenharmony_ci#define TCA_ACT_MAX_PRIO_MASK 0x1FF 6758c2ecf20Sopenharmony_ciint tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, 6768c2ecf20Sopenharmony_ci int nr_actions, struct tcf_result *res) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci u32 jmp_prgcnt = 0; 6798c2ecf20Sopenharmony_ci u32 jmp_ttl = TCA_ACT_MAX_PRIO; /*matches actions per filter */ 6808c2ecf20Sopenharmony_ci int i; 6818c2ecf20Sopenharmony_ci int ret = TC_ACT_OK; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (skb_skip_tc_classify(skb)) 6848c2ecf20Sopenharmony_ci return TC_ACT_OK; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cirestart_act_graph: 6878c2ecf20Sopenharmony_ci for (i = 0; i < nr_actions; i++) { 6888c2ecf20Sopenharmony_ci const struct tc_action *a = actions[i]; 6898c2ecf20Sopenharmony_ci int repeat_ttl; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (jmp_prgcnt > 0) { 6928c2ecf20Sopenharmony_ci jmp_prgcnt -= 1; 6938c2ecf20Sopenharmony_ci continue; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci repeat_ttl = 32; 6978c2ecf20Sopenharmony_cirepeat: 6988c2ecf20Sopenharmony_ci ret = a->ops->act(skb, a, res); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (unlikely(ret == TC_ACT_REPEAT)) { 7018c2ecf20Sopenharmony_ci if (--repeat_ttl != 0) 7028c2ecf20Sopenharmony_ci goto repeat; 7038c2ecf20Sopenharmony_ci /* suspicious opcode, stop pipeline */ 7048c2ecf20Sopenharmony_ci net_warn_ratelimited("TC_ACT_REPEAT abuse ?\n"); 7058c2ecf20Sopenharmony_ci return TC_ACT_OK; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (TC_ACT_EXT_CMP(ret, TC_ACT_JUMP)) { 7098c2ecf20Sopenharmony_ci jmp_prgcnt = ret & TCA_ACT_MAX_PRIO_MASK; 7108c2ecf20Sopenharmony_ci if (!jmp_prgcnt || (jmp_prgcnt > nr_actions)) { 7118c2ecf20Sopenharmony_ci /* faulty opcode, stop pipeline */ 7128c2ecf20Sopenharmony_ci return TC_ACT_OK; 7138c2ecf20Sopenharmony_ci } else { 7148c2ecf20Sopenharmony_ci jmp_ttl -= 1; 7158c2ecf20Sopenharmony_ci if (jmp_ttl > 0) 7168c2ecf20Sopenharmony_ci goto restart_act_graph; 7178c2ecf20Sopenharmony_ci else /* faulty graph, stop pipeline */ 7188c2ecf20Sopenharmony_ci return TC_ACT_OK; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) { 7218c2ecf20Sopenharmony_ci if (unlikely(!rcu_access_pointer(a->goto_chain))) { 7228c2ecf20Sopenharmony_ci net_warn_ratelimited("can't go to NULL chain!\n"); 7238c2ecf20Sopenharmony_ci return TC_ACT_SHOT; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci tcf_action_goto_chain_exec(a, res); 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (ret != TC_ACT_PIPE) 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return ret; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_action_exec); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ciint tcf_action_destroy(struct tc_action *actions[], int bind) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci const struct tc_action_ops *ops; 7398c2ecf20Sopenharmony_ci struct tc_action *a; 7408c2ecf20Sopenharmony_ci int ret = 0, i; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) { 7438c2ecf20Sopenharmony_ci a = actions[i]; 7448c2ecf20Sopenharmony_ci actions[i] = NULL; 7458c2ecf20Sopenharmony_ci ops = a->ops; 7468c2ecf20Sopenharmony_ci ret = __tcf_idr_release(a, bind, true); 7478c2ecf20Sopenharmony_ci if (ret == ACT_P_DELETED) 7488c2ecf20Sopenharmony_ci module_put(ops->owner); 7498c2ecf20Sopenharmony_ci else if (ret < 0) 7508c2ecf20Sopenharmony_ci return ret; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci return ret; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic int tcf_action_put(struct tc_action *p) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci return __tcf_action_put(p, false); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci/* Put all actions in this array, skip those NULL's. */ 7618c2ecf20Sopenharmony_cistatic void tcf_action_put_many(struct tc_action *actions[]) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci int i; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { 7668c2ecf20Sopenharmony_ci struct tc_action *a = actions[i]; 7678c2ecf20Sopenharmony_ci const struct tc_action_ops *ops; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!a) 7708c2ecf20Sopenharmony_ci continue; 7718c2ecf20Sopenharmony_ci ops = a->ops; 7728c2ecf20Sopenharmony_ci if (tcf_action_put(a)) 7738c2ecf20Sopenharmony_ci module_put(ops->owner); 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ciint 7788c2ecf20Sopenharmony_citcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci return a->ops->dump(skb, a, bind, ref); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic int 7848c2ecf20Sopenharmony_citcf_action_dump_terse(struct sk_buff *skb, struct tc_action *a) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 7878c2ecf20Sopenharmony_ci struct tc_cookie *cookie; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (nla_put_string(skb, TCA_KIND, a->ops->kind)) 7908c2ecf20Sopenharmony_ci goto nla_put_failure; 7918c2ecf20Sopenharmony_ci if (tcf_action_copy_stats(skb, a, 0)) 7928c2ecf20Sopenharmony_ci goto nla_put_failure; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci rcu_read_lock(); 7958c2ecf20Sopenharmony_ci cookie = rcu_dereference(a->act_cookie); 7968c2ecf20Sopenharmony_ci if (cookie) { 7978c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_ACT_COOKIE, cookie->len, cookie->data)) { 7988c2ecf20Sopenharmony_ci rcu_read_unlock(); 7998c2ecf20Sopenharmony_ci goto nla_put_failure; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci rcu_read_unlock(); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cinla_put_failure: 8078c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 8088c2ecf20Sopenharmony_ci return -1; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ciint 8128c2ecf20Sopenharmony_citcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci int err = -EINVAL; 8158c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 8168c2ecf20Sopenharmony_ci struct nlattr *nest; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (tcf_action_dump_terse(skb, a)) 8198c2ecf20Sopenharmony_ci goto nla_put_failure; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (a->hw_stats != TCA_ACT_HW_STATS_ANY && 8228c2ecf20Sopenharmony_ci nla_put_bitfield32(skb, TCA_ACT_HW_STATS, 8238c2ecf20Sopenharmony_ci a->hw_stats, TCA_ACT_HW_STATS_ANY)) 8248c2ecf20Sopenharmony_ci goto nla_put_failure; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (a->used_hw_stats_valid && 8278c2ecf20Sopenharmony_ci nla_put_bitfield32(skb, TCA_ACT_USED_HW_STATS, 8288c2ecf20Sopenharmony_ci a->used_hw_stats, TCA_ACT_HW_STATS_ANY)) 8298c2ecf20Sopenharmony_ci goto nla_put_failure; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (a->tcfa_flags && 8328c2ecf20Sopenharmony_ci nla_put_bitfield32(skb, TCA_ACT_FLAGS, 8338c2ecf20Sopenharmony_ci a->tcfa_flags, a->tcfa_flags)) 8348c2ecf20Sopenharmony_ci goto nla_put_failure; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 8378c2ecf20Sopenharmony_ci if (nest == NULL) 8388c2ecf20Sopenharmony_ci goto nla_put_failure; 8398c2ecf20Sopenharmony_ci err = tcf_action_dump_old(skb, a, bind, ref); 8408c2ecf20Sopenharmony_ci if (err > 0) { 8418c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 8428c2ecf20Sopenharmony_ci return err; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cinla_put_failure: 8468c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 8478c2ecf20Sopenharmony_ci return -1; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_action_dump_1); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ciint tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[], 8528c2ecf20Sopenharmony_ci int bind, int ref, bool terse) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct tc_action *a; 8558c2ecf20Sopenharmony_ci int err = -EINVAL, i; 8568c2ecf20Sopenharmony_ci struct nlattr *nest; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) { 8598c2ecf20Sopenharmony_ci a = actions[i]; 8608c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, i + 1); 8618c2ecf20Sopenharmony_ci if (nest == NULL) 8628c2ecf20Sopenharmony_ci goto nla_put_failure; 8638c2ecf20Sopenharmony_ci err = terse ? tcf_action_dump_terse(skb, a) : 8648c2ecf20Sopenharmony_ci tcf_action_dump_1(skb, a, bind, ref); 8658c2ecf20Sopenharmony_ci if (err < 0) 8668c2ecf20Sopenharmony_ci goto errout; 8678c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cinla_put_failure: 8738c2ecf20Sopenharmony_ci err = -EINVAL; 8748c2ecf20Sopenharmony_cierrout: 8758c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 8768c2ecf20Sopenharmony_ci return err; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic struct tc_cookie *nla_memdup_cookie(struct nlattr **tb) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci struct tc_cookie *c = kzalloc(sizeof(*c), GFP_KERNEL); 8828c2ecf20Sopenharmony_ci if (!c) 8838c2ecf20Sopenharmony_ci return NULL; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci c->data = nla_memdup(tb[TCA_ACT_COOKIE], GFP_KERNEL); 8868c2ecf20Sopenharmony_ci if (!c->data) { 8878c2ecf20Sopenharmony_ci kfree(c); 8888c2ecf20Sopenharmony_ci return NULL; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci c->len = nla_len(tb[TCA_ACT_COOKIE]); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return c; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic u8 tcf_action_hw_stats_get(struct nlattr *hw_stats_attr) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct nla_bitfield32 hw_stats_bf; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* If the user did not pass the attr, that means he does 9008c2ecf20Sopenharmony_ci * not care about the type. Return "any" in that case 9018c2ecf20Sopenharmony_ci * which is setting on all supported types. 9028c2ecf20Sopenharmony_ci */ 9038c2ecf20Sopenharmony_ci if (!hw_stats_attr) 9048c2ecf20Sopenharmony_ci return TCA_ACT_HW_STATS_ANY; 9058c2ecf20Sopenharmony_ci hw_stats_bf = nla_get_bitfield32(hw_stats_attr); 9068c2ecf20Sopenharmony_ci return hw_stats_bf.value; 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = { 9108c2ecf20Sopenharmony_ci [TCA_ACT_KIND] = { .type = NLA_STRING }, 9118c2ecf20Sopenharmony_ci [TCA_ACT_INDEX] = { .type = NLA_U32 }, 9128c2ecf20Sopenharmony_ci [TCA_ACT_COOKIE] = { .type = NLA_BINARY, 9138c2ecf20Sopenharmony_ci .len = TC_COOKIE_MAX_SIZE }, 9148c2ecf20Sopenharmony_ci [TCA_ACT_OPTIONS] = { .type = NLA_NESTED }, 9158c2ecf20Sopenharmony_ci [TCA_ACT_FLAGS] = NLA_POLICY_BITFIELD32(TCA_ACT_FLAGS_NO_PERCPU_STATS), 9168c2ecf20Sopenharmony_ci [TCA_ACT_HW_STATS] = NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY), 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_civoid tcf_idr_insert_many(struct tc_action *actions[]) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci int i; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { 9248c2ecf20Sopenharmony_ci struct tc_action *a = actions[i]; 9258c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (!a) 9288c2ecf20Sopenharmony_ci continue; 9298c2ecf20Sopenharmony_ci idrinfo = a->idrinfo; 9308c2ecf20Sopenharmony_ci mutex_lock(&idrinfo->lock); 9318c2ecf20Sopenharmony_ci /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if 9328c2ecf20Sopenharmony_ci * it is just created, otherwise this is just a nop. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci idr_replace(&idrinfo->action_idr, a, a->tcfa_index); 9358c2ecf20Sopenharmony_ci mutex_unlock(&idrinfo->lock); 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistruct tc_action_ops *tc_action_load_ops(char *name, struct nlattr *nla, 9408c2ecf20Sopenharmony_ci bool rtnl_held, 9418c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX + 1]; 9448c2ecf20Sopenharmony_ci struct tc_action_ops *a_o; 9458c2ecf20Sopenharmony_ci char act_name[IFNAMSIZ]; 9468c2ecf20Sopenharmony_ci struct nlattr *kind; 9478c2ecf20Sopenharmony_ci int err; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (name == NULL) { 9508c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla, 9518c2ecf20Sopenharmony_ci tcf_action_policy, extack); 9528c2ecf20Sopenharmony_ci if (err < 0) 9538c2ecf20Sopenharmony_ci return ERR_PTR(err); 9548c2ecf20Sopenharmony_ci err = -EINVAL; 9558c2ecf20Sopenharmony_ci kind = tb[TCA_ACT_KIND]; 9568c2ecf20Sopenharmony_ci if (!kind) { 9578c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "TC action kind must be specified"); 9588c2ecf20Sopenharmony_ci return ERR_PTR(err); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ) { 9618c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "TC action name too long"); 9628c2ecf20Sopenharmony_ci return ERR_PTR(err); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci } else { 9658c2ecf20Sopenharmony_ci if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ) { 9668c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "TC action name too long"); 9678c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci a_o = tc_lookup_action_n(act_name); 9728c2ecf20Sopenharmony_ci if (a_o == NULL) { 9738c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 9748c2ecf20Sopenharmony_ci if (rtnl_held) 9758c2ecf20Sopenharmony_ci rtnl_unlock(); 9768c2ecf20Sopenharmony_ci request_module("act_%s", act_name); 9778c2ecf20Sopenharmony_ci if (rtnl_held) 9788c2ecf20Sopenharmony_ci rtnl_lock(); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci a_o = tc_lookup_action_n(act_name); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* We dropped the RTNL semaphore in order to 9838c2ecf20Sopenharmony_ci * perform the module load. So, even if we 9848c2ecf20Sopenharmony_ci * succeeded in loading the module we have to 9858c2ecf20Sopenharmony_ci * tell the caller to replay the request. We 9868c2ecf20Sopenharmony_ci * indicate this using -EAGAIN. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ci if (a_o != NULL) { 9898c2ecf20Sopenharmony_ci module_put(a_o->owner); 9908c2ecf20Sopenharmony_ci return ERR_PTR(-EAGAIN); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci#endif 9938c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to load TC action module"); 9948c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return a_o; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistruct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, 10018c2ecf20Sopenharmony_ci struct nlattr *nla, struct nlattr *est, 10028c2ecf20Sopenharmony_ci char *name, int ovr, int bind, 10038c2ecf20Sopenharmony_ci struct tc_action_ops *a_o, int *init_res, 10048c2ecf20Sopenharmony_ci bool rtnl_held, 10058c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct nla_bitfield32 flags = { 0, 0 }; 10088c2ecf20Sopenharmony_ci u8 hw_stats = TCA_ACT_HW_STATS_ANY; 10098c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX + 1]; 10108c2ecf20Sopenharmony_ci struct tc_cookie *cookie = NULL; 10118c2ecf20Sopenharmony_ci struct tc_action *a; 10128c2ecf20Sopenharmony_ci int err; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* backward compatibility for policer */ 10158c2ecf20Sopenharmony_ci if (name == NULL) { 10168c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla, 10178c2ecf20Sopenharmony_ci tcf_action_policy, extack); 10188c2ecf20Sopenharmony_ci if (err < 0) 10198c2ecf20Sopenharmony_ci return ERR_PTR(err); 10208c2ecf20Sopenharmony_ci if (tb[TCA_ACT_COOKIE]) { 10218c2ecf20Sopenharmony_ci cookie = nla_memdup_cookie(tb); 10228c2ecf20Sopenharmony_ci if (!cookie) { 10238c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "No memory to generate TC cookie"); 10248c2ecf20Sopenharmony_ci err = -ENOMEM; 10258c2ecf20Sopenharmony_ci goto err_out; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]); 10298c2ecf20Sopenharmony_ci if (tb[TCA_ACT_FLAGS]) 10308c2ecf20Sopenharmony_ci flags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind, 10338c2ecf20Sopenharmony_ci rtnl_held, tp, flags.value, extack); 10348c2ecf20Sopenharmony_ci } else { 10358c2ecf20Sopenharmony_ci err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held, 10368c2ecf20Sopenharmony_ci tp, flags.value, extack); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci if (err < 0) 10398c2ecf20Sopenharmony_ci goto err_out; 10408c2ecf20Sopenharmony_ci *init_res = err; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (!name && tb[TCA_ACT_COOKIE]) 10438c2ecf20Sopenharmony_ci tcf_set_action_cookie(&a->act_cookie, cookie); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (!name) 10468c2ecf20Sopenharmony_ci a->hw_stats = hw_stats; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci return a; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cierr_out: 10518c2ecf20Sopenharmony_ci if (cookie) { 10528c2ecf20Sopenharmony_ci kfree(cookie->data); 10538c2ecf20Sopenharmony_ci kfree(cookie); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci return ERR_PTR(err); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/* Returns numbers of initialized actions or negative error. */ 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ciint tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla, 10618c2ecf20Sopenharmony_ci struct nlattr *est, char *name, int ovr, int bind, 10628c2ecf20Sopenharmony_ci struct tc_action *actions[], int init_res[], size_t *attr_size, 10638c2ecf20Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {}; 10668c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; 10678c2ecf20Sopenharmony_ci struct tc_action *act; 10688c2ecf20Sopenharmony_ci size_t sz = 0; 10698c2ecf20Sopenharmony_ci int err; 10708c2ecf20Sopenharmony_ci int i; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX_PRIO, nla, NULL, 10738c2ecf20Sopenharmony_ci extack); 10748c2ecf20Sopenharmony_ci if (err < 0) 10758c2ecf20Sopenharmony_ci return err; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { 10788c2ecf20Sopenharmony_ci struct tc_action_ops *a_o; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci a_o = tc_action_load_ops(name, tb[i], rtnl_held, extack); 10818c2ecf20Sopenharmony_ci if (IS_ERR(a_o)) { 10828c2ecf20Sopenharmony_ci err = PTR_ERR(a_o); 10838c2ecf20Sopenharmony_ci goto err_mod; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci ops[i - 1] = a_o; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { 10898c2ecf20Sopenharmony_ci act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind, 10908c2ecf20Sopenharmony_ci ops[i - 1], &init_res[i - 1], rtnl_held, 10918c2ecf20Sopenharmony_ci extack); 10928c2ecf20Sopenharmony_ci if (IS_ERR(act)) { 10938c2ecf20Sopenharmony_ci err = PTR_ERR(act); 10948c2ecf20Sopenharmony_ci goto err; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci sz += tcf_action_fill_size(act); 10978c2ecf20Sopenharmony_ci /* Start from index 0 */ 10988c2ecf20Sopenharmony_ci actions[i - 1] = act; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* We have to commit them all together, because if any error happened in 11028c2ecf20Sopenharmony_ci * between, we could not handle the failure gracefully. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci tcf_idr_insert_many(actions); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci *attr_size = tcf_action_full_attrs_size(sz); 11078c2ecf20Sopenharmony_ci err = i - 1; 11088c2ecf20Sopenharmony_ci goto err_mod; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cierr: 11118c2ecf20Sopenharmony_ci tcf_action_destroy(actions, bind); 11128c2ecf20Sopenharmony_cierr_mod: 11138c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { 11148c2ecf20Sopenharmony_ci if (ops[i]) 11158c2ecf20Sopenharmony_ci module_put(ops[i]->owner); 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci return err; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_civoid tcf_action_update_stats(struct tc_action *a, u64 bytes, u64 packets, 11218c2ecf20Sopenharmony_ci u64 drops, bool hw) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci if (a->cpu_bstats) { 11248c2ecf20Sopenharmony_ci _bstats_cpu_update(this_cpu_ptr(a->cpu_bstats), bytes, packets); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci this_cpu_ptr(a->cpu_qstats)->drops += drops; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (hw) 11298c2ecf20Sopenharmony_ci _bstats_cpu_update(this_cpu_ptr(a->cpu_bstats_hw), 11308c2ecf20Sopenharmony_ci bytes, packets); 11318c2ecf20Sopenharmony_ci return; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci _bstats_update(&a->tcfa_bstats, bytes, packets); 11358c2ecf20Sopenharmony_ci a->tcfa_qstats.drops += drops; 11368c2ecf20Sopenharmony_ci if (hw) 11378c2ecf20Sopenharmony_ci _bstats_update(&a->tcfa_bstats_hw, bytes, packets); 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tcf_action_update_stats); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ciint tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p, 11428c2ecf20Sopenharmony_ci int compat_mode) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci int err = 0; 11458c2ecf20Sopenharmony_ci struct gnet_dump d; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (p == NULL) 11488c2ecf20Sopenharmony_ci goto errout; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* compat_mode being true specifies a call that is supposed 11518c2ecf20Sopenharmony_ci * to add additional backward compatibility statistic TLVs. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ci if (compat_mode) { 11548c2ecf20Sopenharmony_ci if (p->type == TCA_OLD_COMPAT) 11558c2ecf20Sopenharmony_ci err = gnet_stats_start_copy_compat(skb, 0, 11568c2ecf20Sopenharmony_ci TCA_STATS, 11578c2ecf20Sopenharmony_ci TCA_XSTATS, 11588c2ecf20Sopenharmony_ci &p->tcfa_lock, &d, 11598c2ecf20Sopenharmony_ci TCA_PAD); 11608c2ecf20Sopenharmony_ci else 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci } else 11638c2ecf20Sopenharmony_ci err = gnet_stats_start_copy(skb, TCA_ACT_STATS, 11648c2ecf20Sopenharmony_ci &p->tcfa_lock, &d, TCA_ACT_PAD); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (err < 0) 11678c2ecf20Sopenharmony_ci goto errout; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (gnet_stats_copy_basic(NULL, &d, p->cpu_bstats, &p->tcfa_bstats) < 0 || 11708c2ecf20Sopenharmony_ci gnet_stats_copy_basic_hw(NULL, &d, p->cpu_bstats_hw, 11718c2ecf20Sopenharmony_ci &p->tcfa_bstats_hw) < 0 || 11728c2ecf20Sopenharmony_ci gnet_stats_copy_rate_est(&d, &p->tcfa_rate_est) < 0 || 11738c2ecf20Sopenharmony_ci gnet_stats_copy_queue(&d, p->cpu_qstats, 11748c2ecf20Sopenharmony_ci &p->tcfa_qstats, 11758c2ecf20Sopenharmony_ci p->tcfa_qstats.qlen) < 0) 11768c2ecf20Sopenharmony_ci goto errout; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (gnet_stats_finish_copy(&d) < 0) 11798c2ecf20Sopenharmony_ci goto errout; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci return 0; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cierrout: 11848c2ecf20Sopenharmony_ci return -1; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cistatic int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], 11888c2ecf20Sopenharmony_ci u32 portid, u32 seq, u16 flags, int event, int bind, 11898c2ecf20Sopenharmony_ci int ref) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci struct tcamsg *t; 11928c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 11938c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 11948c2ecf20Sopenharmony_ci struct nlattr *nest; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags); 11978c2ecf20Sopenharmony_ci if (!nlh) 11988c2ecf20Sopenharmony_ci goto out_nlmsg_trim; 11998c2ecf20Sopenharmony_ci t = nlmsg_data(nlh); 12008c2ecf20Sopenharmony_ci t->tca_family = AF_UNSPEC; 12018c2ecf20Sopenharmony_ci t->tca__pad1 = 0; 12028c2ecf20Sopenharmony_ci t->tca__pad2 = 0; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_ACT_TAB); 12058c2ecf20Sopenharmony_ci if (!nest) 12068c2ecf20Sopenharmony_ci goto out_nlmsg_trim; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (tcf_action_dump(skb, actions, bind, ref, false) < 0) 12098c2ecf20Sopenharmony_ci goto out_nlmsg_trim; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 12148c2ecf20Sopenharmony_ci return skb->len; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ciout_nlmsg_trim: 12178c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 12188c2ecf20Sopenharmony_ci return -1; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int 12228c2ecf20Sopenharmony_citcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, 12238c2ecf20Sopenharmony_ci struct tc_action *actions[], int event, 12248c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct sk_buff *skb; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 12298c2ecf20Sopenharmony_ci if (!skb) 12308c2ecf20Sopenharmony_ci return -ENOBUFS; 12318c2ecf20Sopenharmony_ci if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, 12328c2ecf20Sopenharmony_ci 0, 1) <= 0) { 12338c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action"); 12348c2ecf20Sopenharmony_ci kfree_skb(skb); 12358c2ecf20Sopenharmony_ci return -EINVAL; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci return rtnl_unicast(skb, net, portid); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla, 12428c2ecf20Sopenharmony_ci struct nlmsghdr *n, u32 portid, 12438c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX + 1]; 12468c2ecf20Sopenharmony_ci const struct tc_action_ops *ops; 12478c2ecf20Sopenharmony_ci struct tc_action *a; 12488c2ecf20Sopenharmony_ci int index; 12498c2ecf20Sopenharmony_ci int err; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla, 12528c2ecf20Sopenharmony_ci tcf_action_policy, extack); 12538c2ecf20Sopenharmony_ci if (err < 0) 12548c2ecf20Sopenharmony_ci goto err_out; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci err = -EINVAL; 12578c2ecf20Sopenharmony_ci if (tb[TCA_ACT_INDEX] == NULL || 12588c2ecf20Sopenharmony_ci nla_len(tb[TCA_ACT_INDEX]) < sizeof(index)) { 12598c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid TC action index value"); 12608c2ecf20Sopenharmony_ci goto err_out; 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci index = nla_get_u32(tb[TCA_ACT_INDEX]); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci err = -EINVAL; 12658c2ecf20Sopenharmony_ci ops = tc_lookup_action(tb[TCA_ACT_KIND]); 12668c2ecf20Sopenharmony_ci if (!ops) { /* could happen in batch of actions */ 12678c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified TC action kind not found"); 12688c2ecf20Sopenharmony_ci goto err_out; 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci err = -ENOENT; 12718c2ecf20Sopenharmony_ci if (ops->lookup(net, &a, index) == 0) { 12728c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "TC action with specified index not found"); 12738c2ecf20Sopenharmony_ci goto err_mod; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci module_put(ops->owner); 12778c2ecf20Sopenharmony_ci return a; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cierr_mod: 12808c2ecf20Sopenharmony_ci module_put(ops->owner); 12818c2ecf20Sopenharmony_cierr_out: 12828c2ecf20Sopenharmony_ci return ERR_PTR(err); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cistatic int tca_action_flush(struct net *net, struct nlattr *nla, 12868c2ecf20Sopenharmony_ci struct nlmsghdr *n, u32 portid, 12878c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct sk_buff *skb; 12908c2ecf20Sopenharmony_ci unsigned char *b; 12918c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 12928c2ecf20Sopenharmony_ci struct tcamsg *t; 12938c2ecf20Sopenharmony_ci struct netlink_callback dcb; 12948c2ecf20Sopenharmony_ci struct nlattr *nest; 12958c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX + 1]; 12968c2ecf20Sopenharmony_ci const struct tc_action_ops *ops; 12978c2ecf20Sopenharmony_ci struct nlattr *kind; 12988c2ecf20Sopenharmony_ci int err = -ENOMEM; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 13018c2ecf20Sopenharmony_ci if (!skb) 13028c2ecf20Sopenharmony_ci return err; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci b = skb_tail_pointer(skb); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla, 13078c2ecf20Sopenharmony_ci tcf_action_policy, extack); 13088c2ecf20Sopenharmony_ci if (err < 0) 13098c2ecf20Sopenharmony_ci goto err_out; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci err = -EINVAL; 13128c2ecf20Sopenharmony_ci kind = tb[TCA_ACT_KIND]; 13138c2ecf20Sopenharmony_ci ops = tc_lookup_action(kind); 13148c2ecf20Sopenharmony_ci if (!ops) { /*some idjot trying to flush unknown action */ 13158c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot flush unknown TC action"); 13168c2ecf20Sopenharmony_ci goto err_out; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION, 13208c2ecf20Sopenharmony_ci sizeof(*t), 0); 13218c2ecf20Sopenharmony_ci if (!nlh) { 13228c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to create TC action flush notification"); 13238c2ecf20Sopenharmony_ci goto out_module_put; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci t = nlmsg_data(nlh); 13268c2ecf20Sopenharmony_ci t->tca_family = AF_UNSPEC; 13278c2ecf20Sopenharmony_ci t->tca__pad1 = 0; 13288c2ecf20Sopenharmony_ci t->tca__pad2 = 0; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_ACT_TAB); 13318c2ecf20Sopenharmony_ci if (!nest) { 13328c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to add new netlink message"); 13338c2ecf20Sopenharmony_ci goto out_module_put; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci err = ops->walk(net, skb, &dcb, RTM_DELACTION, ops, extack); 13378c2ecf20Sopenharmony_ci if (err <= 0) { 13388c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 13398c2ecf20Sopenharmony_ci goto out_module_put; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 13458c2ecf20Sopenharmony_ci nlh->nlmsg_flags |= NLM_F_ROOT; 13468c2ecf20Sopenharmony_ci module_put(ops->owner); 13478c2ecf20Sopenharmony_ci err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 13488c2ecf20Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 13498c2ecf20Sopenharmony_ci if (err > 0) 13508c2ecf20Sopenharmony_ci return 0; 13518c2ecf20Sopenharmony_ci if (err < 0) 13528c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to send TC action flush notification"); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci return err; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ciout_module_put: 13578c2ecf20Sopenharmony_ci module_put(ops->owner); 13588c2ecf20Sopenharmony_cierr_out: 13598c2ecf20Sopenharmony_ci kfree_skb(skb); 13608c2ecf20Sopenharmony_ci return err; 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic int tcf_action_delete(struct net *net, struct tc_action *actions[]) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci int i; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) { 13688c2ecf20Sopenharmony_ci struct tc_action *a = actions[i]; 13698c2ecf20Sopenharmony_ci const struct tc_action_ops *ops = a->ops; 13708c2ecf20Sopenharmony_ci /* Actions can be deleted concurrently so we must save their 13718c2ecf20Sopenharmony_ci * type and id to search again after reference is released. 13728c2ecf20Sopenharmony_ci */ 13738c2ecf20Sopenharmony_ci struct tcf_idrinfo *idrinfo = a->idrinfo; 13748c2ecf20Sopenharmony_ci u32 act_index = a->tcfa_index; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci actions[i] = NULL; 13778c2ecf20Sopenharmony_ci if (tcf_action_put(a)) { 13788c2ecf20Sopenharmony_ci /* last reference, action was deleted concurrently */ 13798c2ecf20Sopenharmony_ci module_put(ops->owner); 13808c2ecf20Sopenharmony_ci } else { 13818c2ecf20Sopenharmony_ci int ret; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci /* now do the delete */ 13848c2ecf20Sopenharmony_ci ret = tcf_idr_delete_index(idrinfo, act_index); 13858c2ecf20Sopenharmony_ci if (ret < 0) 13868c2ecf20Sopenharmony_ci return ret; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci return 0; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cistatic int 13938c2ecf20Sopenharmony_citcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[], 13948c2ecf20Sopenharmony_ci u32 portid, size_t attr_size, struct netlink_ext_ack *extack) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci int ret; 13978c2ecf20Sopenharmony_ci struct sk_buff *skb; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size, 14008c2ecf20Sopenharmony_ci GFP_KERNEL); 14018c2ecf20Sopenharmony_ci if (!skb) 14028c2ecf20Sopenharmony_ci return -ENOBUFS; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION, 14058c2ecf20Sopenharmony_ci 0, 2) <= 0) { 14068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes"); 14078c2ecf20Sopenharmony_ci kfree_skb(skb); 14088c2ecf20Sopenharmony_ci return -EINVAL; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* now do the delete */ 14128c2ecf20Sopenharmony_ci ret = tcf_action_delete(net, actions); 14138c2ecf20Sopenharmony_ci if (ret < 0) { 14148c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to delete TC action"); 14158c2ecf20Sopenharmony_ci kfree_skb(skb); 14168c2ecf20Sopenharmony_ci return ret; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 14208c2ecf20Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 14218c2ecf20Sopenharmony_ci if (ret > 0) 14228c2ecf20Sopenharmony_ci return 0; 14238c2ecf20Sopenharmony_ci return ret; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int 14278c2ecf20Sopenharmony_citca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, 14288c2ecf20Sopenharmony_ci u32 portid, int event, struct netlink_ext_ack *extack) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci int i, ret; 14318c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; 14328c2ecf20Sopenharmony_ci struct tc_action *act; 14338c2ecf20Sopenharmony_ci size_t attr_size = 0; 14348c2ecf20Sopenharmony_ci struct tc_action *actions[TCA_ACT_MAX_PRIO] = {}; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ret = nla_parse_nested_deprecated(tb, TCA_ACT_MAX_PRIO, nla, NULL, 14378c2ecf20Sopenharmony_ci extack); 14388c2ecf20Sopenharmony_ci if (ret < 0) 14398c2ecf20Sopenharmony_ci return ret; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) { 14428c2ecf20Sopenharmony_ci if (tb[1]) 14438c2ecf20Sopenharmony_ci return tca_action_flush(net, tb[1], n, portid, extack); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid netlink attributes while flushing TC action"); 14468c2ecf20Sopenharmony_ci return -EINVAL; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { 14508c2ecf20Sopenharmony_ci act = tcf_action_get_1(net, tb[i], n, portid, extack); 14518c2ecf20Sopenharmony_ci if (IS_ERR(act)) { 14528c2ecf20Sopenharmony_ci ret = PTR_ERR(act); 14538c2ecf20Sopenharmony_ci goto err; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci attr_size += tcf_action_fill_size(act); 14568c2ecf20Sopenharmony_ci actions[i - 1] = act; 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci attr_size = tcf_action_full_attrs_size(attr_size); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (event == RTM_GETACTION) 14628c2ecf20Sopenharmony_ci ret = tcf_get_notify(net, portid, n, actions, event, extack); 14638c2ecf20Sopenharmony_ci else { /* delete */ 14648c2ecf20Sopenharmony_ci ret = tcf_del_notify(net, n, actions, portid, attr_size, extack); 14658c2ecf20Sopenharmony_ci if (ret) 14668c2ecf20Sopenharmony_ci goto err; 14678c2ecf20Sopenharmony_ci return 0; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_cierr: 14708c2ecf20Sopenharmony_ci tcf_action_put_many(actions); 14718c2ecf20Sopenharmony_ci return ret; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic int 14758c2ecf20Sopenharmony_citcf_add_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[], 14768c2ecf20Sopenharmony_ci u32 portid, size_t attr_size, struct netlink_ext_ack *extack) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci struct sk_buff *skb; 14798c2ecf20Sopenharmony_ci int err = 0; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size, 14828c2ecf20Sopenharmony_ci GFP_KERNEL); 14838c2ecf20Sopenharmony_ci if (!skb) 14848c2ecf20Sopenharmony_ci return -ENOBUFS; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags, 14878c2ecf20Sopenharmony_ci RTM_NEWACTION, 0, 0) <= 0) { 14888c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action"); 14898c2ecf20Sopenharmony_ci kfree_skb(skb); 14908c2ecf20Sopenharmony_ci return -EINVAL; 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 14948c2ecf20Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 14958c2ecf20Sopenharmony_ci if (err > 0) 14968c2ecf20Sopenharmony_ci err = 0; 14978c2ecf20Sopenharmony_ci return err; 14988c2ecf20Sopenharmony_ci} 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cistatic int tcf_action_add(struct net *net, struct nlattr *nla, 15018c2ecf20Sopenharmony_ci struct nlmsghdr *n, u32 portid, int ovr, 15028c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci size_t attr_size = 0; 15058c2ecf20Sopenharmony_ci int loop, ret, i; 15068c2ecf20Sopenharmony_ci struct tc_action *actions[TCA_ACT_MAX_PRIO] = {}; 15078c2ecf20Sopenharmony_ci int init_res[TCA_ACT_MAX_PRIO] = {}; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci for (loop = 0; loop < 10; loop++) { 15108c2ecf20Sopenharmony_ci ret = tcf_action_init(net, NULL, nla, NULL, NULL, ovr, 0, 15118c2ecf20Sopenharmony_ci actions, init_res, &attr_size, true, extack); 15128c2ecf20Sopenharmony_ci if (ret != -EAGAIN) 15138c2ecf20Sopenharmony_ci break; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (ret < 0) 15178c2ecf20Sopenharmony_ci return ret; 15188c2ecf20Sopenharmony_ci ret = tcf_add_notify(net, n, actions, portid, attr_size, extack); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci /* only put existing actions */ 15218c2ecf20Sopenharmony_ci for (i = 0; i < TCA_ACT_MAX_PRIO; i++) 15228c2ecf20Sopenharmony_ci if (init_res[i] == ACT_P_CREATED) 15238c2ecf20Sopenharmony_ci actions[i] = NULL; 15248c2ecf20Sopenharmony_ci tcf_action_put_many(actions); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci return ret; 15278c2ecf20Sopenharmony_ci} 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cistatic const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = { 15308c2ecf20Sopenharmony_ci [TCA_ROOT_FLAGS] = NLA_POLICY_BITFIELD32(TCA_FLAG_LARGE_DUMP_ON), 15318c2ecf20Sopenharmony_ci [TCA_ROOT_TIME_DELTA] = { .type = NLA_U32 }, 15328c2ecf20Sopenharmony_ci}; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_cistatic int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, 15358c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 15388c2ecf20Sopenharmony_ci struct nlattr *tca[TCA_ROOT_MAX + 1]; 15398c2ecf20Sopenharmony_ci u32 portid = NETLINK_CB(skb).portid; 15408c2ecf20Sopenharmony_ci int ret = 0, ovr = 0; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if ((n->nlmsg_type != RTM_GETACTION) && 15438c2ecf20Sopenharmony_ci !netlink_capable(skb, CAP_NET_ADMIN)) 15448c2ecf20Sopenharmony_ci return -EPERM; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci ret = nlmsg_parse_deprecated(n, sizeof(struct tcamsg), tca, 15478c2ecf20Sopenharmony_ci TCA_ROOT_MAX, NULL, extack); 15488c2ecf20Sopenharmony_ci if (ret < 0) 15498c2ecf20Sopenharmony_ci return ret; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (tca[TCA_ACT_TAB] == NULL) { 15528c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Netlink action attributes missing"); 15538c2ecf20Sopenharmony_ci return -EINVAL; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* n->nlmsg_flags & NLM_F_CREATE */ 15578c2ecf20Sopenharmony_ci switch (n->nlmsg_type) { 15588c2ecf20Sopenharmony_ci case RTM_NEWACTION: 15598c2ecf20Sopenharmony_ci /* we are going to assume all other flags 15608c2ecf20Sopenharmony_ci * imply create only if it doesn't exist 15618c2ecf20Sopenharmony_ci * Note that CREATE | EXCL implies that 15628c2ecf20Sopenharmony_ci * but since we want avoid ambiguity (eg when flags 15638c2ecf20Sopenharmony_ci * is zero) then just set this 15648c2ecf20Sopenharmony_ci */ 15658c2ecf20Sopenharmony_ci if (n->nlmsg_flags & NLM_F_REPLACE) 15668c2ecf20Sopenharmony_ci ovr = 1; 15678c2ecf20Sopenharmony_ci ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, ovr, 15688c2ecf20Sopenharmony_ci extack); 15698c2ecf20Sopenharmony_ci break; 15708c2ecf20Sopenharmony_ci case RTM_DELACTION: 15718c2ecf20Sopenharmony_ci ret = tca_action_gd(net, tca[TCA_ACT_TAB], n, 15728c2ecf20Sopenharmony_ci portid, RTM_DELACTION, extack); 15738c2ecf20Sopenharmony_ci break; 15748c2ecf20Sopenharmony_ci case RTM_GETACTION: 15758c2ecf20Sopenharmony_ci ret = tca_action_gd(net, tca[TCA_ACT_TAB], n, 15768c2ecf20Sopenharmony_ci portid, RTM_GETACTION, extack); 15778c2ecf20Sopenharmony_ci break; 15788c2ecf20Sopenharmony_ci default: 15798c2ecf20Sopenharmony_ci BUG(); 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci return ret; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_cistatic struct nlattr *find_dump_kind(struct nlattr **nla) 15868c2ecf20Sopenharmony_ci{ 15878c2ecf20Sopenharmony_ci struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1]; 15888c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; 15898c2ecf20Sopenharmony_ci struct nlattr *kind; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci tb1 = nla[TCA_ACT_TAB]; 15928c2ecf20Sopenharmony_ci if (tb1 == NULL) 15938c2ecf20Sopenharmony_ci return NULL; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci if (nla_parse_deprecated(tb, TCA_ACT_MAX_PRIO, nla_data(tb1), NLMSG_ALIGN(nla_len(tb1)), NULL, NULL) < 0) 15968c2ecf20Sopenharmony_ci return NULL; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (tb[1] == NULL) 15998c2ecf20Sopenharmony_ci return NULL; 16008c2ecf20Sopenharmony_ci if (nla_parse_nested_deprecated(tb2, TCA_ACT_MAX, tb[1], tcf_action_policy, NULL) < 0) 16018c2ecf20Sopenharmony_ci return NULL; 16028c2ecf20Sopenharmony_ci kind = tb2[TCA_ACT_KIND]; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci return kind; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 16108c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 16118c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 16128c2ecf20Sopenharmony_ci struct nlattr *nest; 16138c2ecf20Sopenharmony_ci struct tc_action_ops *a_o; 16148c2ecf20Sopenharmony_ci int ret = 0; 16158c2ecf20Sopenharmony_ci struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh); 16168c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_ROOT_MAX + 1]; 16178c2ecf20Sopenharmony_ci struct nlattr *count_attr = NULL; 16188c2ecf20Sopenharmony_ci unsigned long jiffy_since = 0; 16198c2ecf20Sopenharmony_ci struct nlattr *kind = NULL; 16208c2ecf20Sopenharmony_ci struct nla_bitfield32 bf; 16218c2ecf20Sopenharmony_ci u32 msecs_since = 0; 16228c2ecf20Sopenharmony_ci u32 act_count = 0; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci ret = nlmsg_parse_deprecated(cb->nlh, sizeof(struct tcamsg), tb, 16258c2ecf20Sopenharmony_ci TCA_ROOT_MAX, tcaa_policy, cb->extack); 16268c2ecf20Sopenharmony_ci if (ret < 0) 16278c2ecf20Sopenharmony_ci return ret; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci kind = find_dump_kind(tb); 16308c2ecf20Sopenharmony_ci if (kind == NULL) { 16318c2ecf20Sopenharmony_ci pr_info("tc_dump_action: action bad kind\n"); 16328c2ecf20Sopenharmony_ci return 0; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci a_o = tc_lookup_action(kind); 16368c2ecf20Sopenharmony_ci if (a_o == NULL) 16378c2ecf20Sopenharmony_ci return 0; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci cb->args[2] = 0; 16408c2ecf20Sopenharmony_ci if (tb[TCA_ROOT_FLAGS]) { 16418c2ecf20Sopenharmony_ci bf = nla_get_bitfield32(tb[TCA_ROOT_FLAGS]); 16428c2ecf20Sopenharmony_ci cb->args[2] = bf.value; 16438c2ecf20Sopenharmony_ci } 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci if (tb[TCA_ROOT_TIME_DELTA]) { 16468c2ecf20Sopenharmony_ci msecs_since = nla_get_u32(tb[TCA_ROOT_TIME_DELTA]); 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 16508c2ecf20Sopenharmony_ci cb->nlh->nlmsg_type, sizeof(*t), 0); 16518c2ecf20Sopenharmony_ci if (!nlh) 16528c2ecf20Sopenharmony_ci goto out_module_put; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (msecs_since) 16558c2ecf20Sopenharmony_ci jiffy_since = jiffies - msecs_to_jiffies(msecs_since); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci t = nlmsg_data(nlh); 16588c2ecf20Sopenharmony_ci t->tca_family = AF_UNSPEC; 16598c2ecf20Sopenharmony_ci t->tca__pad1 = 0; 16608c2ecf20Sopenharmony_ci t->tca__pad2 = 0; 16618c2ecf20Sopenharmony_ci cb->args[3] = jiffy_since; 16628c2ecf20Sopenharmony_ci count_attr = nla_reserve(skb, TCA_ROOT_COUNT, sizeof(u32)); 16638c2ecf20Sopenharmony_ci if (!count_attr) 16648c2ecf20Sopenharmony_ci goto out_module_put; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_ACT_TAB); 16678c2ecf20Sopenharmony_ci if (nest == NULL) 16688c2ecf20Sopenharmony_ci goto out_module_put; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci ret = a_o->walk(net, skb, cb, RTM_GETACTION, a_o, NULL); 16718c2ecf20Sopenharmony_ci if (ret < 0) 16728c2ecf20Sopenharmony_ci goto out_module_put; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (ret > 0) { 16758c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 16768c2ecf20Sopenharmony_ci ret = skb->len; 16778c2ecf20Sopenharmony_ci act_count = cb->args[1]; 16788c2ecf20Sopenharmony_ci memcpy(nla_data(count_attr), &act_count, sizeof(u32)); 16798c2ecf20Sopenharmony_ci cb->args[1] = 0; 16808c2ecf20Sopenharmony_ci } else 16818c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 16848c2ecf20Sopenharmony_ci if (NETLINK_CB(cb->skb).portid && ret) 16858c2ecf20Sopenharmony_ci nlh->nlmsg_flags |= NLM_F_MULTI; 16868c2ecf20Sopenharmony_ci module_put(a_o->owner); 16878c2ecf20Sopenharmony_ci return skb->len; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ciout_module_put: 16908c2ecf20Sopenharmony_ci module_put(a_o->owner); 16918c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 16928c2ecf20Sopenharmony_ci return skb->len; 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_cistatic int __init tc_action_init(void) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0); 16988c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0); 16998c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, 17008c2ecf20Sopenharmony_ci 0); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci return 0; 17038c2ecf20Sopenharmony_ci} 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_cisubsys_initcall(tc_action_init); 1706