mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net/sched: act_mirred: Allow mirred to block
So far the mirred action has dealt with syntax that handles mirror/redirection for netdev. A matching packet is redirected or mirrored to a target netdev. In this patch we enable mirred to mirror to a tc block as well. IOW, the new syntax looks as follows: ... mirred <ingress | egress> <mirror | redirect> [index INDEX] < <blockid BLOCKID> | <dev <devname>> > Examples of mirroring or redirecting to a tc block: $ tc filter add block 22 protocol ip pref 25 \ flower dst_ip 192.168.0.0/16 action mirred egress mirror blockid 22 $ tc filter add block 22 protocol ip pref 25 \ flower dst_ip 10.10.10.10/32 action mirred egress redirect blockid 22 Co-developed-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Co-developed-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									415e38bf1d
								
							
						
					
					
						commit
						42f39036cd
					
				
					 3 changed files with 119 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
struct tcf_mirred {
 | 
			
		||||
	struct tc_action	common;
 | 
			
		||||
	int			tcfm_eaction;
 | 
			
		||||
	u32                     tcfm_blockid;
 | 
			
		||||
	bool			tcfm_mac_header_xmit;
 | 
			
		||||
	struct net_device __rcu	*tcfm_dev;
 | 
			
		||||
	netdevice_tracker	tcfm_dev_tracker;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ enum {
 | 
			
		|||
	TCA_MIRRED_TM,
 | 
			
		||||
	TCA_MIRRED_PARMS,
 | 
			
		||||
	TCA_MIRRED_PAD,
 | 
			
		||||
	TCA_MIRRED_BLOCKID,
 | 
			
		||||
	__TCA_MIRRED_MAX
 | 
			
		||||
};
 | 
			
		||||
#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,6 +85,7 @@ static void tcf_mirred_release(struct tc_action *a)
 | 
			
		|||
 | 
			
		||||
static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
 | 
			
		||||
	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
 | 
			
		||||
	[TCA_MIRRED_BLOCKID]	= NLA_POLICY_MIN(NLA_U32, 1),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct tc_action_ops act_mirred_ops;
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +137,17 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 | 
			
		|||
	if (exists && bind)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (tb[TCA_MIRRED_BLOCKID] && parm->ifindex) {
 | 
			
		||||
		NL_SET_ERR_MSG_MOD(extack,
 | 
			
		||||
				   "Cannot specify Block ID and dev simultaneously");
 | 
			
		||||
		if (exists)
 | 
			
		||||
			tcf_idr_release(*a, bind);
 | 
			
		||||
		else
 | 
			
		||||
			tcf_idr_cleanup(tn, index);
 | 
			
		||||
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (parm->eaction) {
 | 
			
		||||
	case TCA_EGRESS_MIRROR:
 | 
			
		||||
	case TCA_EGRESS_REDIR:
 | 
			
		||||
| 
						 | 
				
			
			@ -152,9 +164,10 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (!exists) {
 | 
			
		||||
		if (!parm->ifindex) {
 | 
			
		||||
		if (!parm->ifindex && !tb[TCA_MIRRED_BLOCKID]) {
 | 
			
		||||
			tcf_idr_cleanup(tn, index);
 | 
			
		||||
			NL_SET_ERR_MSG_MOD(extack, "Specified device does not exist");
 | 
			
		||||
			NL_SET_ERR_MSG_MOD(extack,
 | 
			
		||||
					   "Must specify device or block");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		ret = tcf_idr_create_from_flags(tn, index, est, a,
 | 
			
		||||
| 
						 | 
				
			
			@ -192,6 +205,11 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 | 
			
		|||
		tcf_mirred_replace_dev(m, ndev);
 | 
			
		||||
		netdev_tracker_alloc(ndev, &m->tcfm_dev_tracker, GFP_ATOMIC);
 | 
			
		||||
		m->tcfm_mac_header_xmit = mac_header_xmit;
 | 
			
		||||
		m->tcfm_blockid = 0;
 | 
			
		||||
	} else if (tb[TCA_MIRRED_BLOCKID]) {
 | 
			
		||||
		tcf_mirred_replace_dev(m, NULL);
 | 
			
		||||
		m->tcfm_mac_header_xmit = false;
 | 
			
		||||
		m->tcfm_blockid = nla_get_u32(tb[TCA_MIRRED_BLOCKID]);
 | 
			
		||||
	}
 | 
			
		||||
	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
 | 
			
		||||
	m->tcfm_eaction = parm->eaction;
 | 
			
		||||
| 
						 | 
				
			
			@ -316,6 +334,89 @@ static int tcf_mirred_to_dev(struct sk_buff *skb, struct tcf_mirred *m,
 | 
			
		|||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tcf_blockcast_redir(struct sk_buff *skb, struct tcf_mirred *m,
 | 
			
		||||
			       struct tcf_block *block, int m_eaction,
 | 
			
		||||
			       const u32 exception_ifindex, int retval)
 | 
			
		||||
{
 | 
			
		||||
	struct net_device *dev_prev = NULL;
 | 
			
		||||
	struct net_device *dev = NULL;
 | 
			
		||||
	unsigned long index;
 | 
			
		||||
	int mirred_eaction;
 | 
			
		||||
 | 
			
		||||
	mirred_eaction = tcf_mirred_act_wants_ingress(m_eaction) ?
 | 
			
		||||
		TCA_INGRESS_MIRROR : TCA_EGRESS_MIRROR;
 | 
			
		||||
 | 
			
		||||
	xa_for_each(&block->ports, index, dev) {
 | 
			
		||||
		if (index == exception_ifindex)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!dev_prev)
 | 
			
		||||
			goto assign_prev;
 | 
			
		||||
 | 
			
		||||
		tcf_mirred_to_dev(skb, m, dev_prev,
 | 
			
		||||
				  dev_is_mac_header_xmit(dev),
 | 
			
		||||
				  mirred_eaction, retval);
 | 
			
		||||
assign_prev:
 | 
			
		||||
		dev_prev = dev;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dev_prev)
 | 
			
		||||
		return tcf_mirred_to_dev(skb, m, dev_prev,
 | 
			
		||||
					 dev_is_mac_header_xmit(dev_prev),
 | 
			
		||||
					 m_eaction, retval);
 | 
			
		||||
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
 | 
			
		||||
				struct tcf_block *block, int m_eaction,
 | 
			
		||||
				const u32 exception_ifindex, int retval)
 | 
			
		||||
{
 | 
			
		||||
	struct net_device *dev = NULL;
 | 
			
		||||
	unsigned long index;
 | 
			
		||||
 | 
			
		||||
	xa_for_each(&block->ports, index, dev) {
 | 
			
		||||
		if (index == exception_ifindex)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		tcf_mirred_to_dev(skb, m, dev,
 | 
			
		||||
				  dev_is_mac_header_xmit(dev),
 | 
			
		||||
				  m_eaction, retval);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
 | 
			
		||||
			 const u32 blockid, struct tcf_result *res,
 | 
			
		||||
			 int retval)
 | 
			
		||||
{
 | 
			
		||||
	const u32 exception_ifindex = skb->dev->ifindex;
 | 
			
		||||
	struct tcf_block *block;
 | 
			
		||||
	bool is_redirect;
 | 
			
		||||
	int m_eaction;
 | 
			
		||||
 | 
			
		||||
	m_eaction = READ_ONCE(m->tcfm_eaction);
 | 
			
		||||
	is_redirect = tcf_mirred_is_act_redirect(m_eaction);
 | 
			
		||||
 | 
			
		||||
	/* we are already under rcu protection, so can call block lookup
 | 
			
		||||
	 * directly.
 | 
			
		||||
	 */
 | 
			
		||||
	block = tcf_block_lookup(dev_net(skb->dev), blockid);
 | 
			
		||||
	if (!block || xa_empty(&block->ports)) {
 | 
			
		||||
		tcf_action_inc_overlimit_qstats(&m->common);
 | 
			
		||||
		return retval;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (is_redirect)
 | 
			
		||||
		return tcf_blockcast_redir(skb, m, block, m_eaction,
 | 
			
		||||
					   exception_ifindex, retval);
 | 
			
		||||
 | 
			
		||||
	/* If it's not redirect, it is mirror */
 | 
			
		||||
	return tcf_blockcast_mirror(skb, m, block, m_eaction, exception_ifindex,
 | 
			
		||||
				    retval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
 | 
			
		||||
				     const struct tc_action *a,
 | 
			
		||||
				     struct tcf_result *res)
 | 
			
		||||
| 
						 | 
				
			
			@ -326,6 +427,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
 | 
			
		|||
	bool m_mac_header_xmit;
 | 
			
		||||
	struct net_device *dev;
 | 
			
		||||
	int m_eaction;
 | 
			
		||||
	u32 blockid;
 | 
			
		||||
 | 
			
		||||
	nest_level = __this_cpu_inc_return(mirred_nest_level);
 | 
			
		||||
	if (unlikely(nest_level > MIRRED_NEST_LIMIT)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -338,6 +440,12 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
 | 
			
		|||
	tcf_lastuse_update(&m->tcf_tm);
 | 
			
		||||
	tcf_action_update_bstats(&m->common, skb);
 | 
			
		||||
 | 
			
		||||
	blockid = READ_ONCE(m->tcfm_blockid);
 | 
			
		||||
	if (blockid) {
 | 
			
		||||
		retval = tcf_blockcast(skb, m, blockid, res, retval);
 | 
			
		||||
		goto dec_nest_level;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dev = rcu_dereference_bh(m->tcfm_dev);
 | 
			
		||||
	if (unlikely(!dev)) {
 | 
			
		||||
		pr_notice_once("tc mirred: target device is gone\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -379,6 +487,7 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
 | 
			
		|||
	};
 | 
			
		||||
	struct net_device *dev;
 | 
			
		||||
	struct tcf_t t;
 | 
			
		||||
	u32 blockid;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&m->tcf_lock);
 | 
			
		||||
	opt.action = m->tcf_action;
 | 
			
		||||
| 
						 | 
				
			
			@ -390,6 +499,10 @@ static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
 | 
			
		|||
	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
 | 
			
		||||
		goto nla_put_failure;
 | 
			
		||||
 | 
			
		||||
	blockid = m->tcfm_blockid;
 | 
			
		||||
	if (blockid && nla_put_u32(skb, TCA_MIRRED_BLOCKID, blockid))
 | 
			
		||||
		goto nla_put_failure;
 | 
			
		||||
 | 
			
		||||
	tcf_tm_dump(&t, &m->tcf_tm);
 | 
			
		||||
	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
 | 
			
		||||
		goto nla_put_failure;
 | 
			
		||||
| 
						 | 
				
			
			@ -420,6 +533,8 @@ static int mirred_device_event(struct notifier_block *unused,
 | 
			
		|||
				 * net_device are already rcu protected.
 | 
			
		||||
				 */
 | 
			
		||||
				RCU_INIT_POINTER(m->tcfm_dev, NULL);
 | 
			
		||||
			} else if (m->tcfm_blockid) {
 | 
			
		||||
				m->tcfm_blockid = 0;
 | 
			
		||||
			}
 | 
			
		||||
			spin_unlock_bh(&m->tcf_lock);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue