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