ipv6: introduce per-interface counter for dad-completed ipv6 addresses
authorHannes Frederic Sowa <hannes@stressinduktion.org>
Wed, 26 Jun 2013 22:06:56 +0000 (00:06 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 29 Jun 2013 04:19:17 +0000 (21:19 -0700)
To reduce the number of unnecessary router solicitations, MLDv2 and IGMPv3
messages we need to track the number of valid (as in non-optimistic,
no-dad-failed and non-tentative) link-local addresses. Therefore, this
patch implements a valid_ll_addr_cnt in struct inet6_dev.

We now only emit router solicitations if the first link-local address
finishes duplicate address detection.

The changes for MLDv2 and IGMPv3 are in a follow-up patch.

While there, also simplify one if statement(one minor nit I made in one
of my previous patches):

if (!...)
do();
else
return;

<<into>>

if (...)
return;
do();

Cc: Flavio Leitner <fbl@redhat.com>
Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Cc: David Stevens <dlstevens@us.ibm.com>
Suggested-by: David Stevens <dlstevens@us.ibm.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Flavio Leitner <fbl@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/if_inet6.h
net/ipv6/addrconf.c

index e4c5a2d2ba34fdd18b3faf01af1b9669cc2e82b6..1628b8f5fb2612480550807a1f512e5282f019fb 100644 (file)
@@ -166,6 +166,7 @@ struct inet6_dev {
        struct net_device       *dev;
 
        struct list_head        addr_list;
+       int                     valid_ll_addr_cnt;
 
        struct ifmcaddr6        *mc_list;
        struct ifmcaddr6        *mc_tomb;
index 4e4cc1fc26d148e51f5a3fa8e8f950a16eaa7de6..20d92ff2d690801d486b0da07ebca1f028bb827c 100644 (file)
@@ -3277,6 +3277,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 {
        struct net_device *dev = ifp->idev->dev;
        struct in6_addr lladdr;
+       bool send_rs;
 
        addrconf_del_dad_timer(ifp);
 
@@ -3290,20 +3291,25 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
           router advertisements, start sending router solicitations.
         */
 
-       if (ipv6_accept_ra(ifp->idev) &&
-           ifp->idev->cnf.rtr_solicits > 0 &&
-           (dev->flags&IFF_LOOPBACK) == 0 &&
-           (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+       read_lock_bh(&ifp->idev->lock);
+       spin_lock(&ifp->lock);
+       send_rs = ipv6_accept_ra(ifp->idev) &&
+                 ifp->idev->cnf.rtr_solicits > 0 &&
+                 (dev->flags&IFF_LOOPBACK) == 0 &&
+                 ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
+                 ifp->idev->valid_ll_addr_cnt == 1;
+       spin_unlock(&ifp->lock);
+       read_unlock_bh(&ifp->idev->lock);
+
+       if (send_rs) {
                /*
                 *      If a host as already performed a random delay
                 *      [...] as part of DAD [...] there is no need
                 *      to delay again before sending the first RS
                 */
-               if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
-                       ndisc_send_rs(dev, &lladdr,
-                                     &in6addr_linklocal_allrouters);
-               else
+               if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
                        return;
+               ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters);
 
                write_lock_bh(&ifp->idev->lock);
                spin_lock(&ifp->lock);
@@ -4576,6 +4582,19 @@ errout:
                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)
 {
        struct net *net = dev_net(ifp->idev->dev);
@@ -4584,6 +4603,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 
        switch (event) {
        case RTM_NEWADDR:
+               update_valid_ll_addr_cnt(ifp, 1);
+
                /*
                 * If the address was optimistic
                 * we inserted the route at the start of
@@ -4599,6 +4620,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                                              ifp->idev->dev, 0, 0);
                break;
        case RTM_DELADDR:
+               update_valid_ll_addr_cnt(ifp, -1);
+
                if (ifp->idev->cnf.forwarding)
                        addrconf_leave_anycast(ifp);
                addrconf_leave_solict(ifp->idev, &ifp->addr);