diff options
Diffstat (limited to 'net/ipv4/igmp.c')
-rw-r--r-- | net/ipv4/igmp.c | 87 |
1 files changed, 29 insertions, 58 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index e0e77e297de3..672e476c8c8a 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -149,17 +149,11 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc); | |||
149 | static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, | 149 | static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, |
150 | int sfcount, __be32 *psfsrc, int delta); | 150 | int sfcount, __be32 *psfsrc, int delta); |
151 | 151 | ||
152 | |||
153 | static void ip_mc_list_reclaim(struct rcu_head *head) | ||
154 | { | ||
155 | kfree(container_of(head, struct ip_mc_list, rcu)); | ||
156 | } | ||
157 | |||
158 | static void ip_ma_put(struct ip_mc_list *im) | 152 | static void ip_ma_put(struct ip_mc_list *im) |
159 | { | 153 | { |
160 | if (atomic_dec_and_test(&im->refcnt)) { | 154 | if (atomic_dec_and_test(&im->refcnt)) { |
161 | in_dev_put(im->interface); | 155 | in_dev_put(im->interface); |
162 | call_rcu(&im->rcu, ip_mc_list_reclaim); | 156 | kfree_rcu(im, rcu); |
163 | } | 157 | } |
164 | } | 158 | } |
165 | 159 | ||
@@ -309,6 +303,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | |||
309 | struct iphdr *pip; | 303 | struct iphdr *pip; |
310 | struct igmpv3_report *pig; | 304 | struct igmpv3_report *pig; |
311 | struct net *net = dev_net(dev); | 305 | struct net *net = dev_net(dev); |
306 | struct flowi4 fl4; | ||
312 | 307 | ||
313 | while (1) { | 308 | while (1) { |
314 | skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), | 309 | skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), |
@@ -321,18 +316,11 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | |||
321 | } | 316 | } |
322 | igmp_skb_size(skb) = size; | 317 | igmp_skb_size(skb) = size; |
323 | 318 | ||
324 | { | 319 | rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, |
325 | struct flowi fl = { .oif = dev->ifindex, | 320 | 0, 0, |
326 | .fl4_dst = IGMPV3_ALL_MCR, | 321 | IPPROTO_IGMP, 0, dev->ifindex); |
327 | .proto = IPPROTO_IGMP }; | 322 | if (IS_ERR(rt)) { |
328 | if (ip_route_output_key(net, &rt, &fl)) { | ||
329 | kfree_skb(skb); | ||
330 | return NULL; | ||
331 | } | ||
332 | } | ||
333 | if (rt->rt_src == 0) { | ||
334 | kfree_skb(skb); | 323 | kfree_skb(skb); |
335 | ip_rt_put(rt); | ||
336 | return NULL; | 324 | return NULL; |
337 | } | 325 | } |
338 | 326 | ||
@@ -350,8 +338,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | |||
350 | pip->tos = 0xc0; | 338 | pip->tos = 0xc0; |
351 | pip->frag_off = htons(IP_DF); | 339 | pip->frag_off = htons(IP_DF); |
352 | pip->ttl = 1; | 340 | pip->ttl = 1; |
353 | pip->daddr = rt->rt_dst; | 341 | pip->daddr = fl4.daddr; |
354 | pip->saddr = rt->rt_src; | 342 | pip->saddr = fl4.saddr; |
355 | pip->protocol = IPPROTO_IGMP; | 343 | pip->protocol = IPPROTO_IGMP; |
356 | pip->tot_len = 0; /* filled in later */ | 344 | pip->tot_len = 0; /* filled in later */ |
357 | ip_select_ident(pip, &rt->dst, NULL); | 345 | ip_select_ident(pip, &rt->dst, NULL); |
@@ -657,6 +645,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
657 | struct net_device *dev = in_dev->dev; | 645 | struct net_device *dev = in_dev->dev; |
658 | struct net *net = dev_net(dev); | 646 | struct net *net = dev_net(dev); |
659 | __be32 group = pmc ? pmc->multiaddr : 0; | 647 | __be32 group = pmc ? pmc->multiaddr : 0; |
648 | struct flowi4 fl4; | ||
660 | __be32 dst; | 649 | __be32 dst; |
661 | 650 | ||
662 | if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) | 651 | if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) |
@@ -666,17 +655,11 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
666 | else | 655 | else |
667 | dst = group; | 656 | dst = group; |
668 | 657 | ||
669 | { | 658 | rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, |
670 | struct flowi fl = { .oif = dev->ifindex, | 659 | 0, 0, |
671 | .fl4_dst = dst, | 660 | IPPROTO_IGMP, 0, dev->ifindex); |
672 | .proto = IPPROTO_IGMP }; | 661 | if (IS_ERR(rt)) |
673 | if (ip_route_output_key(net, &rt, &fl)) | ||
674 | return -1; | ||
675 | } | ||
676 | if (rt->rt_src == 0) { | ||
677 | ip_rt_put(rt); | ||
678 | return -1; | 662 | return -1; |
679 | } | ||
680 | 663 | ||
681 | skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); | 664 | skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); |
682 | if (skb == NULL) { | 665 | if (skb == NULL) { |
@@ -698,7 +681,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
698 | iph->frag_off = htons(IP_DF); | 681 | iph->frag_off = htons(IP_DF); |
699 | iph->ttl = 1; | 682 | iph->ttl = 1; |
700 | iph->daddr = dst; | 683 | iph->daddr = dst; |
701 | iph->saddr = rt->rt_src; | 684 | iph->saddr = fl4.saddr; |
702 | iph->protocol = IPPROTO_IGMP; | 685 | iph->protocol = IPPROTO_IGMP; |
703 | ip_select_ident(iph, &rt->dst, NULL); | 686 | ip_select_ident(iph, &rt->dst, NULL); |
704 | ((u8*)&iph[1])[0] = IPOPT_RA; | 687 | ((u8*)&iph[1])[0] = IPOPT_RA; |
@@ -1439,8 +1422,6 @@ void ip_mc_destroy_dev(struct in_device *in_dev) | |||
1439 | /* RTNL is locked */ | 1422 | /* RTNL is locked */ |
1440 | static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) | 1423 | static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) |
1441 | { | 1424 | { |
1442 | struct flowi fl = { .fl4_dst = imr->imr_multiaddr.s_addr }; | ||
1443 | struct rtable *rt; | ||
1444 | struct net_device *dev = NULL; | 1425 | struct net_device *dev = NULL; |
1445 | struct in_device *idev = NULL; | 1426 | struct in_device *idev = NULL; |
1446 | 1427 | ||
@@ -1454,9 +1435,14 @@ static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) | |||
1454 | return NULL; | 1435 | return NULL; |
1455 | } | 1436 | } |
1456 | 1437 | ||
1457 | if (!dev && !ip_route_output_key(net, &rt, &fl)) { | 1438 | if (!dev) { |
1458 | dev = rt->dst.dev; | 1439 | struct rtable *rt = ip_route_output(net, |
1459 | ip_rt_put(rt); | 1440 | imr->imr_multiaddr.s_addr, |
1441 | 0, 0, 0); | ||
1442 | if (!IS_ERR(rt)) { | ||
1443 | dev = rt->dst.dev; | ||
1444 | ip_rt_put(rt); | ||
1445 | } | ||
1460 | } | 1446 | } |
1461 | if (dev) { | 1447 | if (dev) { |
1462 | imr->imr_ifindex = dev->ifindex; | 1448 | imr->imr_ifindex = dev->ifindex; |
@@ -1836,12 +1822,6 @@ done: | |||
1836 | } | 1822 | } |
1837 | EXPORT_SYMBOL(ip_mc_join_group); | 1823 | EXPORT_SYMBOL(ip_mc_join_group); |
1838 | 1824 | ||
1839 | static void ip_sf_socklist_reclaim(struct rcu_head *rp) | ||
1840 | { | ||
1841 | kfree(container_of(rp, struct ip_sf_socklist, rcu)); | ||
1842 | /* sk_omem_alloc should have been decreased by the caller*/ | ||
1843 | } | ||
1844 | |||
1845 | static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, | 1825 | static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, |
1846 | struct in_device *in_dev) | 1826 | struct in_device *in_dev) |
1847 | { | 1827 | { |
@@ -1858,18 +1838,10 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, | |||
1858 | rcu_assign_pointer(iml->sflist, NULL); | 1838 | rcu_assign_pointer(iml->sflist, NULL); |
1859 | /* decrease mem now to avoid the memleak warning */ | 1839 | /* decrease mem now to avoid the memleak warning */ |
1860 | atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); | 1840 | atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); |
1861 | call_rcu(&psf->rcu, ip_sf_socklist_reclaim); | 1841 | kfree_rcu(psf, rcu); |
1862 | return err; | 1842 | return err; |
1863 | } | 1843 | } |
1864 | 1844 | ||
1865 | |||
1866 | static void ip_mc_socklist_reclaim(struct rcu_head *rp) | ||
1867 | { | ||
1868 | kfree(container_of(rp, struct ip_mc_socklist, rcu)); | ||
1869 | /* sk_omem_alloc should have been decreased by the caller*/ | ||
1870 | } | ||
1871 | |||
1872 | |||
1873 | /* | 1845 | /* |
1874 | * Ask a socket to leave a group. | 1846 | * Ask a socket to leave a group. |
1875 | */ | 1847 | */ |
@@ -1909,7 +1881,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1909 | rtnl_unlock(); | 1881 | rtnl_unlock(); |
1910 | /* decrease mem now to avoid the memleak warning */ | 1882 | /* decrease mem now to avoid the memleak warning */ |
1911 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | 1883 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); |
1912 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | 1884 | kfree_rcu(iml, rcu); |
1913 | return 0; | 1885 | return 0; |
1914 | } | 1886 | } |
1915 | if (!in_dev) | 1887 | if (!in_dev) |
@@ -2026,7 +1998,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
2026 | newpsl->sl_addr[i] = psl->sl_addr[i]; | 1998 | newpsl->sl_addr[i] = psl->sl_addr[i]; |
2027 | /* decrease mem now to avoid the memleak warning */ | 1999 | /* decrease mem now to avoid the memleak warning */ |
2028 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); | 2000 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
2029 | call_rcu(&psl->rcu, ip_sf_socklist_reclaim); | 2001 | kfree_rcu(psl, rcu); |
2030 | } | 2002 | } |
2031 | rcu_assign_pointer(pmc->sflist, newpsl); | 2003 | rcu_assign_pointer(pmc->sflist, newpsl); |
2032 | psl = newpsl; | 2004 | psl = newpsl; |
@@ -2127,7 +2099,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
2127 | psl->sl_count, psl->sl_addr, 0); | 2099 | psl->sl_count, psl->sl_addr, 0); |
2128 | /* decrease mem now to avoid the memleak warning */ | 2100 | /* decrease mem now to avoid the memleak warning */ |
2129 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); | 2101 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
2130 | call_rcu(&psl->rcu, ip_sf_socklist_reclaim); | 2102 | kfree_rcu(psl, rcu); |
2131 | } else | 2103 | } else |
2132 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, | 2104 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
2133 | 0, NULL, 0); | 2105 | 0, NULL, 0); |
@@ -2324,18 +2296,18 @@ void ip_mc_drop_socket(struct sock *sk) | |||
2324 | ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); | 2296 | ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); |
2325 | /* decrease mem now to avoid the memleak warning */ | 2297 | /* decrease mem now to avoid the memleak warning */ |
2326 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | 2298 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); |
2327 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | 2299 | kfree_rcu(iml, rcu); |
2328 | } | 2300 | } |
2329 | rtnl_unlock(); | 2301 | rtnl_unlock(); |
2330 | } | 2302 | } |
2331 | 2303 | ||
2332 | int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto) | 2304 | /* called with rcu_read_lock() */ |
2305 | int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto) | ||
2333 | { | 2306 | { |
2334 | struct ip_mc_list *im; | 2307 | struct ip_mc_list *im; |
2335 | struct ip_sf_list *psf; | 2308 | struct ip_sf_list *psf; |
2336 | int rv = 0; | 2309 | int rv = 0; |
2337 | 2310 | ||
2338 | rcu_read_lock(); | ||
2339 | for_each_pmc_rcu(in_dev, im) { | 2311 | for_each_pmc_rcu(in_dev, im) { |
2340 | if (im->multiaddr == mc_addr) | 2312 | if (im->multiaddr == mc_addr) |
2341 | break; | 2313 | break; |
@@ -2357,7 +2329,6 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p | |||
2357 | } else | 2329 | } else |
2358 | rv = 1; /* unspecified source; tentatively allow */ | 2330 | rv = 1; /* unspecified source; tentatively allow */ |
2359 | } | 2331 | } |
2360 | rcu_read_unlock(); | ||
2361 | return rv; | 2332 | return rv; |
2362 | } | 2333 | } |
2363 | 2334 | ||