forked from mirrors/linux
		
	ip6mr: Make mroute_sk rcu-based
In ipmr the mr_table socket is handled under RCU. Introduce the same for ip6mr. Signed-off-by: Yuval Mintz <yuvalm@mellanox.com> Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									6853f21f76
								
							
						
					
					
						commit
						8571ab479a
					
				
					 3 changed files with 31 additions and 22 deletions
				
			
		| 
						 | 
					@ -111,12 +111,12 @@ extern int ip6mr_get_route(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
			   struct rtmsg *rtm, u32 portid);
 | 
								   struct rtmsg *rtm, u32 portid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IPV6_MROUTE
 | 
					#ifdef CONFIG_IPV6_MROUTE
 | 
				
			||||||
extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb);
 | 
					bool mroute6_is_socket(struct net *net, struct sk_buff *skb);
 | 
				
			||||||
extern int ip6mr_sk_done(struct sock *sk);
 | 
					extern int ip6mr_sk_done(struct sock *sk);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
static inline struct sock *mroute6_socket(struct net *net, struct sk_buff *skb)
 | 
					static inline bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return NULL;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
static inline int ip6mr_sk_done(struct sock *sk)
 | 
					static inline int ip6mr_sk_done(struct sock *sk)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
 | 
				
			||||||
		struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
 | 
							struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
 | 
							if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
 | 
				
			||||||
		    ((mroute6_socket(net, skb) &&
 | 
							    ((mroute6_is_socket(net, skb) &&
 | 
				
			||||||
		     !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
 | 
							     !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
 | 
				
			||||||
		     ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
 | 
							     ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
 | 
				
			||||||
					 &ipv6_hdr(skb)->saddr))) {
 | 
										 &ipv6_hdr(skb)->saddr))) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ struct mr6_table {
 | 
				
			||||||
	struct list_head	list;
 | 
						struct list_head	list;
 | 
				
			||||||
	possible_net_t		net;
 | 
						possible_net_t		net;
 | 
				
			||||||
	u32			id;
 | 
						u32			id;
 | 
				
			||||||
	struct sock		*mroute6_sk;
 | 
						struct sock __rcu	*mroute6_sk;
 | 
				
			||||||
	struct timer_list	ipmr_expire_timer;
 | 
						struct timer_list	ipmr_expire_timer;
 | 
				
			||||||
	struct list_head	mfc6_unres_queue;
 | 
						struct list_head	mfc6_unres_queue;
 | 
				
			||||||
	struct list_head	mfc6_cache_array[MFC6_LINES];
 | 
						struct list_head	mfc6_cache_array[MFC6_LINES];
 | 
				
			||||||
| 
						 | 
					@ -1121,6 +1121,7 @@ static void ip6mr_cache_resolve(struct net *net, struct mr6_table *mrt,
 | 
				
			||||||
static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt,
 | 
					static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt,
 | 
				
			||||||
			      mifi_t mifi, int assert)
 | 
								      mifi_t mifi, int assert)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct sock *mroute6_sk;
 | 
				
			||||||
	struct sk_buff *skb;
 | 
						struct sk_buff *skb;
 | 
				
			||||||
	struct mrt6msg *msg;
 | 
						struct mrt6msg *msg;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -1190,17 +1191,19 @@ static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt,
 | 
				
			||||||
	skb->ip_summed = CHECKSUM_UNNECESSARY;
 | 
						skb->ip_summed = CHECKSUM_UNNECESSARY;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!mrt->mroute6_sk) {
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
						mroute6_sk = rcu_dereference(mrt->mroute6_sk);
 | 
				
			||||||
 | 
						if (!mroute6_sk) {
 | 
				
			||||||
 | 
							rcu_read_unlock();
 | 
				
			||||||
		kfree_skb(skb);
 | 
							kfree_skb(skb);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mrt6msg_netlink_event(mrt, skb);
 | 
						mrt6msg_netlink_event(mrt, skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/* Deliver to user space multicast routing algorithms */
 | 
				
			||||||
	 *	Deliver to user space multicast routing algorithms
 | 
						ret = sock_queue_rcv_skb(mroute6_sk, skb);
 | 
				
			||||||
	 */
 | 
						rcu_read_unlock();
 | 
				
			||||||
	ret = sock_queue_rcv_skb(mrt->mroute6_sk, skb);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		net_warn_ratelimited("mroute6: pending queue full, dropping entries\n");
 | 
							net_warn_ratelimited("mroute6: pending queue full, dropping entries\n");
 | 
				
			||||||
		kfree_skb(skb);
 | 
							kfree_skb(skb);
 | 
				
			||||||
| 
						 | 
					@ -1584,11 +1587,11 @@ static int ip6mr_sk_init(struct mr6_table *mrt, struct sock *sk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtnl_lock();
 | 
						rtnl_lock();
 | 
				
			||||||
	write_lock_bh(&mrt_lock);
 | 
						write_lock_bh(&mrt_lock);
 | 
				
			||||||
	if (likely(mrt->mroute6_sk == NULL)) {
 | 
						if (rtnl_dereference(mrt->mroute6_sk)) {
 | 
				
			||||||
		mrt->mroute6_sk = sk;
 | 
					 | 
				
			||||||
		net->ipv6.devconf_all->mc_forwarding++;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		err = -EADDRINUSE;
 | 
							err = -EADDRINUSE;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							rcu_assign_pointer(mrt->mroute6_sk, sk);
 | 
				
			||||||
 | 
							net->ipv6.devconf_all->mc_forwarding++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	write_unlock_bh(&mrt_lock);
 | 
						write_unlock_bh(&mrt_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1614,9 +1617,9 @@ int ip6mr_sk_done(struct sock *sk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtnl_lock();
 | 
						rtnl_lock();
 | 
				
			||||||
	ip6mr_for_each_table(mrt, net) {
 | 
						ip6mr_for_each_table(mrt, net) {
 | 
				
			||||||
		if (sk == mrt->mroute6_sk) {
 | 
							if (sk == rtnl_dereference(mrt->mroute6_sk)) {
 | 
				
			||||||
			write_lock_bh(&mrt_lock);
 | 
								write_lock_bh(&mrt_lock);
 | 
				
			||||||
			mrt->mroute6_sk = NULL;
 | 
								RCU_INIT_POINTER(mrt->mroute6_sk, NULL);
 | 
				
			||||||
			net->ipv6.devconf_all->mc_forwarding--;
 | 
								net->ipv6.devconf_all->mc_forwarding--;
 | 
				
			||||||
			write_unlock_bh(&mrt_lock);
 | 
								write_unlock_bh(&mrt_lock);
 | 
				
			||||||
			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
 | 
								inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
 | 
				
			||||||
| 
						 | 
					@ -1630,11 +1633,12 @@ int ip6mr_sk_done(struct sock *sk)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	rtnl_unlock();
 | 
						rtnl_unlock();
 | 
				
			||||||
 | 
						synchronize_rcu();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct sock *mroute6_socket(struct net *net, struct sk_buff *skb)
 | 
					bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct mr6_table *mrt;
 | 
						struct mr6_table *mrt;
 | 
				
			||||||
	struct flowi6 fl6 = {
 | 
						struct flowi6 fl6 = {
 | 
				
			||||||
| 
						 | 
					@ -1646,8 +1650,9 @@ struct sock *mroute6_socket(struct net *net, struct sk_buff *skb)
 | 
				
			||||||
	if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
 | 
						if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return mrt->mroute6_sk;
 | 
						return rcu_access_pointer(mrt->mroute6_sk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(mroute6_is_socket);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *	Socket options and virtual interface manipulation. The whole
 | 
					 *	Socket options and virtual interface manipulation. The whole
 | 
				
			||||||
| 
						 | 
					@ -1674,7 +1679,8 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (optname != MRT6_INIT) {
 | 
						if (optname != MRT6_INIT) {
 | 
				
			||||||
		if (sk != mrt->mroute6_sk && !ns_capable(net->user_ns, CAP_NET_ADMIN))
 | 
							if (sk != rcu_access_pointer(mrt->mroute6_sk) &&
 | 
				
			||||||
 | 
							    !ns_capable(net->user_ns, CAP_NET_ADMIN))
 | 
				
			||||||
			return -EACCES;
 | 
								return -EACCES;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1696,7 +1702,8 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
 | 
				
			||||||
		if (vif.mif6c_mifi >= MAXMIFS)
 | 
							if (vif.mif6c_mifi >= MAXMIFS)
 | 
				
			||||||
			return -ENFILE;
 | 
								return -ENFILE;
 | 
				
			||||||
		rtnl_lock();
 | 
							rtnl_lock();
 | 
				
			||||||
		ret = mif6_add(net, mrt, &vif, sk == mrt->mroute6_sk);
 | 
							ret = mif6_add(net, mrt, &vif,
 | 
				
			||||||
 | 
								       sk == rtnl_dereference(mrt->mroute6_sk));
 | 
				
			||||||
		rtnl_unlock();
 | 
							rtnl_unlock();
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1731,7 +1738,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
 | 
				
			||||||
			ret = ip6mr_mfc_delete(mrt, &mfc, parent);
 | 
								ret = ip6mr_mfc_delete(mrt, &mfc, parent);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			ret = ip6mr_mfc_add(net, mrt, &mfc,
 | 
								ret = ip6mr_mfc_add(net, mrt, &mfc,
 | 
				
			||||||
					    sk == mrt->mroute6_sk, parent);
 | 
										    sk ==
 | 
				
			||||||
 | 
										    rtnl_dereference(mrt->mroute6_sk),
 | 
				
			||||||
 | 
										    parent);
 | 
				
			||||||
		rtnl_unlock();
 | 
							rtnl_unlock();
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1783,7 +1792,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
 | 
				
			||||||
		/* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
 | 
							/* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
 | 
				
			||||||
		if (v != RT_TABLE_DEFAULT && v >= 100000000)
 | 
							if (v != RT_TABLE_DEFAULT && v >= 100000000)
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		if (sk == mrt->mroute6_sk)
 | 
							if (sk == rcu_access_pointer(mrt->mroute6_sk))
 | 
				
			||||||
			return -EBUSY;
 | 
								return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rtnl_lock();
 | 
							rtnl_lock();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue