xref: /openbmc/linux/net/ipv6/anycast.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	Anycast support for IPv6
41da177e4SLinus Torvalds  *	Linux INET6 implementation
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Authors:
71da177e4SLinus Torvalds  *	David L Stevens (dlstevens@us.ibm.com)
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	based heavily on net/ipv6/mcast.c
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
124fc268d2SRandy Dunlap #include <linux/capability.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
151da177e4SLinus Torvalds #include <linux/types.h>
161da177e4SLinus Torvalds #include <linux/random.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include <linux/socket.h>
191da177e4SLinus Torvalds #include <linux/sockios.h>
201da177e4SLinus Torvalds #include <linux/net.h>
211da177e4SLinus Torvalds #include <linux/in6.h>
221da177e4SLinus Torvalds #include <linux/netdevice.h>
231da177e4SLinus Torvalds #include <linux/if_arp.h>
241da177e4SLinus Torvalds #include <linux/route.h>
251da177e4SLinus Torvalds #include <linux/init.h>
261da177e4SLinus Torvalds #include <linux/proc_fs.h>
271da177e4SLinus Torvalds #include <linux/seq_file.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
291da177e4SLinus Torvalds 
30457c4cbcSEric W. Biederman #include <net/net_namespace.h>
311da177e4SLinus Torvalds #include <net/sock.h>
321da177e4SLinus Torvalds #include <net/snmp.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <net/ipv6.h>
351da177e4SLinus Torvalds #include <net/protocol.h>
361da177e4SLinus Torvalds #include <net/if_inet6.h>
371da177e4SLinus Torvalds #include <net/ndisc.h>
381da177e4SLinus Torvalds #include <net/addrconf.h>
391da177e4SLinus Torvalds #include <net/ip6_route.h>
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds #include <net/checksum.h>
421da177e4SLinus Torvalds 
432384d025SJeff Barnhill #define IN6_ADDR_HSIZE_SHIFT	8
442384d025SJeff Barnhill #define IN6_ADDR_HSIZE		BIT(IN6_ADDR_HSIZE_SHIFT)
452384d025SJeff Barnhill /*	anycast address hash table
462384d025SJeff Barnhill  */
472384d025SJeff Barnhill static struct hlist_head inet6_acaddr_lst[IN6_ADDR_HSIZE];
482384d025SJeff Barnhill static DEFINE_SPINLOCK(acaddr_hash_lock);
492384d025SJeff Barnhill 
50b71d1d42SEric Dumazet static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr);
511da177e4SLinus Torvalds 
inet6_acaddr_hash(struct net * net,const struct in6_addr * addr)522384d025SJeff Barnhill static u32 inet6_acaddr_hash(struct net *net, const struct in6_addr *addr)
532384d025SJeff Barnhill {
542384d025SJeff Barnhill 	u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net);
552384d025SJeff Barnhill 
562384d025SJeff Barnhill 	return hash_32(val, IN6_ADDR_HSIZE_SHIFT);
572384d025SJeff Barnhill }
582384d025SJeff Barnhill 
591da177e4SLinus Torvalds /*
601da177e4SLinus Torvalds  *	socket join an anycast group
611da177e4SLinus Torvalds  */
621da177e4SLinus Torvalds 
ipv6_sock_ac_join(struct sock * sk,int ifindex,const struct in6_addr * addr)63b71d1d42SEric Dumazet int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
661da177e4SLinus Torvalds 	struct net_device *dev = NULL;
671da177e4SLinus Torvalds 	struct inet6_dev *idev;
681da177e4SLinus Torvalds 	struct ipv6_ac_socklist *pac;
696ab57e7eSDaniel Lezcano 	struct net *net = sock_net(sk);
7053b7997fSYOSHIFUJI Hideaki 	int	ishost = !net->ipv6.devconf_all->forwarding;
711da177e4SLinus Torvalds 	int	err = 0;
721da177e4SLinus Torvalds 
73c4a6853dSMarcelo Ricardo Leitner 	ASSERT_RTNL();
74c4a6853dSMarcelo Ricardo Leitner 
75af31f412SEric W. Biederman 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
761da177e4SLinus Torvalds 		return -EPERM;
771da177e4SLinus Torvalds 	if (ipv6_addr_is_multicast(addr))
781da177e4SLinus Torvalds 		return -EINVAL;
79232378e8SDavid Ahern 
80232378e8SDavid Ahern 	if (ifindex)
81232378e8SDavid Ahern 		dev = __dev_get_by_index(net, ifindex);
82232378e8SDavid Ahern 
83232378e8SDavid Ahern 	if (ipv6_chk_addr_and_flags(net, addr, dev, true, 0, IFA_F_TENTATIVE))
841da177e4SLinus Torvalds 		return -EINVAL;
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
8763159f29SIan Morris 	if (!pac)
881da177e4SLinus Torvalds 		return -ENOMEM;
891da177e4SLinus Torvalds 	pac->acl_next = NULL;
904e3fd7a0SAlexey Dobriyan 	pac->acl_addr = *addr;
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	if (ifindex == 0) {
931da177e4SLinus Torvalds 		struct rt6_info *rt;
941da177e4SLinus Torvalds 
95b75cc8f9SDavid Ahern 		rt = rt6_lookup(net, addr, NULL, 0, NULL, 0);
961da177e4SLinus Torvalds 		if (rt) {
97d1918542SDavid S. Miller 			dev = rt->dst.dev;
9894e187c0SAmerigo Wang 			ip6_rt_put(rt);
991da177e4SLinus Torvalds 		} else if (ishost) {
1001da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
101bb69ae04SEric Dumazet 			goto error;
1021da177e4SLinus Torvalds 		} else {
1031da177e4SLinus Torvalds 			/* router, no matching interface: just pick one */
1046c555490SWANG Cong 			dev = __dev_get_by_flags(net, IFF_UP,
105bb69ae04SEric Dumazet 						 IFF_UP | IFF_LOOPBACK);
1061da177e4SLinus Torvalds 		}
107232378e8SDavid Ahern 	}
1081da177e4SLinus Torvalds 
10963159f29SIan Morris 	if (!dev) {
1101da177e4SLinus Torvalds 		err = -ENODEV;
111bb69ae04SEric Dumazet 		goto error;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 
114bb69ae04SEric Dumazet 	idev = __in6_dev_get(dev);
1151da177e4SLinus Torvalds 	if (!idev) {
1161da177e4SLinus Torvalds 		if (ifindex)
1171da177e4SLinus Torvalds 			err = -ENODEV;
1181da177e4SLinus Torvalds 		else
1191da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
120bb69ae04SEric Dumazet 		goto error;
1211da177e4SLinus Torvalds 	}
1221da177e4SLinus Torvalds 	/* reset ishost, now that we have a specific device */
1231da177e4SLinus Torvalds 	ishost = !idev->cnf.forwarding;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	pac->acl_ifindex = dev->ifindex;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	/* XXX
1281da177e4SLinus Torvalds 	 * For hosts, allow link-local or matching prefix anycasts.
1291da177e4SLinus Torvalds 	 * This obviates the need for propagating anycast routes while
1301da177e4SLinus Torvalds 	 * still allowing some non-router anycast participation.
1311da177e4SLinus Torvalds 	 */
13252eeeb84SYOSHIFUJI Hideaki 	if (!ipv6_chk_prefix(addr, dev)) {
1331da177e4SLinus Torvalds 		if (ishost)
1341da177e4SLinus Torvalds 			err = -EADDRNOTAVAIL;
1351da177e4SLinus Torvalds 		if (err)
136bb69ae04SEric Dumazet 			goto error;
1371da177e4SLinus Torvalds 	}
1381da177e4SLinus Torvalds 
139013b4d90SWANG Cong 	err = __ipv6_dev_ac_inc(idev, addr);
140bb69ae04SEric Dumazet 	if (!err) {
1411da177e4SLinus Torvalds 		pac->acl_next = np->ipv6_ac_list;
1421da177e4SLinus Torvalds 		np->ipv6_ac_list = pac;
143bb69ae04SEric Dumazet 		pac = NULL;
144bb69ae04SEric Dumazet 	}
1451da177e4SLinus Torvalds 
146bb69ae04SEric Dumazet error:
147bb69ae04SEric Dumazet 	if (pac)
1481da177e4SLinus Torvalds 		sock_kfree_s(sk, pac, sizeof(*pac));
1491da177e4SLinus Torvalds 	return err;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds /*
1531da177e4SLinus Torvalds  *	socket leave an anycast group
1541da177e4SLinus Torvalds  */
ipv6_sock_ac_drop(struct sock * sk,int ifindex,const struct in6_addr * addr)155b71d1d42SEric Dumazet int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
1581da177e4SLinus Torvalds 	struct net_device *dev;
1591da177e4SLinus Torvalds 	struct ipv6_ac_socklist *pac, *prev_pac;
1606ab57e7eSDaniel Lezcano 	struct net *net = sock_net(sk);
1611da177e4SLinus Torvalds 
162c4a6853dSMarcelo Ricardo Leitner 	ASSERT_RTNL();
163c4a6853dSMarcelo Ricardo Leitner 
1641da177e4SLinus Torvalds 	prev_pac = NULL;
1651da177e4SLinus Torvalds 	for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {
1661da177e4SLinus Torvalds 		if ((ifindex == 0 || pac->acl_ifindex == ifindex) &&
1671da177e4SLinus Torvalds 		     ipv6_addr_equal(&pac->acl_addr, addr))
1681da177e4SLinus Torvalds 			break;
1691da177e4SLinus Torvalds 		prev_pac = pac;
1701da177e4SLinus Torvalds 	}
171c4a6853dSMarcelo Ricardo Leitner 	if (!pac)
1721da177e4SLinus Torvalds 		return -ENOENT;
1731da177e4SLinus Torvalds 	if (prev_pac)
1741da177e4SLinus Torvalds 		prev_pac->acl_next = pac->acl_next;
1751da177e4SLinus Torvalds 	else
1761da177e4SLinus Torvalds 		np->ipv6_ac_list = pac->acl_next;
1771da177e4SLinus Torvalds 
1786c555490SWANG Cong 	dev = __dev_get_by_index(net, pac->acl_ifindex);
179bb69ae04SEric Dumazet 	if (dev)
1801da177e4SLinus Torvalds 		ipv6_dev_ac_dec(dev, &pac->acl_addr);
181bb69ae04SEric Dumazet 
1821da177e4SLinus Torvalds 	sock_kfree_s(sk, pac, sizeof(*pac));
1831da177e4SLinus Torvalds 	return 0;
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
__ipv6_sock_ac_close(struct sock * sk)1868c0de6e9SCong Wang void __ipv6_sock_ac_close(struct sock *sk)
1871da177e4SLinus Torvalds {
1881da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
1891da177e4SLinus Torvalds 	struct net_device *dev = NULL;
1901da177e4SLinus Torvalds 	struct ipv6_ac_socklist *pac;
1916ab57e7eSDaniel Lezcano 	struct net *net = sock_net(sk);
1921da177e4SLinus Torvalds 	int	prev_index;
1931da177e4SLinus Torvalds 
1948c0de6e9SCong Wang 	ASSERT_RTNL();
1951da177e4SLinus Torvalds 	pac = np->ipv6_ac_list;
1961da177e4SLinus Torvalds 	np->ipv6_ac_list = NULL;
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	prev_index = 0;
1991da177e4SLinus Torvalds 	while (pac) {
2001da177e4SLinus Torvalds 		struct ipv6_ac_socklist *next = pac->acl_next;
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 		if (pac->acl_ifindex != prev_index) {
2036c555490SWANG Cong 			dev = __dev_get_by_index(net, pac->acl_ifindex);
2041da177e4SLinus Torvalds 			prev_index = pac->acl_ifindex;
2051da177e4SLinus Torvalds 		}
2061da177e4SLinus Torvalds 		if (dev)
2071da177e4SLinus Torvalds 			ipv6_dev_ac_dec(dev, &pac->acl_addr);
2081da177e4SLinus Torvalds 		sock_kfree_s(sk, pac, sizeof(*pac));
2091da177e4SLinus Torvalds 		pac = next;
2101da177e4SLinus Torvalds 	}
2118c0de6e9SCong Wang }
2128c0de6e9SCong Wang 
ipv6_sock_ac_close(struct sock * sk)2138c0de6e9SCong Wang void ipv6_sock_ac_close(struct sock *sk)
2148c0de6e9SCong Wang {
2158c0de6e9SCong Wang 	struct ipv6_pinfo *np = inet6_sk(sk);
2168c0de6e9SCong Wang 
2178c0de6e9SCong Wang 	if (!np->ipv6_ac_list)
2188c0de6e9SCong Wang 		return;
2198c0de6e9SCong Wang 	rtnl_lock();
2208c0de6e9SCong Wang 	__ipv6_sock_ac_close(sk);
221a9ed4a29SSabrina Dubroca 	rtnl_unlock();
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
ipv6_add_acaddr_hash(struct net * net,struct ifacaddr6 * aca)2242384d025SJeff Barnhill static void ipv6_add_acaddr_hash(struct net *net, struct ifacaddr6 *aca)
2252384d025SJeff Barnhill {
2262384d025SJeff Barnhill 	unsigned int hash = inet6_acaddr_hash(net, &aca->aca_addr);
2272384d025SJeff Barnhill 
2282384d025SJeff Barnhill 	spin_lock(&acaddr_hash_lock);
2292384d025SJeff Barnhill 	hlist_add_head_rcu(&aca->aca_addr_lst, &inet6_acaddr_lst[hash]);
2302384d025SJeff Barnhill 	spin_unlock(&acaddr_hash_lock);
2312384d025SJeff Barnhill }
2322384d025SJeff Barnhill 
ipv6_del_acaddr_hash(struct ifacaddr6 * aca)2332384d025SJeff Barnhill static void ipv6_del_acaddr_hash(struct ifacaddr6 *aca)
2342384d025SJeff Barnhill {
2352384d025SJeff Barnhill 	spin_lock(&acaddr_hash_lock);
2362384d025SJeff Barnhill 	hlist_del_init_rcu(&aca->aca_addr_lst);
2372384d025SJeff Barnhill 	spin_unlock(&acaddr_hash_lock);
2382384d025SJeff Barnhill }
2392384d025SJeff Barnhill 
aca_get(struct ifacaddr6 * aca)24083aa29eeSWANG Cong static void aca_get(struct ifacaddr6 *aca)
24183aa29eeSWANG Cong {
242affa78bcSReshetova, Elena 	refcount_inc(&aca->aca_refcnt);
24383aa29eeSWANG Cong }
24483aa29eeSWANG Cong 
aca_free_rcu(struct rcu_head * h)2452384d025SJeff Barnhill static void aca_free_rcu(struct rcu_head *h)
2462384d025SJeff Barnhill {
2472384d025SJeff Barnhill 	struct ifacaddr6 *aca = container_of(h, struct ifacaddr6, rcu);
2482384d025SJeff Barnhill 
2492384d025SJeff Barnhill 	fib6_info_release(aca->aca_rt);
2502384d025SJeff Barnhill 	kfree(aca);
2512384d025SJeff Barnhill }
2522384d025SJeff Barnhill 
aca_put(struct ifacaddr6 * ac)2531da177e4SLinus Torvalds static void aca_put(struct ifacaddr6 *ac)
2541da177e4SLinus Torvalds {
255affa78bcSReshetova, Elena 	if (refcount_dec_and_test(&ac->aca_refcnt)) {
2562384d025SJeff Barnhill 		call_rcu(&ac->rcu, aca_free_rcu);
2571da177e4SLinus Torvalds 	}
2581da177e4SLinus Torvalds }
2591da177e4SLinus Torvalds 
aca_alloc(struct fib6_info * f6i,const struct in6_addr * addr)26093c2fb25SDavid Ahern static struct ifacaddr6 *aca_alloc(struct fib6_info *f6i,
26183aa29eeSWANG Cong 				   const struct in6_addr *addr)
26283aa29eeSWANG Cong {
26383aa29eeSWANG Cong 	struct ifacaddr6 *aca;
26483aa29eeSWANG Cong 
26583aa29eeSWANG Cong 	aca = kzalloc(sizeof(*aca), GFP_ATOMIC);
26663159f29SIan Morris 	if (!aca)
26783aa29eeSWANG Cong 		return NULL;
26883aa29eeSWANG Cong 
26983aa29eeSWANG Cong 	aca->aca_addr = *addr;
27093c2fb25SDavid Ahern 	fib6_info_hold(f6i);
27193c2fb25SDavid Ahern 	aca->aca_rt = f6i;
2722384d025SJeff Barnhill 	INIT_HLIST_NODE(&aca->aca_addr_lst);
27383aa29eeSWANG Cong 	aca->aca_users = 1;
27483aa29eeSWANG Cong 	/* aca_tstamp should be updated upon changes */
27583aa29eeSWANG Cong 	aca->aca_cstamp = aca->aca_tstamp = jiffies;
276affa78bcSReshetova, Elena 	refcount_set(&aca->aca_refcnt, 1);
27783aa29eeSWANG Cong 
27883aa29eeSWANG Cong 	return aca;
27983aa29eeSWANG Cong }
28083aa29eeSWANG Cong 
2811da177e4SLinus Torvalds /*
2821da177e4SLinus Torvalds  *	device anycast group inc (add if not found)
2831da177e4SLinus Torvalds  */
__ipv6_dev_ac_inc(struct inet6_dev * idev,const struct in6_addr * addr)284013b4d90SWANG Cong int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
2851da177e4SLinus Torvalds {
2861da177e4SLinus Torvalds 	struct ifacaddr6 *aca;
287360a9887SDavid Ahern 	struct fib6_info *f6i;
288afb1d4b5SDavid Ahern 	struct net *net;
2891da177e4SLinus Torvalds 	int err;
2901da177e4SLinus Torvalds 
291a9ed4a29SSabrina Dubroca 	ASSERT_RTNL();
292a9ed4a29SSabrina Dubroca 
2931da177e4SLinus Torvalds 	write_lock_bh(&idev->lock);
2941da177e4SLinus Torvalds 	if (idev->dead) {
2951da177e4SLinus Torvalds 		err = -ENODEV;
2961da177e4SLinus Torvalds 		goto out;
2971da177e4SLinus Torvalds 	}
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	for (aca = idev->ac_list; aca; aca = aca->aca_next) {
3001da177e4SLinus Torvalds 		if (ipv6_addr_equal(&aca->aca_addr, addr)) {
3011da177e4SLinus Torvalds 			aca->aca_users++;
3021da177e4SLinus Torvalds 			err = 0;
3031da177e4SLinus Torvalds 			goto out;
3041da177e4SLinus Torvalds 		}
3051da177e4SLinus Torvalds 	}
3061da177e4SLinus Torvalds 
307afb1d4b5SDavid Ahern 	net = dev_net(idev->dev);
308*7f6c4039SHangbin Liu 	f6i = addrconf_f6i_alloc(net, idev, addr, true, GFP_ATOMIC, NULL);
309360a9887SDavid Ahern 	if (IS_ERR(f6i)) {
310360a9887SDavid Ahern 		err = PTR_ERR(f6i);
31183aa29eeSWANG Cong 		goto out;
31283aa29eeSWANG Cong 	}
313360a9887SDavid Ahern 	aca = aca_alloc(f6i, addr);
31463159f29SIan Morris 	if (!aca) {
315360a9887SDavid Ahern 		fib6_info_release(f6i);
3161da177e4SLinus Torvalds 		err = -ENOMEM;
3171da177e4SLinus Torvalds 		goto out;
3181da177e4SLinus Torvalds 	}
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	aca->aca_next = idev->ac_list;
3211da177e4SLinus Torvalds 	idev->ac_list = aca;
32283aa29eeSWANG Cong 
32383aa29eeSWANG Cong 	/* Hold this for addrconf_join_solict() below before we unlock,
32483aa29eeSWANG Cong 	 * it is already exposed via idev->ac_list.
32583aa29eeSWANG Cong 	 */
32683aa29eeSWANG Cong 	aca_get(aca);
3271da177e4SLinus Torvalds 	write_unlock_bh(&idev->lock);
3281da177e4SLinus Torvalds 
3292384d025SJeff Barnhill 	ipv6_add_acaddr_hash(net, aca);
3302384d025SJeff Barnhill 
331360a9887SDavid Ahern 	ip6_ins_rt(net, f6i);
3321da177e4SLinus Torvalds 
333013b4d90SWANG Cong 	addrconf_join_solict(idev->dev, &aca->aca_addr);
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 	aca_put(aca);
3361da177e4SLinus Torvalds 	return 0;
3371da177e4SLinus Torvalds out:
3381da177e4SLinus Torvalds 	write_unlock_bh(&idev->lock);
3391da177e4SLinus Torvalds 	return err;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds /*
3431da177e4SLinus Torvalds  *	device anycast group decrement
3441da177e4SLinus Torvalds  */
__ipv6_dev_ac_dec(struct inet6_dev * idev,const struct in6_addr * addr)345b71d1d42SEric Dumazet int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
3461da177e4SLinus Torvalds {
3471da177e4SLinus Torvalds 	struct ifacaddr6 *aca, *prev_aca;
3481da177e4SLinus Torvalds 
349a9ed4a29SSabrina Dubroca 	ASSERT_RTNL();
350a9ed4a29SSabrina Dubroca 
3511da177e4SLinus Torvalds 	write_lock_bh(&idev->lock);
3521da177e4SLinus Torvalds 	prev_aca = NULL;
3531da177e4SLinus Torvalds 	for (aca = idev->ac_list; aca; aca = aca->aca_next) {
3541da177e4SLinus Torvalds 		if (ipv6_addr_equal(&aca->aca_addr, addr))
3551da177e4SLinus Torvalds 			break;
3561da177e4SLinus Torvalds 		prev_aca = aca;
3571da177e4SLinus Torvalds 	}
3581da177e4SLinus Torvalds 	if (!aca) {
3591da177e4SLinus Torvalds 		write_unlock_bh(&idev->lock);
3601da177e4SLinus Torvalds 		return -ENOENT;
3611da177e4SLinus Torvalds 	}
3621da177e4SLinus Torvalds 	if (--aca->aca_users > 0) {
3631da177e4SLinus Torvalds 		write_unlock_bh(&idev->lock);
3641da177e4SLinus Torvalds 		return 0;
3651da177e4SLinus Torvalds 	}
3661da177e4SLinus Torvalds 	if (prev_aca)
3671da177e4SLinus Torvalds 		prev_aca->aca_next = aca->aca_next;
3681da177e4SLinus Torvalds 	else
3691da177e4SLinus Torvalds 		idev->ac_list = aca->aca_next;
3701da177e4SLinus Torvalds 	write_unlock_bh(&idev->lock);
3712384d025SJeff Barnhill 	ipv6_del_acaddr_hash(aca);
3721da177e4SLinus Torvalds 	addrconf_leave_solict(idev, &aca->aca_addr);
3731da177e4SLinus Torvalds 
37411dd74b3SRoopa Prabhu 	ip6_del_rt(dev_net(idev->dev), aca->aca_rt, false);
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	aca_put(aca);
3771da177e4SLinus Torvalds 	return 0;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3806c555490SWANG Cong /* called with rtnl_lock() */
ipv6_dev_ac_dec(struct net_device * dev,const struct in6_addr * addr)381b71d1d42SEric Dumazet static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr)
3821da177e4SLinus Torvalds {
383bb69ae04SEric Dumazet 	struct inet6_dev *idev = __in6_dev_get(dev);
384bb69ae04SEric Dumazet 
38563159f29SIan Morris 	if (!idev)
3861da177e4SLinus Torvalds 		return -ENODEV;
387bb69ae04SEric Dumazet 	return __ipv6_dev_ac_dec(idev, addr);
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds 
ipv6_ac_destroy_dev(struct inet6_dev * idev)390381f4dcaSSabrina Dubroca void ipv6_ac_destroy_dev(struct inet6_dev *idev)
391381f4dcaSSabrina Dubroca {
392381f4dcaSSabrina Dubroca 	struct ifacaddr6 *aca;
393381f4dcaSSabrina Dubroca 
394381f4dcaSSabrina Dubroca 	write_lock_bh(&idev->lock);
395381f4dcaSSabrina Dubroca 	while ((aca = idev->ac_list) != NULL) {
396381f4dcaSSabrina Dubroca 		idev->ac_list = aca->aca_next;
397381f4dcaSSabrina Dubroca 		write_unlock_bh(&idev->lock);
398381f4dcaSSabrina Dubroca 
3992384d025SJeff Barnhill 		ipv6_del_acaddr_hash(aca);
4002384d025SJeff Barnhill 
401381f4dcaSSabrina Dubroca 		addrconf_leave_solict(idev, &aca->aca_addr);
402381f4dcaSSabrina Dubroca 
40311dd74b3SRoopa Prabhu 		ip6_del_rt(dev_net(idev->dev), aca->aca_rt, false);
404381f4dcaSSabrina Dubroca 
405381f4dcaSSabrina Dubroca 		aca_put(aca);
406381f4dcaSSabrina Dubroca 
407381f4dcaSSabrina Dubroca 		write_lock_bh(&idev->lock);
408381f4dcaSSabrina Dubroca 	}
409381f4dcaSSabrina Dubroca 	write_unlock_bh(&idev->lock);
410381f4dcaSSabrina Dubroca }
411381f4dcaSSabrina Dubroca 
4121da177e4SLinus Torvalds /*
4131da177e4SLinus Torvalds  *	check if the interface has this anycast address
414bb69ae04SEric Dumazet  *	called with rcu_read_lock()
4151da177e4SLinus Torvalds  */
ipv6_chk_acast_dev(struct net_device * dev,const struct in6_addr * addr)416a50feda5SEric Dumazet static bool ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *addr)
4171da177e4SLinus Torvalds {
4181da177e4SLinus Torvalds 	struct inet6_dev *idev;
4191da177e4SLinus Torvalds 	struct ifacaddr6 *aca;
4201da177e4SLinus Torvalds 
421bb69ae04SEric Dumazet 	idev = __in6_dev_get(dev);
4221da177e4SLinus Torvalds 	if (idev) {
4231da177e4SLinus Torvalds 		read_lock_bh(&idev->lock);
4241da177e4SLinus Torvalds 		for (aca = idev->ac_list; aca; aca = aca->aca_next)
4251da177e4SLinus Torvalds 			if (ipv6_addr_equal(&aca->aca_addr, addr))
4261da177e4SLinus Torvalds 				break;
4271da177e4SLinus Torvalds 		read_unlock_bh(&idev->lock);
428cfcabdccSStephen Hemminger 		return aca != NULL;
4291da177e4SLinus Torvalds 	}
430a50feda5SEric Dumazet 	return false;
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds /*
4341da177e4SLinus Torvalds  *	check if given interface (or any, if dev==0) has this anycast address
4351da177e4SLinus Torvalds  */
ipv6_chk_acast_addr(struct net * net,struct net_device * dev,const struct in6_addr * addr)436a50feda5SEric Dumazet bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
437b71d1d42SEric Dumazet 			 const struct in6_addr *addr)
4381da177e4SLinus Torvalds {
4392384d025SJeff Barnhill 	struct net_device *nh_dev;
4402384d025SJeff Barnhill 	struct ifacaddr6 *aca;
441a50feda5SEric Dumazet 	bool found = false;
4427562f876SPavel Emelianov 
443c6d14c84SEric Dumazet 	rcu_read_lock();
444bb69ae04SEric Dumazet 	if (dev)
445bb69ae04SEric Dumazet 		found = ipv6_chk_acast_dev(dev, addr);
4461c51dc9aSLi RongQing 	else {
4471c51dc9aSLi RongQing 		unsigned int hash = inet6_acaddr_hash(net, addr);
4481c51dc9aSLi RongQing 
4492384d025SJeff Barnhill 		hlist_for_each_entry_rcu(aca, &inet6_acaddr_lst[hash],
4502384d025SJeff Barnhill 					 aca_addr_lst) {
4512384d025SJeff Barnhill 			nh_dev = fib6_info_nh_dev(aca->aca_rt);
4522384d025SJeff Barnhill 			if (!nh_dev || !net_eq(dev_net(nh_dev), net))
4532384d025SJeff Barnhill 				continue;
4542384d025SJeff Barnhill 			if (ipv6_addr_equal(&aca->aca_addr, addr)) {
455a50feda5SEric Dumazet 				found = true;
4561da177e4SLinus Torvalds 				break;
4577562f876SPavel Emelianov 			}
4582384d025SJeff Barnhill 		}
4591c51dc9aSLi RongQing 	}
460c6d14c84SEric Dumazet 	rcu_read_unlock();
4617562f876SPavel Emelianov 	return found;
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds 
4647c90cc2dSFX Le Bail /*	check if this anycast address is link-local on given interface or
4657c90cc2dSFX Le Bail  *	is global
4667c90cc2dSFX Le Bail  */
ipv6_chk_acast_addr_src(struct net * net,struct net_device * dev,const struct in6_addr * addr)4677c90cc2dSFX Le Bail bool ipv6_chk_acast_addr_src(struct net *net, struct net_device *dev,
4687c90cc2dSFX Le Bail 			     const struct in6_addr *addr)
4697c90cc2dSFX Le Bail {
4707c90cc2dSFX Le Bail 	return ipv6_chk_acast_addr(net,
4717c90cc2dSFX Le Bail 				   (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL ?
4727c90cc2dSFX Le Bail 				    dev : NULL),
4737c90cc2dSFX Le Bail 				   addr);
4747c90cc2dSFX Le Bail }
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
4771da177e4SLinus Torvalds struct ac6_iter_state {
4786ab57e7eSDaniel Lezcano 	struct seq_net_private p;
4791da177e4SLinus Torvalds 	struct net_device *dev;
4801da177e4SLinus Torvalds 	struct inet6_dev *idev;
4811da177e4SLinus Torvalds };
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds #define ac6_seq_private(seq)	((struct ac6_iter_state *)(seq)->private)
4841da177e4SLinus Torvalds 
ac6_get_first(struct seq_file * seq)4851da177e4SLinus Torvalds static inline struct ifacaddr6 *ac6_get_first(struct seq_file *seq)
4861da177e4SLinus Torvalds {
4871da177e4SLinus Torvalds 	struct ifacaddr6 *im = NULL;
4881da177e4SLinus Torvalds 	struct ac6_iter_state *state = ac6_seq_private(seq);
4896ab57e7eSDaniel Lezcano 	struct net *net = seq_file_net(seq);
4901da177e4SLinus Torvalds 
4917562f876SPavel Emelianov 	state->idev = NULL;
492ce81b76aSEric Dumazet 	for_each_netdev_rcu(net, state->dev) {
4931da177e4SLinus Torvalds 		struct inet6_dev *idev;
494ce81b76aSEric Dumazet 		idev = __in6_dev_get(state->dev);
4951da177e4SLinus Torvalds 		if (!idev)
4961da177e4SLinus Torvalds 			continue;
4971da177e4SLinus Torvalds 		read_lock_bh(&idev->lock);
4981da177e4SLinus Torvalds 		im = idev->ac_list;
4991da177e4SLinus Torvalds 		if (im) {
5001da177e4SLinus Torvalds 			state->idev = idev;
5011da177e4SLinus Torvalds 			break;
5021da177e4SLinus Torvalds 		}
5031da177e4SLinus Torvalds 		read_unlock_bh(&idev->lock);
5041da177e4SLinus Torvalds 	}
5051da177e4SLinus Torvalds 	return im;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
ac6_get_next(struct seq_file * seq,struct ifacaddr6 * im)5081da177e4SLinus Torvalds static struct ifacaddr6 *ac6_get_next(struct seq_file *seq, struct ifacaddr6 *im)
5091da177e4SLinus Torvalds {
5101da177e4SLinus Torvalds 	struct ac6_iter_state *state = ac6_seq_private(seq);
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	im = im->aca_next;
5131da177e4SLinus Torvalds 	while (!im) {
514ce81b76aSEric Dumazet 		if (likely(state->idev != NULL))
5151da177e4SLinus Torvalds 			read_unlock_bh(&state->idev->lock);
516ce81b76aSEric Dumazet 
517ce81b76aSEric Dumazet 		state->dev = next_net_device_rcu(state->dev);
5181da177e4SLinus Torvalds 		if (!state->dev) {
5191da177e4SLinus Torvalds 			state->idev = NULL;
5201da177e4SLinus Torvalds 			break;
5211da177e4SLinus Torvalds 		}
522ce81b76aSEric Dumazet 		state->idev = __in6_dev_get(state->dev);
5231da177e4SLinus Torvalds 		if (!state->idev)
5241da177e4SLinus Torvalds 			continue;
5251da177e4SLinus Torvalds 		read_lock_bh(&state->idev->lock);
5261da177e4SLinus Torvalds 		im = state->idev->ac_list;
5271da177e4SLinus Torvalds 	}
5281da177e4SLinus Torvalds 	return im;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds 
ac6_get_idx(struct seq_file * seq,loff_t pos)5311da177e4SLinus Torvalds static struct ifacaddr6 *ac6_get_idx(struct seq_file *seq, loff_t pos)
5321da177e4SLinus Torvalds {
5331da177e4SLinus Torvalds 	struct ifacaddr6 *im = ac6_get_first(seq);
5341da177e4SLinus Torvalds 	if (im)
5351da177e4SLinus Torvalds 		while (pos && (im = ac6_get_next(seq, im)) != NULL)
5361da177e4SLinus Torvalds 			--pos;
5371da177e4SLinus Torvalds 	return pos ? NULL : im;
5381da177e4SLinus Torvalds }
5391da177e4SLinus Torvalds 
ac6_seq_start(struct seq_file * seq,loff_t * pos)5401da177e4SLinus Torvalds static void *ac6_seq_start(struct seq_file *seq, loff_t *pos)
541ce81b76aSEric Dumazet 	__acquires(RCU)
5421da177e4SLinus Torvalds {
543ce81b76aSEric Dumazet 	rcu_read_lock();
5441da177e4SLinus Torvalds 	return ac6_get_idx(seq, *pos);
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds 
ac6_seq_next(struct seq_file * seq,void * v,loff_t * pos)5471da177e4SLinus Torvalds static void *ac6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
5481da177e4SLinus Torvalds {
549ce81b76aSEric Dumazet 	struct ifacaddr6 *im = ac6_get_next(seq, v);
550ce81b76aSEric Dumazet 
5511da177e4SLinus Torvalds 	++*pos;
5521da177e4SLinus Torvalds 	return im;
5531da177e4SLinus Torvalds }
5541da177e4SLinus Torvalds 
ac6_seq_stop(struct seq_file * seq,void * v)5551da177e4SLinus Torvalds static void ac6_seq_stop(struct seq_file *seq, void *v)
556ce81b76aSEric Dumazet 	__releases(RCU)
5571da177e4SLinus Torvalds {
5581da177e4SLinus Torvalds 	struct ac6_iter_state *state = ac6_seq_private(seq);
559ce81b76aSEric Dumazet 
5601da177e4SLinus Torvalds 	if (likely(state->idev != NULL)) {
5611da177e4SLinus Torvalds 		read_unlock_bh(&state->idev->lock);
562ce81b76aSEric Dumazet 		state->idev = NULL;
5631da177e4SLinus Torvalds 	}
564ce81b76aSEric Dumazet 	rcu_read_unlock();
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
ac6_seq_show(struct seq_file * seq,void * v)5671da177e4SLinus Torvalds static int ac6_seq_show(struct seq_file *seq, void *v)
5681da177e4SLinus Torvalds {
5691da177e4SLinus Torvalds 	struct ifacaddr6 *im = (struct ifacaddr6 *)v;
5701da177e4SLinus Torvalds 	struct ac6_iter_state *state = ac6_seq_private(seq);
5711da177e4SLinus Torvalds 
5724b7a4274SHarvey Harrison 	seq_printf(seq, "%-4d %-15s %pi6 %5d\n",
5731da177e4SLinus Torvalds 		   state->dev->ifindex, state->dev->name,
574b071195dSHarvey Harrison 		   &im->aca_addr, im->aca_users);
5751da177e4SLinus Torvalds 	return 0;
5761da177e4SLinus Torvalds }
5771da177e4SLinus Torvalds 
57856b3d975SPhilippe De Muyter static const struct seq_operations ac6_seq_ops = {
5791da177e4SLinus Torvalds 	.start	=	ac6_seq_start,
5801da177e4SLinus Torvalds 	.next	=	ac6_seq_next,
5811da177e4SLinus Torvalds 	.stop	=	ac6_seq_stop,
5821da177e4SLinus Torvalds 	.show	=	ac6_seq_show,
5831da177e4SLinus Torvalds };
5841da177e4SLinus Torvalds 
ac6_proc_init(struct net * net)5852c8c1e72SAlexey Dobriyan int __net_init ac6_proc_init(struct net *net)
5861da177e4SLinus Torvalds {
587c3506372SChristoph Hellwig 	if (!proc_create_net("anycast6", 0444, net->proc_net, &ac6_seq_ops,
588c3506372SChristoph Hellwig 			sizeof(struct ac6_iter_state)))
5891da177e4SLinus Torvalds 		return -ENOMEM;
5901da177e4SLinus Torvalds 
5911da177e4SLinus Torvalds 	return 0;
5921da177e4SLinus Torvalds }
5931da177e4SLinus Torvalds 
ac6_proc_exit(struct net * net)5946ab57e7eSDaniel Lezcano void ac6_proc_exit(struct net *net)
5951da177e4SLinus Torvalds {
596ece31ffdSGao feng 	remove_proc_entry("anycast6", net->proc_net);
5971da177e4SLinus Torvalds }
5986915ed86SJeff Barnhill #endif
5992384d025SJeff Barnhill 
6002384d025SJeff Barnhill /*	Init / cleanup code
6012384d025SJeff Barnhill  */
ipv6_anycast_init(void)6022384d025SJeff Barnhill int __init ipv6_anycast_init(void)
6032384d025SJeff Barnhill {
6042384d025SJeff Barnhill 	int i;
6052384d025SJeff Barnhill 
6062384d025SJeff Barnhill 	for (i = 0; i < IN6_ADDR_HSIZE; i++)
6072384d025SJeff Barnhill 		INIT_HLIST_HEAD(&inet6_acaddr_lst[i]);
6082384d025SJeff Barnhill 	return 0;
6092384d025SJeff Barnhill }
6102384d025SJeff Barnhill 
ipv6_anycast_cleanup(void)6112384d025SJeff Barnhill void ipv6_anycast_cleanup(void)
6122384d025SJeff Barnhill {
6132384d025SJeff Barnhill 	int i;
6142384d025SJeff Barnhill 
6152384d025SJeff Barnhill 	spin_lock(&acaddr_hash_lock);
6162384d025SJeff Barnhill 	for (i = 0; i < IN6_ADDR_HSIZE; i++)
6172384d025SJeff Barnhill 		WARN_ON(!hlist_empty(&inet6_acaddr_lst[i]));
6182384d025SJeff Barnhill 	spin_unlock(&acaddr_hash_lock);
6192384d025SJeff Barnhill }
620