ipv6: resend MLD report if a link-local address completes DAD

RFC3590/RFC3810 specifies we should resend MLD reports as soon as a
valid link-local address is available.

We now use the valid_ll_addr_cnt to check if it is necessary to resend
a new report.

Changes since Flavio Leitner's version:
a) adapt for valid_ll_addr_cnt
b) resend first reports directly in the path and just arm the timer for
   mc_qrv-1 resends.

Reported-by: Flavio Leitner <fleitner@redhat.com>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: David Stevens <dlstevens@us.ibm.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-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:07:01 +02:00 committed by David S. Miller
parent 1ec047eb47
commit b173ee488d
4 changed files with 67 additions and 5 deletions

View File

@ -155,6 +155,7 @@ extern bool ipv6_chk_mcast_addr(struct net_device *dev,
const struct in6_addr *group,
const struct in6_addr *src_addr);
extern void ipv6_mc_dad_complete(struct inet6_dev *idev);
/*
* identify MLD packets for MLD filter exceptions
*/

View File

@ -174,10 +174,12 @@ struct inet6_dev {
unsigned char mc_qrv;
unsigned char mc_gq_running;
unsigned char mc_ifc_count;
unsigned char mc_dad_count;
unsigned long mc_v1_seen;
unsigned long mc_maxdelay;
struct timer_list mc_gq_timer; /* general query timer */
struct timer_list mc_ifc_timer; /* interface change timer */
struct timer_list mc_dad_timer; /* dad complete mc timer */
struct ifacaddr6 *ac_list;
rwlock_t lock;

View File

@ -3277,7 +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;
bool send_rs, send_mld;
addrconf_del_dad_timer(ifp);
@ -3293,14 +3293,21 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
read_lock_bh(&ifp->idev->lock);
spin_lock(&ifp->lock);
send_rs = ipv6_accept_ra(ifp->idev) &&
send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
ifp->idev->valid_ll_addr_cnt == 1;
send_rs = send_mld &&
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;
(dev->flags&IFF_LOOPBACK) == 0;
spin_unlock(&ifp->lock);
read_unlock_bh(&ifp->idev->lock);
/* While dad is in progress mld report's source address is in6_addrany.
* Resend with proper ll now.
*/
if (send_mld)
ipv6_mc_dad_complete(ifp->idev);
if (send_rs) {
/*
* If a host as already performed a random delay

View File

@ -999,6 +999,14 @@ static void mld_ifc_start_timer(struct inet6_dev *idev, int delay)
in6_dev_hold(idev);
}
static void mld_dad_start_timer(struct inet6_dev *idev, int delay)
{
int tv = net_random() % delay;
if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2))
in6_dev_hold(idev);
}
/*
* IGMP handling (alias multicast ICMPv6 messages)
*/
@ -1815,6 +1823,46 @@ err_out:
goto out;
}
static void mld_resend_report(struct inet6_dev *idev)
{
if (MLD_V1_SEEN(idev)) {
struct ifmcaddr6 *mcaddr;
read_lock_bh(&idev->lock);
for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) {
if (!(mcaddr->mca_flags & MAF_NOREPORT))
igmp6_send(&mcaddr->mca_addr, idev->dev,
ICMPV6_MGM_REPORT);
}
read_unlock_bh(&idev->lock);
} else {
mld_send_report(idev, NULL);
}
}
void ipv6_mc_dad_complete(struct inet6_dev *idev)
{
idev->mc_dad_count = idev->mc_qrv;
if (idev->mc_dad_count) {
mld_resend_report(idev);
idev->mc_dad_count--;
if (idev->mc_dad_count)
mld_dad_start_timer(idev, idev->mc_maxdelay);
}
}
static void mld_dad_timer_expire(unsigned long data)
{
struct inet6_dev *idev = (struct inet6_dev *)data;
mld_resend_report(idev);
if (idev->mc_dad_count) {
idev->mc_dad_count--;
if (idev->mc_dad_count)
mld_dad_start_timer(idev, idev->mc_maxdelay);
}
__in6_dev_put(idev);
}
static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
const struct in6_addr *psfsrc)
{
@ -2232,6 +2280,8 @@ void ipv6_mc_down(struct inet6_dev *idev)
idev->mc_gq_running = 0;
if (del_timer(&idev->mc_gq_timer))
__in6_dev_put(idev);
if (del_timer(&idev->mc_dad_timer))
__in6_dev_put(idev);
for (i = idev->mc_list; i; i=i->next)
igmp6_group_dropped(i);
@ -2268,6 +2318,8 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
idev->mc_ifc_count = 0;
setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire,
(unsigned long)idev);
setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire,
(unsigned long)idev);
idev->mc_qrv = MLD_QRV_DEFAULT;
idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
idev->mc_v1_seen = 0;