|
@@ -77,41 +77,40 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
|
|
|
pac->acl_next = NULL;
|
|
|
ipv6_addr_copy(&pac->acl_addr, addr);
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
if (ifindex == 0) {
|
|
|
struct rt6_info *rt;
|
|
|
|
|
|
rt = rt6_lookup(net, addr, NULL, 0, 0);
|
|
|
if (rt) {
|
|
|
dev = rt->rt6i_dev;
|
|
|
- dev_hold(dev);
|
|
|
dst_release(&rt->u.dst);
|
|
|
} else if (ishost) {
|
|
|
err = -EADDRNOTAVAIL;
|
|
|
- goto out_free_pac;
|
|
|
+ goto error;
|
|
|
} else {
|
|
|
/* router, no matching interface: just pick one */
|
|
|
-
|
|
|
- dev = dev_get_by_flags(net, IFF_UP, IFF_UP|IFF_LOOPBACK);
|
|
|
+ dev = dev_get_by_flags_rcu(net, IFF_UP,
|
|
|
+ IFF_UP | IFF_LOOPBACK);
|
|
|
}
|
|
|
} else
|
|
|
- dev = dev_get_by_index(net, ifindex);
|
|
|
+ dev = dev_get_by_index_rcu(net, ifindex);
|
|
|
|
|
|
if (dev == NULL) {
|
|
|
err = -ENODEV;
|
|
|
- goto out_free_pac;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
- idev = in6_dev_get(dev);
|
|
|
+ idev = __in6_dev_get(dev);
|
|
|
if (!idev) {
|
|
|
if (ifindex)
|
|
|
err = -ENODEV;
|
|
|
else
|
|
|
err = -EADDRNOTAVAIL;
|
|
|
- goto out_dev_put;
|
|
|
+ goto error;
|
|
|
}
|
|
|
/* reset ishost, now that we have a specific device */
|
|
|
ishost = !idev->cnf.forwarding;
|
|
|
- in6_dev_put(idev);
|
|
|
|
|
|
pac->acl_ifindex = dev->ifindex;
|
|
|
|
|
@@ -124,26 +123,22 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
|
|
|
if (ishost)
|
|
|
err = -EADDRNOTAVAIL;
|
|
|
if (err)
|
|
|
- goto out_dev_put;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
err = ipv6_dev_ac_inc(dev, addr);
|
|
|
- if (err)
|
|
|
- goto out_dev_put;
|
|
|
-
|
|
|
- write_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);
|
|
|
-
|
|
|
- dev_put(dev);
|
|
|
-
|
|
|
- return 0;
|
|
|
+ if (!err) {
|
|
|
+ write_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);
|
|
|
+ pac = NULL;
|
|
|
+ }
|
|
|
|
|
|
-out_dev_put:
|
|
|
- dev_put(dev);
|
|
|
-out_free_pac:
|
|
|
- sock_kfree_s(sk, pac, sizeof(*pac));
|
|
|
+error:
|
|
|
+ rcu_read_unlock();
|
|
|
+ if (pac)
|
|
|
+ sock_kfree_s(sk, pac, sizeof(*pac));
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -176,11 +171,12 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
|
|
|
|
|
|
write_unlock_bh(&ipv6_sk_ac_lock);
|
|
|
|
|
|
- dev = dev_get_by_index(net, pac->acl_ifindex);
|
|
|
- if (dev) {
|
|
|
+ rcu_read_lock();
|
|
|
+ dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
|
|
|
+ if (dev)
|
|
|
ipv6_dev_ac_dec(dev, &pac->acl_addr);
|
|
|
- dev_put(dev);
|
|
|
- }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
sock_kfree_s(sk, pac, sizeof(*pac));
|
|
|
return 0;
|
|
|
}
|
|
@@ -199,13 +195,12 @@ void ipv6_sock_ac_close(struct sock *sk)
|
|
|
write_unlock_bh(&ipv6_sk_ac_lock);
|
|
|
|
|
|
prev_index = 0;
|
|
|
+ rcu_read_lock();
|
|
|
while (pac) {
|
|
|
struct ipv6_ac_socklist *next = pac->acl_next;
|
|
|
|
|
|
if (pac->acl_ifindex != prev_index) {
|
|
|
- if (dev)
|
|
|
- dev_put(dev);
|
|
|
- dev = dev_get_by_index(net, pac->acl_ifindex);
|
|
|
+ dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
|
|
|
prev_index = pac->acl_ifindex;
|
|
|
}
|
|
|
if (dev)
|
|
@@ -213,8 +208,7 @@ void ipv6_sock_ac_close(struct sock *sk)
|
|
|
sock_kfree_s(sk, pac, sizeof(*pac));
|
|
|
pac = next;
|
|
|
}
|
|
|
- if (dev)
|
|
|
- dev_put(dev);
|
|
|
+ rcu_read_unlock();
|
|
|
}
|
|
|
|
|
|
#if 0
|
|
@@ -363,33 +357,32 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* called with rcu_read_lock() */
|
|
|
static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)
|
|
|
{
|
|
|
- int ret;
|
|
|
- struct inet6_dev *idev = in6_dev_get(dev);
|
|
|
+ struct inet6_dev *idev = __in6_dev_get(dev);
|
|
|
+
|
|
|
if (idev == NULL)
|
|
|
return -ENODEV;
|
|
|
- ret = __ipv6_dev_ac_dec(idev, addr);
|
|
|
- in6_dev_put(idev);
|
|
|
- return ret;
|
|
|
+ return __ipv6_dev_ac_dec(idev, 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)
|
|
|
{
|
|
|
struct inet6_dev *idev;
|
|
|
struct ifacaddr6 *aca;
|
|
|
|
|
|
- idev = in6_dev_get(dev);
|
|
|
+ idev = __in6_dev_get(dev);
|
|
|
if (idev) {
|
|
|
read_lock_bh(&idev->lock);
|
|
|
for (aca = idev->ac_list; aca; aca = aca->aca_next)
|
|
|
if (ipv6_addr_equal(&aca->aca_addr, addr))
|
|
|
break;
|
|
|
read_unlock_bh(&idev->lock);
|
|
|
- in6_dev_put(idev);
|
|
|
return aca != NULL;
|
|
|
}
|
|
|
return 0;
|
|
@@ -403,14 +396,15 @@ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
|
|
|
{
|
|
|
int found = 0;
|
|
|
|
|
|
- if (dev)
|
|
|
- return ipv6_chk_acast_dev(dev, addr);
|
|
|
rcu_read_lock();
|
|
|
- for_each_netdev_rcu(net, dev)
|
|
|
- if (ipv6_chk_acast_dev(dev, addr)) {
|
|
|
- found = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (dev)
|
|
|
+ found = ipv6_chk_acast_dev(dev, addr);
|
|
|
+ else
|
|
|
+ for_each_netdev_rcu(net, dev)
|
|
|
+ if (ipv6_chk_acast_dev(dev, addr)) {
|
|
|
+ found = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
rcu_read_unlock();
|
|
|
return found;
|
|
|
}
|