aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2013-06-26 18:07:01 -0400
committerDavid S. Miller <davem@davemloft.net>2013-06-29 00:19:17 -0400
commitb173ee488dcc545e77ed482158a2f0d06d7a5860 (patch)
tree1268f9ebef676bc7f9dddb69f923e067cbe1db26
parent1ec047eb4751e331bc61cff0e98f0db67db8b8dc (diff)
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>
-rw-r--r--include/net/addrconf.h1
-rw-r--r--include/net/if_inet6.h2
-rw-r--r--net/ipv6/addrconf.c17
-rw-r--r--net/ipv6/mcast.c52
4 files changed, 67 insertions, 5 deletions
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 21f702704f24..f68eaf574d7e 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -155,6 +155,7 @@ extern bool ipv6_chk_mcast_addr(struct net_device *dev,
155 const struct in6_addr *group, 155 const struct in6_addr *group,
156 const struct in6_addr *src_addr); 156 const struct in6_addr *src_addr);
157 157
158extern void ipv6_mc_dad_complete(struct inet6_dev *idev);
158/* 159/*
159 * identify MLD packets for MLD filter exceptions 160 * identify MLD packets for MLD filter exceptions
160 */ 161 */
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 1628b8f5fb26..736b5fb95474 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -174,10 +174,12 @@ struct inet6_dev {
174 unsigned char mc_qrv; 174 unsigned char mc_qrv;
175 unsigned char mc_gq_running; 175 unsigned char mc_gq_running;
176 unsigned char mc_ifc_count; 176 unsigned char mc_ifc_count;
177 unsigned char mc_dad_count;
177 unsigned long mc_v1_seen; 178 unsigned long mc_v1_seen;
178 unsigned long mc_maxdelay; 179 unsigned long mc_maxdelay;
179 struct timer_list mc_gq_timer; /* general query timer */ 180 struct timer_list mc_gq_timer; /* general query timer */
180 struct timer_list mc_ifc_timer; /* interface change timer */ 181 struct timer_list mc_ifc_timer; /* interface change timer */
182 struct timer_list mc_dad_timer; /* dad complete mc timer */
181 183
182 struct ifacaddr6 *ac_list; 184 struct ifacaddr6 *ac_list;
183 rwlock_t lock; 185 rwlock_t lock;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 20d92ff2d690..12dd2fec045c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3277,7 +3277,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
3277{ 3277{
3278 struct net_device *dev = ifp->idev->dev; 3278 struct net_device *dev = ifp->idev->dev;
3279 struct in6_addr lladdr; 3279 struct in6_addr lladdr;
3280 bool send_rs; 3280 bool send_rs, send_mld;
3281 3281
3282 addrconf_del_dad_timer(ifp); 3282 addrconf_del_dad_timer(ifp);
3283 3283
@@ -3293,14 +3293,21 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
3293 3293
3294 read_lock_bh(&ifp->idev->lock); 3294 read_lock_bh(&ifp->idev->lock);
3295 spin_lock(&ifp->lock); 3295 spin_lock(&ifp->lock);
3296 send_rs = ipv6_accept_ra(ifp->idev) && 3296 send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
3297 ifp->idev->valid_ll_addr_cnt == 1;
3298 send_rs = send_mld &&
3299 ipv6_accept_ra(ifp->idev) &&
3297 ifp->idev->cnf.rtr_solicits > 0 && 3300 ifp->idev->cnf.rtr_solicits > 0 &&
3298 (dev->flags&IFF_LOOPBACK) == 0 && 3301 (dev->flags&IFF_LOOPBACK) == 0;
3299 ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
3300 ifp->idev->valid_ll_addr_cnt == 1;
3301 spin_unlock(&ifp->lock); 3302 spin_unlock(&ifp->lock);
3302 read_unlock_bh(&ifp->idev->lock); 3303 read_unlock_bh(&ifp->idev->lock);
3303 3304
3305 /* While dad is in progress mld report's source address is in6_addrany.
3306 * Resend with proper ll now.
3307 */
3308 if (send_mld)
3309 ipv6_mc_dad_complete(ifp->idev);
3310
3304 if (send_rs) { 3311 if (send_rs) {
3305 /* 3312 /*
3306 * If a host as already performed a random delay 3313 * If a host as already performed a random delay
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 72c8bfe06bb4..502c877cbf10 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -999,6 +999,14 @@ static void mld_ifc_start_timer(struct inet6_dev *idev, int delay)
999 in6_dev_hold(idev); 999 in6_dev_hold(idev);
1000} 1000}
1001 1001
1002static void mld_dad_start_timer(struct inet6_dev *idev, int delay)
1003{
1004 int tv = net_random() % delay;
1005
1006 if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2))
1007 in6_dev_hold(idev);
1008}
1009
1002/* 1010/*
1003 * IGMP handling (alias multicast ICMPv6 messages) 1011 * IGMP handling (alias multicast ICMPv6 messages)
1004 */ 1012 */
@@ -1815,6 +1823,46 @@ err_out:
1815 goto out; 1823 goto out;
1816} 1824}
1817 1825
1826static void mld_resend_report(struct inet6_dev *idev)
1827{
1828 if (MLD_V1_SEEN(idev)) {
1829 struct ifmcaddr6 *mcaddr;
1830 read_lock_bh(&idev->lock);
1831 for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) {
1832 if (!(mcaddr->mca_flags & MAF_NOREPORT))
1833 igmp6_send(&mcaddr->mca_addr, idev->dev,
1834 ICMPV6_MGM_REPORT);
1835 }
1836 read_unlock_bh(&idev->lock);
1837 } else {
1838 mld_send_report(idev, NULL);
1839 }
1840}
1841
1842void ipv6_mc_dad_complete(struct inet6_dev *idev)
1843{
1844 idev->mc_dad_count = idev->mc_qrv;
1845 if (idev->mc_dad_count) {
1846 mld_resend_report(idev);
1847 idev->mc_dad_count--;
1848 if (idev->mc_dad_count)
1849 mld_dad_start_timer(idev, idev->mc_maxdelay);
1850 }
1851}
1852
1853static void mld_dad_timer_expire(unsigned long data)
1854{
1855 struct inet6_dev *idev = (struct inet6_dev *)data;
1856
1857 mld_resend_report(idev);
1858 if (idev->mc_dad_count) {
1859 idev->mc_dad_count--;
1860 if (idev->mc_dad_count)
1861 mld_dad_start_timer(idev, idev->mc_maxdelay);
1862 }
1863 __in6_dev_put(idev);
1864}
1865
1818static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, 1866static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
1819 const struct in6_addr *psfsrc) 1867 const struct in6_addr *psfsrc)
1820{ 1868{
@@ -2232,6 +2280,8 @@ void ipv6_mc_down(struct inet6_dev *idev)
2232 idev->mc_gq_running = 0; 2280 idev->mc_gq_running = 0;
2233 if (del_timer(&idev->mc_gq_timer)) 2281 if (del_timer(&idev->mc_gq_timer))
2234 __in6_dev_put(idev); 2282 __in6_dev_put(idev);
2283 if (del_timer(&idev->mc_dad_timer))
2284 __in6_dev_put(idev);
2235 2285
2236 for (i = idev->mc_list; i; i=i->next) 2286 for (i = idev->mc_list; i; i=i->next)
2237 igmp6_group_dropped(i); 2287 igmp6_group_dropped(i);
@@ -2268,6 +2318,8 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
2268 idev->mc_ifc_count = 0; 2318 idev->mc_ifc_count = 0;
2269 setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire, 2319 setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire,
2270 (unsigned long)idev); 2320 (unsigned long)idev);
2321 setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire,
2322 (unsigned long)idev);
2271 idev->mc_qrv = MLD_QRV_DEFAULT; 2323 idev->mc_qrv = MLD_QRV_DEFAULT;
2272 idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL; 2324 idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
2273 idev->mc_v1_seen = 0; 2325 idev->mc_v1_seen = 0;