ipv6: introduce per-interface counter for dad-completed ipv6 addresses

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>
This commit is contained in:
Hannes Frederic Sowa 2013-06-27 00:06:56 +02:00 committed by David S. Miller
parent ae0d67505c
commit 1ec047eb47
2 changed files with 32 additions and 8 deletions

View File

@ -166,6 +166,7 @@ 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;

View File

@ -3277,6 +3277,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
{ {
struct net_device *dev = ifp->idev->dev; struct net_device *dev = ifp->idev->dev;
struct in6_addr lladdr; struct in6_addr lladdr;
bool send_rs;
addrconf_del_dad_timer(ifp); addrconf_del_dad_timer(ifp);
@ -3290,20 +3291,25 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
router advertisements, start sending router solicitations. router advertisements, start sending router solicitations.
*/ */
if (ipv6_accept_ra(ifp->idev) && read_lock_bh(&ifp->idev->lock);
ifp->idev->cnf.rtr_solicits > 0 && spin_lock(&ifp->lock);
(dev->flags&IFF_LOOPBACK) == 0 && send_rs = ipv6_accept_ra(ifp->idev) &&
(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { 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 * If a host as already performed a random delay
* [...] as part of DAD [...] there is no need * [...] as part of DAD [...] there is no need
* to delay again before sending the first RS * to delay again before sending the first RS
*/ */
if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
ndisc_send_rs(dev, &lladdr,
&in6addr_linklocal_allrouters);
else
return; return;
ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters);
write_lock_bh(&ifp->idev->lock); write_lock_bh(&ifp->idev->lock);
spin_lock(&ifp->lock); spin_lock(&ifp->lock);
@ -4576,6 +4582,19 @@ errout:
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);
@ -4584,6 +4603,8 @@ 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
@ -4599,6 +4620,8 @@ 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);