xref: /kernel/linux/linux-5.10/net/sched/act_mirred.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/act_mirred.c	packet mirroring and redirect actions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:	Jamal Hadi Salim (2002-4)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * TODO: Add ingress support (and socket redirect support)
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
158c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/gfp.h>
198c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
208c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
218c2ecf20Sopenharmony_ci#include <net/netlink.h>
228c2ecf20Sopenharmony_ci#include <net/dst.h>
238c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
248c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
258c2ecf20Sopenharmony_ci#include <linux/tc_act/tc_mirred.h>
268c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mirred.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic LIST_HEAD(mirred_list);
298c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mirred_list_lock);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define MIRRED_NEST_LIMIT    4
328c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(unsigned int, mirred_nest_level);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic bool tcf_mirred_is_act_redirect(int action)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	return action == TCA_EGRESS_REDIR || action == TCA_INGRESS_REDIR;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic bool tcf_mirred_act_wants_ingress(int action)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	switch (action) {
428c2ecf20Sopenharmony_ci	case TCA_EGRESS_REDIR:
438c2ecf20Sopenharmony_ci	case TCA_EGRESS_MIRROR:
448c2ecf20Sopenharmony_ci		return false;
458c2ecf20Sopenharmony_ci	case TCA_INGRESS_REDIR:
468c2ecf20Sopenharmony_ci	case TCA_INGRESS_MIRROR:
478c2ecf20Sopenharmony_ci		return true;
488c2ecf20Sopenharmony_ci	default:
498c2ecf20Sopenharmony_ci		BUG();
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic bool tcf_mirred_can_reinsert(int action)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	switch (action) {
568c2ecf20Sopenharmony_ci	case TC_ACT_SHOT:
578c2ecf20Sopenharmony_ci	case TC_ACT_STOLEN:
588c2ecf20Sopenharmony_ci	case TC_ACT_QUEUED:
598c2ecf20Sopenharmony_ci	case TC_ACT_TRAP:
608c2ecf20Sopenharmony_ci		return true;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	return false;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct net_device *tcf_mirred_dev_dereference(struct tcf_mirred *m)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return rcu_dereference_protected(m->tcfm_dev,
688c2ecf20Sopenharmony_ci					 lockdep_is_held(&m->tcf_lock));
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void tcf_mirred_release(struct tc_action *a)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct tcf_mirred *m = to_mirred(a);
748c2ecf20Sopenharmony_ci	struct net_device *dev;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	spin_lock(&mirred_list_lock);
778c2ecf20Sopenharmony_ci	list_del(&m->tcfm_list);
788c2ecf20Sopenharmony_ci	spin_unlock(&mirred_list_lock);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* last reference to action, no need to lock */
818c2ecf20Sopenharmony_ci	dev = rcu_dereference_protected(m->tcfm_dev, 1);
828c2ecf20Sopenharmony_ci	if (dev)
838c2ecf20Sopenharmony_ci		dev_put(dev);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
878c2ecf20Sopenharmony_ci	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic unsigned int mirred_net_id;
918c2ecf20Sopenharmony_cistatic struct tc_action_ops act_mirred_ops;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int tcf_mirred_init(struct net *net, struct nlattr *nla,
948c2ecf20Sopenharmony_ci			   struct nlattr *est, struct tc_action **a,
958c2ecf20Sopenharmony_ci			   int ovr, int bind, bool rtnl_held,
968c2ecf20Sopenharmony_ci			   struct tcf_proto *tp,
978c2ecf20Sopenharmony_ci			   u32 flags, struct netlink_ext_ack *extack)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, mirred_net_id);
1008c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_MIRRED_MAX + 1];
1018c2ecf20Sopenharmony_ci	struct tcf_chain *goto_ch = NULL;
1028c2ecf20Sopenharmony_ci	bool mac_header_xmit = false;
1038c2ecf20Sopenharmony_ci	struct tc_mirred *parm;
1048c2ecf20Sopenharmony_ci	struct tcf_mirred *m;
1058c2ecf20Sopenharmony_ci	struct net_device *dev;
1068c2ecf20Sopenharmony_ci	bool exists = false;
1078c2ecf20Sopenharmony_ci	int ret, err;
1088c2ecf20Sopenharmony_ci	u32 index;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!nla) {
1118c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Mirred requires attributes to be passed");
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	ret = nla_parse_nested_deprecated(tb, TCA_MIRRED_MAX, nla,
1158c2ecf20Sopenharmony_ci					  mirred_policy, extack);
1168c2ecf20Sopenharmony_ci	if (ret < 0)
1178c2ecf20Sopenharmony_ci		return ret;
1188c2ecf20Sopenharmony_ci	if (!tb[TCA_MIRRED_PARMS]) {
1198c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Missing required mirred parameters");
1208c2ecf20Sopenharmony_ci		return -EINVAL;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	parm = nla_data(tb[TCA_MIRRED_PARMS]);
1238c2ecf20Sopenharmony_ci	index = parm->index;
1248c2ecf20Sopenharmony_ci	err = tcf_idr_check_alloc(tn, &index, a, bind);
1258c2ecf20Sopenharmony_ci	if (err < 0)
1268c2ecf20Sopenharmony_ci		return err;
1278c2ecf20Sopenharmony_ci	exists = err;
1288c2ecf20Sopenharmony_ci	if (exists && bind)
1298c2ecf20Sopenharmony_ci		return 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	switch (parm->eaction) {
1328c2ecf20Sopenharmony_ci	case TCA_EGRESS_MIRROR:
1338c2ecf20Sopenharmony_ci	case TCA_EGRESS_REDIR:
1348c2ecf20Sopenharmony_ci	case TCA_INGRESS_REDIR:
1358c2ecf20Sopenharmony_ci	case TCA_INGRESS_MIRROR:
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci	default:
1388c2ecf20Sopenharmony_ci		if (exists)
1398c2ecf20Sopenharmony_ci			tcf_idr_release(*a, bind);
1408c2ecf20Sopenharmony_ci		else
1418c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1428c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unknown mirred option");
1438c2ecf20Sopenharmony_ci		return -EINVAL;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (!exists) {
1478c2ecf20Sopenharmony_ci		if (!parm->ifindex) {
1488c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1498c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Specified device does not exist");
1508c2ecf20Sopenharmony_ci			return -EINVAL;
1518c2ecf20Sopenharmony_ci		}
1528c2ecf20Sopenharmony_ci		ret = tcf_idr_create_from_flags(tn, index, est, a,
1538c2ecf20Sopenharmony_ci						&act_mirred_ops, bind, flags);
1548c2ecf20Sopenharmony_ci		if (ret) {
1558c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1568c2ecf20Sopenharmony_ci			return ret;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci		ret = ACT_P_CREATED;
1598c2ecf20Sopenharmony_ci	} else if (!ovr) {
1608c2ecf20Sopenharmony_ci		tcf_idr_release(*a, bind);
1618c2ecf20Sopenharmony_ci		return -EEXIST;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	m = to_mirred(*a);
1658c2ecf20Sopenharmony_ci	if (ret == ACT_P_CREATED)
1668c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&m->tcfm_list);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
1698c2ecf20Sopenharmony_ci	if (err < 0)
1708c2ecf20Sopenharmony_ci		goto release_idr;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	spin_lock_bh(&m->tcf_lock);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (parm->ifindex) {
1758c2ecf20Sopenharmony_ci		dev = dev_get_by_index(net, parm->ifindex);
1768c2ecf20Sopenharmony_ci		if (!dev) {
1778c2ecf20Sopenharmony_ci			spin_unlock_bh(&m->tcf_lock);
1788c2ecf20Sopenharmony_ci			err = -ENODEV;
1798c2ecf20Sopenharmony_ci			goto put_chain;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		mac_header_xmit = dev_is_mac_header_xmit(dev);
1828c2ecf20Sopenharmony_ci		dev = rcu_replace_pointer(m->tcfm_dev, dev,
1838c2ecf20Sopenharmony_ci					  lockdep_is_held(&m->tcf_lock));
1848c2ecf20Sopenharmony_ci		if (dev)
1858c2ecf20Sopenharmony_ci			dev_put(dev);
1868c2ecf20Sopenharmony_ci		m->tcfm_mac_header_xmit = mac_header_xmit;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
1898c2ecf20Sopenharmony_ci	m->tcfm_eaction = parm->eaction;
1908c2ecf20Sopenharmony_ci	spin_unlock_bh(&m->tcf_lock);
1918c2ecf20Sopenharmony_ci	if (goto_ch)
1928c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (ret == ACT_P_CREATED) {
1958c2ecf20Sopenharmony_ci		spin_lock(&mirred_list_lock);
1968c2ecf20Sopenharmony_ci		list_add(&m->tcfm_list, &mirred_list);
1978c2ecf20Sopenharmony_ci		spin_unlock(&mirred_list_lock);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return ret;
2018c2ecf20Sopenharmony_ciput_chain:
2028c2ecf20Sopenharmony_ci	if (goto_ch)
2038c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
2048c2ecf20Sopenharmony_cirelease_idr:
2058c2ecf20Sopenharmony_ci	tcf_idr_release(*a, bind);
2068c2ecf20Sopenharmony_ci	return err;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic bool is_mirred_nested(void)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	return unlikely(__this_cpu_read(mirred_nest_level) > 1);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int tcf_mirred_forward(bool want_ingress, struct sk_buff *skb)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	int err;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!want_ingress)
2198c2ecf20Sopenharmony_ci		err = dev_queue_xmit(skb);
2208c2ecf20Sopenharmony_ci	else if (is_mirred_nested())
2218c2ecf20Sopenharmony_ci		err = netif_rx(skb);
2228c2ecf20Sopenharmony_ci	else
2238c2ecf20Sopenharmony_ci		err = netif_receive_skb(skb);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return err;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a,
2298c2ecf20Sopenharmony_ci			  struct tcf_result *res)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct tcf_mirred *m = to_mirred(a);
2328c2ecf20Sopenharmony_ci	struct sk_buff *skb2 = skb;
2338c2ecf20Sopenharmony_ci	bool m_mac_header_xmit;
2348c2ecf20Sopenharmony_ci	struct net_device *dev;
2358c2ecf20Sopenharmony_ci	unsigned int nest_level;
2368c2ecf20Sopenharmony_ci	int retval, err = 0;
2378c2ecf20Sopenharmony_ci	bool use_reinsert;
2388c2ecf20Sopenharmony_ci	bool want_ingress;
2398c2ecf20Sopenharmony_ci	bool is_redirect;
2408c2ecf20Sopenharmony_ci	bool expects_nh;
2418c2ecf20Sopenharmony_ci	bool at_ingress;
2428c2ecf20Sopenharmony_ci	int m_eaction;
2438c2ecf20Sopenharmony_ci	int mac_len;
2448c2ecf20Sopenharmony_ci	bool at_nh;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	nest_level = __this_cpu_inc_return(mirred_nest_level);
2478c2ecf20Sopenharmony_ci	if (unlikely(nest_level > MIRRED_NEST_LIMIT)) {
2488c2ecf20Sopenharmony_ci		net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n",
2498c2ecf20Sopenharmony_ci				     netdev_name(skb->dev));
2508c2ecf20Sopenharmony_ci		__this_cpu_dec(mirred_nest_level);
2518c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	tcf_lastuse_update(&m->tcf_tm);
2558c2ecf20Sopenharmony_ci	tcf_action_update_bstats(&m->common, skb);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit);
2588c2ecf20Sopenharmony_ci	m_eaction = READ_ONCE(m->tcfm_eaction);
2598c2ecf20Sopenharmony_ci	retval = READ_ONCE(m->tcf_action);
2608c2ecf20Sopenharmony_ci	dev = rcu_dereference_bh(m->tcfm_dev);
2618c2ecf20Sopenharmony_ci	if (unlikely(!dev)) {
2628c2ecf20Sopenharmony_ci		pr_notice_once("tc mirred: target device is gone\n");
2638c2ecf20Sopenharmony_ci		goto out;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (unlikely(!(dev->flags & IFF_UP)) || !netif_carrier_ok(dev)) {
2678c2ecf20Sopenharmony_ci		net_notice_ratelimited("tc mirred to Houston: device %s is down\n",
2688c2ecf20Sopenharmony_ci				       dev->name);
2698c2ecf20Sopenharmony_ci		goto out;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* we could easily avoid the clone only if called by ingress and clsact;
2738c2ecf20Sopenharmony_ci	 * since we can't easily detect the clsact caller, skip clone only for
2748c2ecf20Sopenharmony_ci	 * ingress - that covers the TC S/W datapath.
2758c2ecf20Sopenharmony_ci	 */
2768c2ecf20Sopenharmony_ci	is_redirect = tcf_mirred_is_act_redirect(m_eaction);
2778c2ecf20Sopenharmony_ci	at_ingress = skb_at_tc_ingress(skb);
2788c2ecf20Sopenharmony_ci	use_reinsert = at_ingress && is_redirect &&
2798c2ecf20Sopenharmony_ci		       tcf_mirred_can_reinsert(retval);
2808c2ecf20Sopenharmony_ci	if (!use_reinsert) {
2818c2ecf20Sopenharmony_ci		skb2 = skb_clone(skb, GFP_ATOMIC);
2828c2ecf20Sopenharmony_ci		if (!skb2)
2838c2ecf20Sopenharmony_ci			goto out;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* All mirred/redirected skbs should clear previous ct info */
2898c2ecf20Sopenharmony_ci	nf_reset_ct(skb2);
2908c2ecf20Sopenharmony_ci	if (want_ingress && !at_ingress) /* drop dst for egress -> ingress */
2918c2ecf20Sopenharmony_ci		skb_dst_drop(skb2);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	expects_nh = want_ingress || !m_mac_header_xmit;
2948c2ecf20Sopenharmony_ci	at_nh = skb->data == skb_network_header(skb);
2958c2ecf20Sopenharmony_ci	if (at_nh != expects_nh) {
2968c2ecf20Sopenharmony_ci		mac_len = skb_at_tc_ingress(skb) ? skb->mac_len :
2978c2ecf20Sopenharmony_ci			  skb_network_header(skb) - skb_mac_header(skb);
2988c2ecf20Sopenharmony_ci		if (expects_nh) {
2998c2ecf20Sopenharmony_ci			/* target device/action expect data at nh */
3008c2ecf20Sopenharmony_ci			skb_pull_rcsum(skb2, mac_len);
3018c2ecf20Sopenharmony_ci		} else {
3028c2ecf20Sopenharmony_ci			/* target device/action expect data at mac */
3038c2ecf20Sopenharmony_ci			skb_push_rcsum(skb2, mac_len);
3048c2ecf20Sopenharmony_ci		}
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	skb2->skb_iif = skb->dev->ifindex;
3088c2ecf20Sopenharmony_ci	skb2->dev = dev;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* mirror is always swallowed */
3118c2ecf20Sopenharmony_ci	if (is_redirect) {
3128c2ecf20Sopenharmony_ci		skb_set_redirected(skb2, skb2->tc_at_ingress);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		/* let's the caller reinsert the packet, if possible */
3158c2ecf20Sopenharmony_ci		if (use_reinsert) {
3168c2ecf20Sopenharmony_ci			res->ingress = want_ingress;
3178c2ecf20Sopenharmony_ci			err = tcf_mirred_forward(res->ingress, skb);
3188c2ecf20Sopenharmony_ci			if (err)
3198c2ecf20Sopenharmony_ci				tcf_action_inc_overlimit_qstats(&m->common);
3208c2ecf20Sopenharmony_ci			__this_cpu_dec(mirred_nest_level);
3218c2ecf20Sopenharmony_ci			return TC_ACT_CONSUMED;
3228c2ecf20Sopenharmony_ci		}
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	err = tcf_mirred_forward(want_ingress, skb2);
3268c2ecf20Sopenharmony_ci	if (err) {
3278c2ecf20Sopenharmony_ciout:
3288c2ecf20Sopenharmony_ci		tcf_action_inc_overlimit_qstats(&m->common);
3298c2ecf20Sopenharmony_ci		if (tcf_mirred_is_act_redirect(m_eaction))
3308c2ecf20Sopenharmony_ci			retval = TC_ACT_SHOT;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	__this_cpu_dec(mirred_nest_level);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return retval;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets,
3388c2ecf20Sopenharmony_ci			     u64 drops, u64 lastuse, bool hw)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct tcf_mirred *m = to_mirred(a);
3418c2ecf20Sopenharmony_ci	struct tcf_t *tm = &m->tcf_tm;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	tcf_action_update_stats(a, bytes, packets, drops, hw);
3448c2ecf20Sopenharmony_ci	tm->lastuse = max_t(u64, tm->lastuse, lastuse);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
3488c2ecf20Sopenharmony_ci			   int ref)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
3518c2ecf20Sopenharmony_ci	struct tcf_mirred *m = to_mirred(a);
3528c2ecf20Sopenharmony_ci	struct tc_mirred opt = {
3538c2ecf20Sopenharmony_ci		.index   = m->tcf_index,
3548c2ecf20Sopenharmony_ci		.refcnt  = refcount_read(&m->tcf_refcnt) - ref,
3558c2ecf20Sopenharmony_ci		.bindcnt = atomic_read(&m->tcf_bindcnt) - bind,
3568c2ecf20Sopenharmony_ci	};
3578c2ecf20Sopenharmony_ci	struct net_device *dev;
3588c2ecf20Sopenharmony_ci	struct tcf_t t;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	spin_lock_bh(&m->tcf_lock);
3618c2ecf20Sopenharmony_ci	opt.action = m->tcf_action;
3628c2ecf20Sopenharmony_ci	opt.eaction = m->tcfm_eaction;
3638c2ecf20Sopenharmony_ci	dev = tcf_mirred_dev_dereference(m);
3648c2ecf20Sopenharmony_ci	if (dev)
3658c2ecf20Sopenharmony_ci		opt.ifindex = dev->ifindex;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
3688c2ecf20Sopenharmony_ci		goto nla_put_failure;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	tcf_tm_dump(&t, &m->tcf_tm);
3718c2ecf20Sopenharmony_ci	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
3728c2ecf20Sopenharmony_ci		goto nla_put_failure;
3738c2ecf20Sopenharmony_ci	spin_unlock_bh(&m->tcf_lock);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return skb->len;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cinla_put_failure:
3788c2ecf20Sopenharmony_ci	spin_unlock_bh(&m->tcf_lock);
3798c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
3808c2ecf20Sopenharmony_ci	return -1;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int tcf_mirred_walker(struct net *net, struct sk_buff *skb,
3848c2ecf20Sopenharmony_ci			     struct netlink_callback *cb, int type,
3858c2ecf20Sopenharmony_ci			     const struct tc_action_ops *ops,
3868c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, mirred_net_id);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int tcf_mirred_search(struct net *net, struct tc_action **a, u32 index)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, mirred_net_id);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return tcf_idr_search(tn, a, index);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int mirred_device_event(struct notifier_block *unused,
4018c2ecf20Sopenharmony_ci			       unsigned long event, void *ptr)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
4048c2ecf20Sopenharmony_ci	struct tcf_mirred *m;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	ASSERT_RTNL();
4078c2ecf20Sopenharmony_ci	if (event == NETDEV_UNREGISTER) {
4088c2ecf20Sopenharmony_ci		spin_lock(&mirred_list_lock);
4098c2ecf20Sopenharmony_ci		list_for_each_entry(m, &mirred_list, tcfm_list) {
4108c2ecf20Sopenharmony_ci			spin_lock_bh(&m->tcf_lock);
4118c2ecf20Sopenharmony_ci			if (tcf_mirred_dev_dereference(m) == dev) {
4128c2ecf20Sopenharmony_ci				dev_put(dev);
4138c2ecf20Sopenharmony_ci				/* Note : no rcu grace period necessary, as
4148c2ecf20Sopenharmony_ci				 * net_device are already rcu protected.
4158c2ecf20Sopenharmony_ci				 */
4168c2ecf20Sopenharmony_ci				RCU_INIT_POINTER(m->tcfm_dev, NULL);
4178c2ecf20Sopenharmony_ci			}
4188c2ecf20Sopenharmony_ci			spin_unlock_bh(&m->tcf_lock);
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci		spin_unlock(&mirred_list_lock);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic struct notifier_block mirred_device_notifier = {
4278c2ecf20Sopenharmony_ci	.notifier_call = mirred_device_event,
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void tcf_mirred_dev_put(void *priv)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct net_device *dev = priv;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	dev_put(dev);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic struct net_device *
4388c2ecf20Sopenharmony_citcf_mirred_get_dev(const struct tc_action *a,
4398c2ecf20Sopenharmony_ci		   tc_action_priv_destructor *destructor)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct tcf_mirred *m = to_mirred(a);
4428c2ecf20Sopenharmony_ci	struct net_device *dev;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	rcu_read_lock();
4458c2ecf20Sopenharmony_ci	dev = rcu_dereference(m->tcfm_dev);
4468c2ecf20Sopenharmony_ci	if (dev) {
4478c2ecf20Sopenharmony_ci		dev_hold(dev);
4488c2ecf20Sopenharmony_ci		*destructor = tcf_mirred_dev_put;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	rcu_read_unlock();
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return dev;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic size_t tcf_mirred_get_fill_size(const struct tc_action *act)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	return nla_total_size(sizeof(struct tc_mirred));
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic struct tc_action_ops act_mirred_ops = {
4618c2ecf20Sopenharmony_ci	.kind		=	"mirred",
4628c2ecf20Sopenharmony_ci	.id		=	TCA_ID_MIRRED,
4638c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
4648c2ecf20Sopenharmony_ci	.act		=	tcf_mirred_act,
4658c2ecf20Sopenharmony_ci	.stats_update	=	tcf_stats_update,
4668c2ecf20Sopenharmony_ci	.dump		=	tcf_mirred_dump,
4678c2ecf20Sopenharmony_ci	.cleanup	=	tcf_mirred_release,
4688c2ecf20Sopenharmony_ci	.init		=	tcf_mirred_init,
4698c2ecf20Sopenharmony_ci	.walk		=	tcf_mirred_walker,
4708c2ecf20Sopenharmony_ci	.lookup		=	tcf_mirred_search,
4718c2ecf20Sopenharmony_ci	.get_fill_size	=	tcf_mirred_get_fill_size,
4728c2ecf20Sopenharmony_ci	.size		=	sizeof(struct tcf_mirred),
4738c2ecf20Sopenharmony_ci	.get_dev	=	tcf_mirred_get_dev,
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic __net_init int mirred_init_net(struct net *net)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, mirred_net_id);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return tc_action_net_init(net, tn, &act_mirred_ops);
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic void __net_exit mirred_exit_net(struct list_head *net_list)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	tc_action_net_exit(net_list, mirred_net_id);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic struct pernet_operations mirred_net_ops = {
4898c2ecf20Sopenharmony_ci	.init = mirred_init_net,
4908c2ecf20Sopenharmony_ci	.exit_batch = mirred_exit_net,
4918c2ecf20Sopenharmony_ci	.id   = &mirred_net_id,
4928c2ecf20Sopenharmony_ci	.size = sizeof(struct tc_action_net),
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jamal Hadi Salim(2002)");
4968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Device Mirror/redirect actions");
4978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic int __init mirred_init_module(void)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	int err = register_netdevice_notifier(&mirred_device_notifier);
5028c2ecf20Sopenharmony_ci	if (err)
5038c2ecf20Sopenharmony_ci		return err;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	pr_info("Mirror/redirect action on\n");
5068c2ecf20Sopenharmony_ci	err = tcf_register_action(&act_mirred_ops, &mirred_net_ops);
5078c2ecf20Sopenharmony_ci	if (err)
5088c2ecf20Sopenharmony_ci		unregister_netdevice_notifier(&mirred_device_notifier);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return err;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic void __exit mirred_cleanup_module(void)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	tcf_unregister_action(&act_mirred_ops, &mirred_net_ops);
5168c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&mirred_device_notifier);
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cimodule_init(mirred_init_module);
5208c2ecf20Sopenharmony_cimodule_exit(mirred_cleanup_module);
521