Commit 11ffff75 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller

ipv6: simplify detection of first operational link-local address on interface

In commit 1ec047eb ("ipv6: introduce per-interface counter for
dad-completed ipv6 addresses") I build the detection of the first
operational link-local address much to complex. Additionally this code
now has a race condition.

Replace it with a much simpler variant, which just scans the address
list when duplicate address detection completes, to check if this is
the first valid link local address and send RS and MLD reports then.

Fixes: 1ec047eb ("ipv6: introduce per-interface counter for dad-completed ipv6 addresses")
Reported-by: default avatarJiri Pirko <jiri@resnulli.us>
Cc: Flavio Leitner <fbl@redhat.com>
Signed-off-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: default avatarFlavio Leitner <fbl@redhat.com>
Acked-by: default avatarJiri Pirko <jiri@resnulli.us>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 77f99ad1
...@@ -165,7 +165,6 @@ struct inet6_dev { ...@@ -165,7 +165,6 @@ struct inet6_dev {
struct net_device *dev; struct net_device *dev;
struct list_head addr_list; struct list_head addr_list;
int valid_ll_addr_cnt;
struct ifmcaddr6 *mc_list; struct ifmcaddr6 *mc_list;
struct ifmcaddr6 *mc_tomb; struct ifmcaddr6 *mc_tomb;
......
...@@ -3189,6 +3189,22 @@ static void addrconf_dad_timer(unsigned long data) ...@@ -3189,6 +3189,22 @@ static void addrconf_dad_timer(unsigned long data)
in6_ifa_put(ifp); in6_ifa_put(ifp);
} }
/* ifp->idev must be at least read locked */
static bool ipv6_lonely_lladdr(struct inet6_ifaddr *ifp)
{
struct inet6_ifaddr *ifpiter;
struct inet6_dev *idev = ifp->idev;
list_for_each_entry(ifpiter, &idev->addr_list, if_list) {
if (ifp != ifpiter && ifpiter->scope == IFA_LINK &&
(ifpiter->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|
IFA_F_OPTIMISTIC|IFA_F_DADFAILED)) ==
IFA_F_PERMANENT)
return false;
}
return true;
}
static void addrconf_dad_completed(struct inet6_ifaddr *ifp) static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
{ {
struct net_device *dev = ifp->idev->dev; struct net_device *dev = ifp->idev->dev;
...@@ -3208,14 +3224,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) ...@@ -3208,14 +3224,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
*/ */
read_lock_bh(&ifp->idev->lock); read_lock_bh(&ifp->idev->lock);
spin_lock(&ifp->lock); send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp);
send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
ifp->idev->valid_ll_addr_cnt == 1;
send_rs = send_mld && send_rs = send_mld &&
ipv6_accept_ra(ifp->idev) && ipv6_accept_ra(ifp->idev) &&
ifp->idev->cnf.rtr_solicits > 0 && ifp->idev->cnf.rtr_solicits > 0 &&
(dev->flags&IFF_LOOPBACK) == 0; (dev->flags&IFF_LOOPBACK) == 0;
spin_unlock(&ifp->lock);
read_unlock_bh(&ifp->idev->lock); read_unlock_bh(&ifp->idev->lock);
/* While dad is in progress mld report's source address is in6_addrany. /* While dad is in progress mld report's source address is in6_addrany.
...@@ -4512,19 +4525,6 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev, ...@@ -4512,19 +4525,6 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,
rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err); rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);
} }
static void update_valid_ll_addr_cnt(struct inet6_ifaddr *ifp, int count)
{
write_lock_bh(&ifp->idev->lock);
spin_lock(&ifp->lock);
if (((ifp->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|
IFA_F_DADFAILED)) == IFA_F_PERMANENT) &&
(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL))
ifp->idev->valid_ll_addr_cnt += count;
WARN_ON(ifp->idev->valid_ll_addr_cnt < 0);
spin_unlock(&ifp->lock);
write_unlock_bh(&ifp->idev->lock);
}
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
{ {
struct net *net = dev_net(ifp->idev->dev); struct net *net = dev_net(ifp->idev->dev);
...@@ -4533,8 +4533,6 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -4533,8 +4533,6 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
switch (event) { switch (event) {
case RTM_NEWADDR: case RTM_NEWADDR:
update_valid_ll_addr_cnt(ifp, 1);
/* /*
* If the address was optimistic * If the address was optimistic
* we inserted the route at the start of * we inserted the route at the start of
...@@ -4550,8 +4548,6 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -4550,8 +4548,6 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
ifp->idev->dev, 0, 0); ifp->idev->dev, 0, 0);
break; break;
case RTM_DELADDR: case RTM_DELADDR:
update_valid_ll_addr_cnt(ifp, -1);
if (ifp->idev->cnf.forwarding) if (ifp->idev->cnf.forwarding)
addrconf_leave_anycast(ifp); addrconf_leave_anycast(ifp);
addrconf_leave_solict(ifp->idev, &ifp->addr); addrconf_leave_solict(ifp->idev, &ifp->addr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment