|
@@ -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;
|