// SPDX-License-Identifier: GPL-2.0-or-later
/*
* net/sched/act_api.c Packet action API.
*
* Author: Jamal Hadi Salim
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/err.h>
#include <linux/module.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/sch_generic.h>
#include <net/pkt_cls.h>
#include <net/act_api.h>
#include <net/netlink.h>
#ifdef CONFIG_INET
DEFINE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
EXPORT_SYMBOL_GPL(tcf_frag_xmit_count);
#endif
int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb))
{
#ifdef CONFIG_INET
if (static_branch_unlikely(&tcf_frag_xmit_count))
return sch_frag_xmit_hook(skb, xmit);
#endif
return xmit(skb);
}
EXPORT_SYMBOL_GPL(tcf_dev_queue_xmit);
static void tcf_action_goto_chain_exec(const struct tc_action *a,
struct tcf_result *res)
{
const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain);
res->goto_tp = rcu_dereference_bh(chain->filter_chain);
}
static void tcf_free_cookie_rcu(struct rcu_head *p)
{
struct tc_cookie *cookie = container_of(p, struct tc_cookie, rcu);
kfree(cookie->data);
kfree(cookie);
}
static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
struct tc_cookie *new_cookie)
{
struct tc_cookie *old;
old = xchg((__force struct tc_cookie **)old_cookie, new_cookie);
if (old)
call_rcu(&old->rcu, tcf_free_cookie_rcu);
}
int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
struct tcf_chain **newchain,
struct netlink_ext_ack *extack)
{
int opcode = TC_ACT_EXT_OPCODE(action), ret = -EINVAL;
u32 chain_index;
if (!opcode)
ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
else if (opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC)
ret = 0;
if (ret) {
NL_SET_ERR_MSG(extack, "invalid control action");
goto end;
}
if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) {
chain_index = action & TC_ACT_EXT_VAL_MASK;
if (!tp || !newchain) {
ret = -EINVAL;
NL_SET_ERR_MSG(extack,
"can't goto NULL proto/chain");
goto end;
}
*newchain = tcf_chain_get_by_act(tp->chain->block, chain_index);
if (!*newchain) {
ret = -ENOMEM;
NL_SET_ERR_MSG(extack,
"can't allocate goto_chain");
}
}
end:
return ret;
}
EXPORT_SYMBOL
|