diff options
| author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-06-26 18:07:01 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-06-29 00:19:17 -0400 |
| commit | b173ee488dcc545e77ed482158a2f0d06d7a5860 (patch) | |
| tree | 1268f9ebef676bc7f9dddb69f923e067cbe1db26 | |
| parent | 1ec047eb4751e331bc61cff0e98f0db67db8b8dc (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.h | 1 | ||||
| -rw-r--r-- | include/net/if_inet6.h | 2 | ||||
| -rw-r--r-- | net/ipv6/addrconf.c | 17 | ||||
| -rw-r--r-- | net/ipv6/mcast.c | 52 |
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 | ||
| 158 | extern 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 | ||
| 1002 | static 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 | ||
| 1826 | static 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 | |||
| 1842 | void 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 | |||
| 1853 | static 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 | |||
| 1818 | static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, | 1866 | static 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; |
