diff options
Diffstat (limited to 'net/ipv6/anycast.c')
| -rw-r--r-- | net/ipv6/anycast.c | 99 | 
1 files changed, 42 insertions, 57 deletions
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 0e5e943446f..21018324468 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -44,17 +44,17 @@  #include <net/checksum.h> -static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr); +static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr);  /* Big ac list lock for all the sockets */ -static DEFINE_RWLOCK(ipv6_sk_ac_lock); +static DEFINE_SPINLOCK(ipv6_sk_ac_lock);  /*   *	socket join an anycast group   */ -int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr) +int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)  {  	struct ipv6_pinfo *np = inet6_sk(sk);  	struct net_device *dev = NULL; @@ -64,7 +64,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)  	int	ishost = !net->ipv6.devconf_all->forwarding;  	int	err = 0; -	if (!capable(CAP_NET_ADMIN)) +	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))  		return -EPERM;  	if (ipv6_addr_is_multicast(addr))  		return -EINVAL; @@ -75,7 +75,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)  	if (pac == NULL)  		return -ENOMEM;  	pac->acl_next = NULL; -	ipv6_addr_copy(&pac->acl_addr, addr); +	pac->acl_addr = *addr;  	rcu_read_lock();  	if (ifindex == 0) { @@ -83,8 +83,8 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)  		rt = rt6_lookup(net, addr, NULL, 0, 0);  		if (rt) { -			dev = rt->rt6i_dev; -			dst_release(&rt->dst); +			dev = rt->dst.dev; +			ip6_rt_put(rt);  		} else if (ishost) {  			err = -EADDRNOTAVAIL;  			goto error; @@ -128,10 +128,10 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)  	err = ipv6_dev_ac_inc(dev, addr);  	if (!err) { -		write_lock_bh(&ipv6_sk_ac_lock); +		spin_lock_bh(&ipv6_sk_ac_lock);  		pac->acl_next = np->ipv6_ac_list;  		np->ipv6_ac_list = pac; -		write_unlock_bh(&ipv6_sk_ac_lock); +		spin_unlock_bh(&ipv6_sk_ac_lock);  		pac = NULL;  	} @@ -145,14 +145,14 @@ error:  /*   *	socket leave an anycast group   */ -int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr) +int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)  {  	struct ipv6_pinfo *np = inet6_sk(sk);  	struct net_device *dev;  	struct ipv6_ac_socklist *pac, *prev_pac;  	struct net *net = sock_net(sk); -	write_lock_bh(&ipv6_sk_ac_lock); +	spin_lock_bh(&ipv6_sk_ac_lock);  	prev_pac = NULL;  	for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {  		if ((ifindex == 0 || pac->acl_ifindex == ifindex) && @@ -161,7 +161,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr)  		prev_pac = pac;  	}  	if (!pac) { -		write_unlock_bh(&ipv6_sk_ac_lock); +		spin_unlock_bh(&ipv6_sk_ac_lock);  		return -ENOENT;  	}  	if (prev_pac) @@ -169,7 +169,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr)  	else  		np->ipv6_ac_list = pac->acl_next; -	write_unlock_bh(&ipv6_sk_ac_lock); +	spin_unlock_bh(&ipv6_sk_ac_lock);  	rcu_read_lock();  	dev = dev_get_by_index_rcu(net, pac->acl_ifindex); @@ -189,10 +189,13 @@ void ipv6_sock_ac_close(struct sock *sk)  	struct net *net = sock_net(sk);  	int	prev_index; -	write_lock_bh(&ipv6_sk_ac_lock); +	if (!np->ipv6_ac_list) +		return; + +	spin_lock_bh(&ipv6_sk_ac_lock);  	pac = np->ipv6_ac_list;  	np->ipv6_ac_list = NULL; -	write_unlock_bh(&ipv6_sk_ac_lock); +	spin_unlock_bh(&ipv6_sk_ac_lock);  	prev_index = 0;  	rcu_read_lock(); @@ -211,35 +214,6 @@ void ipv6_sock_ac_close(struct sock *sk)  	rcu_read_unlock();  } -#if 0 -/* The function is not used, which is funny. Apparently, author - * supposed to use it to filter out datagrams inside udp/raw but forgot. - * - * It is OK, anycasts are not special comparing to delivery to unicasts. - */ - -int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex) -{ -	struct ipv6_ac_socklist *pac; -	struct ipv6_pinfo *np = inet6_sk(sk); -	int	found; - -	found = 0; -	read_lock(&ipv6_sk_ac_lock); -	for (pac=np->ipv6_ac_list; pac; pac=pac->acl_next) { -		if (ifindex && pac->acl_ifindex != ifindex) -			continue; -		found = ipv6_addr_equal(&pac->acl_addr, addr); -		if (found) -			break; -	} -	read_unlock(&ipv6_sk_ac_lock); - -	return found; -} - -#endif -  static void aca_put(struct ifacaddr6 *ac)  {  	if (atomic_dec_and_test(&ac->aca_refcnt)) { @@ -252,7 +226,7 @@ static void aca_put(struct ifacaddr6 *ac)  /*   *	device anycast group inc (add if not found)   */ -int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) +int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr)  {  	struct ifacaddr6 *aca;  	struct inet6_dev *idev; @@ -289,14 +263,14 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr)  		goto out;  	} -	rt = addrconf_dst_alloc(idev, addr, 1); +	rt = addrconf_dst_alloc(idev, addr, true);  	if (IS_ERR(rt)) {  		kfree(aca);  		err = PTR_ERR(rt);  		goto out;  	} -	ipv6_addr_copy(&aca->aca_addr, addr); +	aca->aca_addr = *addr;  	aca->aca_idev = idev;  	aca->aca_rt = rt;  	aca->aca_users = 1; @@ -324,7 +298,7 @@ out:  /*   *	device anycast group decrement   */ -int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) +int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)  {  	struct ifacaddr6 *aca, *prev_aca; @@ -358,7 +332,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr)  }  /* called with rcu_read_lock() */ -static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) +static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr)  {  	struct inet6_dev *idev = __in6_dev_get(dev); @@ -371,7 +345,7 @@ static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)   *	check if the interface has this anycast address   *	called with rcu_read_lock()   */ -static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr) +static bool ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *addr)  {  	struct inet6_dev *idev;  	struct ifacaddr6 *aca; @@ -385,16 +359,16 @@ static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr)  		read_unlock_bh(&idev->lock);  		return aca != NULL;  	} -	return 0; +	return false;  }  /*   *	check if given interface (or any, if dev==0) has this anycast address   */ -int ipv6_chk_acast_addr(struct net *net, struct net_device *dev, -			struct in6_addr *addr) +bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, +			 const struct in6_addr *addr)  { -	int found = 0; +	bool found = false;  	rcu_read_lock();  	if (dev) @@ -402,13 +376,24 @@ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev,  	else  		for_each_netdev_rcu(net, dev)  			if (ipv6_chk_acast_dev(dev, addr)) { -				found = 1; +				found = true;  				break;  			}  	rcu_read_unlock();  	return found;  } +/*	check if this anycast address is link-local on given interface or + *	is global + */ +bool ipv6_chk_acast_addr_src(struct net *net, struct net_device *dev, +			     const struct in6_addr *addr) +{ +	return ipv6_chk_acast_addr(net, +				   (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL ? +				    dev : NULL), +				   addr); +}  #ifdef CONFIG_PROC_FS  struct ac6_iter_state { @@ -535,7 +520,7 @@ static const struct file_operations ac6_seq_fops = {  int __net_init ac6_proc_init(struct net *net)  { -	if (!proc_net_fops_create(net, "anycast6", S_IRUGO, &ac6_seq_fops)) +	if (!proc_create("anycast6", S_IRUGO, net->proc_net, &ac6_seq_fops))  		return -ENOMEM;  	return 0; @@ -543,7 +528,7 @@ int __net_init ac6_proc_init(struct net *net)  void ac6_proc_exit(struct net *net)  { -	proc_net_remove(net, "anycast6"); +	remove_proc_entry("anycast6", net->proc_net);  }  #endif  | 
