diff options
Diffstat (limited to 'net/ipv4/igmp.c')
-rw-r--r-- | net/ipv4/igmp.c | 357 |
1 files changed, 161 insertions, 196 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 2a4bb76f2132..f1d27f6c9351 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -153,17 +153,27 @@ static void ip_ma_put(struct ip_mc_list *im) | |||
153 | { | 153 | { |
154 | if (atomic_dec_and_test(&im->refcnt)) { | 154 | if (atomic_dec_and_test(&im->refcnt)) { |
155 | in_dev_put(im->interface); | 155 | in_dev_put(im->interface); |
156 | kfree(im); | 156 | kfree_rcu(im, rcu); |
157 | } | 157 | } |
158 | } | 158 | } |
159 | 159 | ||
160 | #define for_each_pmc_rcu(in_dev, pmc) \ | ||
161 | for (pmc = rcu_dereference(in_dev->mc_list); \ | ||
162 | pmc != NULL; \ | ||
163 | pmc = rcu_dereference(pmc->next_rcu)) | ||
164 | |||
165 | #define for_each_pmc_rtnl(in_dev, pmc) \ | ||
166 | for (pmc = rtnl_dereference(in_dev->mc_list); \ | ||
167 | pmc != NULL; \ | ||
168 | pmc = rtnl_dereference(pmc->next_rcu)) | ||
169 | |||
160 | #ifdef CONFIG_IP_MULTICAST | 170 | #ifdef CONFIG_IP_MULTICAST |
161 | 171 | ||
162 | /* | 172 | /* |
163 | * Timer management | 173 | * Timer management |
164 | */ | 174 | */ |
165 | 175 | ||
166 | static __inline__ void igmp_stop_timer(struct ip_mc_list *im) | 176 | static void igmp_stop_timer(struct ip_mc_list *im) |
167 | { | 177 | { |
168 | spin_lock_bh(&im->lock); | 178 | spin_lock_bh(&im->lock); |
169 | if (del_timer(&im->timer)) | 179 | if (del_timer(&im->timer)) |
@@ -284,6 +294,8 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) | |||
284 | return scount; | 294 | return scount; |
285 | } | 295 | } |
286 | 296 | ||
297 | #define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb)) | ||
298 | |||
287 | static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | 299 | static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) |
288 | { | 300 | { |
289 | struct sk_buff *skb; | 301 | struct sk_buff *skb; |
@@ -291,24 +303,24 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | |||
291 | struct iphdr *pip; | 303 | struct iphdr *pip; |
292 | struct igmpv3_report *pig; | 304 | struct igmpv3_report *pig; |
293 | struct net *net = dev_net(dev); | 305 | struct net *net = dev_net(dev); |
306 | struct flowi4 fl4; | ||
294 | 307 | ||
295 | skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); | 308 | while (1) { |
296 | if (skb == NULL) | 309 | skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), |
297 | return NULL; | 310 | GFP_ATOMIC | __GFP_NOWARN); |
298 | 311 | if (skb) | |
299 | { | 312 | break; |
300 | struct flowi fl = { .oif = dev->ifindex, | 313 | size >>= 1; |
301 | .nl_u = { .ip4_u = { | 314 | if (size < 256) |
302 | .daddr = IGMPV3_ALL_MCR } }, | ||
303 | .proto = IPPROTO_IGMP }; | ||
304 | if (ip_route_output_key(net, &rt, &fl)) { | ||
305 | kfree_skb(skb); | ||
306 | return NULL; | 315 | return NULL; |
307 | } | ||
308 | } | 316 | } |
309 | if (rt->rt_src == 0) { | 317 | igmp_skb_size(skb) = size; |
318 | |||
319 | rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, | ||
320 | 0, 0, | ||
321 | IPPROTO_IGMP, 0, dev->ifindex); | ||
322 | if (IS_ERR(rt)) { | ||
310 | kfree_skb(skb); | 323 | kfree_skb(skb); |
311 | ip_rt_put(rt); | ||
312 | return NULL; | 324 | return NULL; |
313 | } | 325 | } |
314 | 326 | ||
@@ -326,8 +338,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | |||
326 | pip->tos = 0xc0; | 338 | pip->tos = 0xc0; |
327 | pip->frag_off = htons(IP_DF); | 339 | pip->frag_off = htons(IP_DF); |
328 | pip->ttl = 1; | 340 | pip->ttl = 1; |
329 | pip->daddr = rt->rt_dst; | 341 | pip->daddr = fl4.daddr; |
330 | pip->saddr = rt->rt_src; | 342 | pip->saddr = fl4.saddr; |
331 | pip->protocol = IPPROTO_IGMP; | 343 | pip->protocol = IPPROTO_IGMP; |
332 | pip->tot_len = 0; /* filled in later */ | 344 | pip->tot_len = 0; /* filled in later */ |
333 | ip_select_ident(pip, &rt->dst, NULL); | 345 | ip_select_ident(pip, &rt->dst, NULL); |
@@ -384,7 +396,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, | |||
384 | return skb; | 396 | return skb; |
385 | } | 397 | } |
386 | 398 | ||
387 | #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \ | 399 | #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \ |
388 | skb_tailroom(skb)) : 0) | 400 | skb_tailroom(skb)) : 0) |
389 | 401 | ||
390 | static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, | 402 | static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, |
@@ -502,8 +514,8 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) | |||
502 | int type; | 514 | int type; |
503 | 515 | ||
504 | if (!pmc) { | 516 | if (!pmc) { |
505 | read_lock(&in_dev->mc_list_lock); | 517 | rcu_read_lock(); |
506 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 518 | for_each_pmc_rcu(in_dev, pmc) { |
507 | if (pmc->multiaddr == IGMP_ALL_HOSTS) | 519 | if (pmc->multiaddr == IGMP_ALL_HOSTS) |
508 | continue; | 520 | continue; |
509 | spin_lock_bh(&pmc->lock); | 521 | spin_lock_bh(&pmc->lock); |
@@ -514,7 +526,7 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) | |||
514 | skb = add_grec(skb, pmc, type, 0, 0); | 526 | skb = add_grec(skb, pmc, type, 0, 0); |
515 | spin_unlock_bh(&pmc->lock); | 527 | spin_unlock_bh(&pmc->lock); |
516 | } | 528 | } |
517 | read_unlock(&in_dev->mc_list_lock); | 529 | rcu_read_unlock(); |
518 | } else { | 530 | } else { |
519 | spin_lock_bh(&pmc->lock); | 531 | spin_lock_bh(&pmc->lock); |
520 | if (pmc->sfcount[MCAST_EXCLUDE]) | 532 | if (pmc->sfcount[MCAST_EXCLUDE]) |
@@ -556,7 +568,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) | |||
556 | struct sk_buff *skb = NULL; | 568 | struct sk_buff *skb = NULL; |
557 | int type, dtype; | 569 | int type, dtype; |
558 | 570 | ||
559 | read_lock(&in_dev->mc_list_lock); | 571 | rcu_read_lock(); |
560 | spin_lock_bh(&in_dev->mc_tomb_lock); | 572 | spin_lock_bh(&in_dev->mc_tomb_lock); |
561 | 573 | ||
562 | /* deleted MCA's */ | 574 | /* deleted MCA's */ |
@@ -593,7 +605,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) | |||
593 | spin_unlock_bh(&in_dev->mc_tomb_lock); | 605 | spin_unlock_bh(&in_dev->mc_tomb_lock); |
594 | 606 | ||
595 | /* change recs */ | 607 | /* change recs */ |
596 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 608 | for_each_pmc_rcu(in_dev, pmc) { |
597 | spin_lock_bh(&pmc->lock); | 609 | spin_lock_bh(&pmc->lock); |
598 | if (pmc->sfcount[MCAST_EXCLUDE]) { | 610 | if (pmc->sfcount[MCAST_EXCLUDE]) { |
599 | type = IGMPV3_BLOCK_OLD_SOURCES; | 611 | type = IGMPV3_BLOCK_OLD_SOURCES; |
@@ -616,7 +628,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) | |||
616 | } | 628 | } |
617 | spin_unlock_bh(&pmc->lock); | 629 | spin_unlock_bh(&pmc->lock); |
618 | } | 630 | } |
619 | read_unlock(&in_dev->mc_list_lock); | 631 | rcu_read_unlock(); |
620 | 632 | ||
621 | if (!skb) | 633 | if (!skb) |
622 | return; | 634 | return; |
@@ -633,6 +645,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
633 | struct net_device *dev = in_dev->dev; | 645 | struct net_device *dev = in_dev->dev; |
634 | struct net *net = dev_net(dev); | 646 | struct net *net = dev_net(dev); |
635 | __be32 group = pmc ? pmc->multiaddr : 0; | 647 | __be32 group = pmc ? pmc->multiaddr : 0; |
648 | struct flowi4 fl4; | ||
636 | __be32 dst; | 649 | __be32 dst; |
637 | 650 | ||
638 | if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) | 651 | if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) |
@@ -642,17 +655,11 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
642 | else | 655 | else |
643 | dst = group; | 656 | dst = group; |
644 | 657 | ||
645 | { | 658 | rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, |
646 | struct flowi fl = { .oif = dev->ifindex, | 659 | 0, 0, |
647 | .nl_u = { .ip4_u = { .daddr = dst } }, | 660 | IPPROTO_IGMP, 0, dev->ifindex); |
648 | .proto = IPPROTO_IGMP }; | 661 | if (IS_ERR(rt)) |
649 | if (ip_route_output_key(net, &rt, &fl)) | ||
650 | return -1; | ||
651 | } | ||
652 | if (rt->rt_src == 0) { | ||
653 | ip_rt_put(rt); | ||
654 | return -1; | 662 | return -1; |
655 | } | ||
656 | 663 | ||
657 | skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); | 664 | skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); |
658 | if (skb == NULL) { | 665 | if (skb == NULL) { |
@@ -674,7 +681,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
674 | iph->frag_off = htons(IP_DF); | 681 | iph->frag_off = htons(IP_DF); |
675 | iph->ttl = 1; | 682 | iph->ttl = 1; |
676 | iph->daddr = dst; | 683 | iph->daddr = dst; |
677 | iph->saddr = rt->rt_src; | 684 | iph->saddr = fl4.saddr; |
678 | iph->protocol = IPPROTO_IGMP; | 685 | iph->protocol = IPPROTO_IGMP; |
679 | ip_select_ident(iph, &rt->dst, NULL); | 686 | ip_select_ident(iph, &rt->dst, NULL); |
680 | ((u8*)&iph[1])[0] = IPOPT_RA; | 687 | ((u8*)&iph[1])[0] = IPOPT_RA; |
@@ -813,14 +820,14 @@ static void igmp_heard_report(struct in_device *in_dev, __be32 group) | |||
813 | if (group == IGMP_ALL_HOSTS) | 820 | if (group == IGMP_ALL_HOSTS) |
814 | return; | 821 | return; |
815 | 822 | ||
816 | read_lock(&in_dev->mc_list_lock); | 823 | rcu_read_lock(); |
817 | for (im=in_dev->mc_list; im!=NULL; im=im->next) { | 824 | for_each_pmc_rcu(in_dev, im) { |
818 | if (im->multiaddr == group) { | 825 | if (im->multiaddr == group) { |
819 | igmp_stop_timer(im); | 826 | igmp_stop_timer(im); |
820 | break; | 827 | break; |
821 | } | 828 | } |
822 | } | 829 | } |
823 | read_unlock(&in_dev->mc_list_lock); | 830 | rcu_read_unlock(); |
824 | } | 831 | } |
825 | 832 | ||
826 | static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | 833 | static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, |
@@ -906,8 +913,8 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
906 | * - Use the igmp->igmp_code field as the maximum | 913 | * - Use the igmp->igmp_code field as the maximum |
907 | * delay possible | 914 | * delay possible |
908 | */ | 915 | */ |
909 | read_lock(&in_dev->mc_list_lock); | 916 | rcu_read_lock(); |
910 | for (im=in_dev->mc_list; im!=NULL; im=im->next) { | 917 | for_each_pmc_rcu(in_dev, im) { |
911 | int changed; | 918 | int changed; |
912 | 919 | ||
913 | if (group && group != im->multiaddr) | 920 | if (group && group != im->multiaddr) |
@@ -925,7 +932,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
925 | if (changed) | 932 | if (changed) |
926 | igmp_mod_timer(im, max_delay); | 933 | igmp_mod_timer(im, max_delay); |
927 | } | 934 | } |
928 | read_unlock(&in_dev->mc_list_lock); | 935 | rcu_read_unlock(); |
929 | } | 936 | } |
930 | 937 | ||
931 | /* called in rcu_read_lock() section */ | 938 | /* called in rcu_read_lock() section */ |
@@ -961,7 +968,7 @@ int igmp_rcv(struct sk_buff *skb) | |||
961 | case IGMP_HOST_MEMBERSHIP_REPORT: | 968 | case IGMP_HOST_MEMBERSHIP_REPORT: |
962 | case IGMPV2_HOST_MEMBERSHIP_REPORT: | 969 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
963 | /* Is it our report looped back? */ | 970 | /* Is it our report looped back? */ |
964 | if (skb_rtable(skb)->fl.iif == 0) | 971 | if (rt_is_output_route(skb_rtable(skb))) |
965 | break; | 972 | break; |
966 | /* don't rely on MC router hearing unicast reports */ | 973 | /* don't rely on MC router hearing unicast reports */ |
967 | if (skb->pkt_type == PACKET_MULTICAST || | 974 | if (skb->pkt_type == PACKET_MULTICAST || |
@@ -1110,8 +1117,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) | |||
1110 | kfree(pmc); | 1117 | kfree(pmc); |
1111 | } | 1118 | } |
1112 | /* clear dead sources, too */ | 1119 | /* clear dead sources, too */ |
1113 | read_lock(&in_dev->mc_list_lock); | 1120 | rcu_read_lock(); |
1114 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 1121 | for_each_pmc_rcu(in_dev, pmc) { |
1115 | struct ip_sf_list *psf, *psf_next; | 1122 | struct ip_sf_list *psf, *psf_next; |
1116 | 1123 | ||
1117 | spin_lock_bh(&pmc->lock); | 1124 | spin_lock_bh(&pmc->lock); |
@@ -1123,7 +1130,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) | |||
1123 | kfree(psf); | 1130 | kfree(psf); |
1124 | } | 1131 | } |
1125 | } | 1132 | } |
1126 | read_unlock(&in_dev->mc_list_lock); | 1133 | rcu_read_unlock(); |
1127 | } | 1134 | } |
1128 | #endif | 1135 | #endif |
1129 | 1136 | ||
@@ -1148,20 +1155,18 @@ static void igmp_group_dropped(struct ip_mc_list *im) | |||
1148 | 1155 | ||
1149 | if (!in_dev->dead) { | 1156 | if (!in_dev->dead) { |
1150 | if (IGMP_V1_SEEN(in_dev)) | 1157 | if (IGMP_V1_SEEN(in_dev)) |
1151 | goto done; | 1158 | return; |
1152 | if (IGMP_V2_SEEN(in_dev)) { | 1159 | if (IGMP_V2_SEEN(in_dev)) { |
1153 | if (reporter) | 1160 | if (reporter) |
1154 | igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); | 1161 | igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); |
1155 | goto done; | 1162 | return; |
1156 | } | 1163 | } |
1157 | /* IGMPv3 */ | 1164 | /* IGMPv3 */ |
1158 | igmpv3_add_delrec(in_dev, im); | 1165 | igmpv3_add_delrec(in_dev, im); |
1159 | 1166 | ||
1160 | igmp_ifc_event(in_dev); | 1167 | igmp_ifc_event(in_dev); |
1161 | } | 1168 | } |
1162 | done: | ||
1163 | #endif | 1169 | #endif |
1164 | ip_mc_clear_src(im); | ||
1165 | } | 1170 | } |
1166 | 1171 | ||
1167 | static void igmp_group_added(struct ip_mc_list *im) | 1172 | static void igmp_group_added(struct ip_mc_list *im) |
@@ -1209,7 +1214,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1209 | 1214 | ||
1210 | ASSERT_RTNL(); | 1215 | ASSERT_RTNL(); |
1211 | 1216 | ||
1212 | for (im=in_dev->mc_list; im; im=im->next) { | 1217 | for_each_pmc_rtnl(in_dev, im) { |
1213 | if (im->multiaddr == addr) { | 1218 | if (im->multiaddr == addr) { |
1214 | im->users++; | 1219 | im->users++; |
1215 | ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); | 1220 | ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); |
@@ -1217,7 +1222,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1217 | } | 1222 | } |
1218 | } | 1223 | } |
1219 | 1224 | ||
1220 | im = kmalloc(sizeof(*im), GFP_KERNEL); | 1225 | im = kzalloc(sizeof(*im), GFP_KERNEL); |
1221 | if (!im) | 1226 | if (!im) |
1222 | goto out; | 1227 | goto out; |
1223 | 1228 | ||
@@ -1227,26 +1232,18 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1227 | im->multiaddr = addr; | 1232 | im->multiaddr = addr; |
1228 | /* initial mode is (EX, empty) */ | 1233 | /* initial mode is (EX, empty) */ |
1229 | im->sfmode = MCAST_EXCLUDE; | 1234 | im->sfmode = MCAST_EXCLUDE; |
1230 | im->sfcount[MCAST_INCLUDE] = 0; | ||
1231 | im->sfcount[MCAST_EXCLUDE] = 1; | 1235 | im->sfcount[MCAST_EXCLUDE] = 1; |
1232 | im->sources = NULL; | ||
1233 | im->tomb = NULL; | ||
1234 | im->crcount = 0; | ||
1235 | atomic_set(&im->refcnt, 1); | 1236 | atomic_set(&im->refcnt, 1); |
1236 | spin_lock_init(&im->lock); | 1237 | spin_lock_init(&im->lock); |
1237 | #ifdef CONFIG_IP_MULTICAST | 1238 | #ifdef CONFIG_IP_MULTICAST |
1238 | im->tm_running = 0; | ||
1239 | setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im); | 1239 | setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im); |
1240 | im->unsolicit_count = IGMP_Unsolicited_Report_Count; | 1240 | im->unsolicit_count = IGMP_Unsolicited_Report_Count; |
1241 | im->reporter = 0; | ||
1242 | im->gsquery = 0; | ||
1243 | #endif | 1241 | #endif |
1244 | im->loaded = 0; | 1242 | |
1245 | write_lock_bh(&in_dev->mc_list_lock); | 1243 | im->next_rcu = in_dev->mc_list; |
1246 | im->next = in_dev->mc_list; | ||
1247 | in_dev->mc_list = im; | ||
1248 | in_dev->mc_count++; | 1244 | in_dev->mc_count++; |
1249 | write_unlock_bh(&in_dev->mc_list_lock); | 1245 | rcu_assign_pointer(in_dev->mc_list, im); |
1246 | |||
1250 | #ifdef CONFIG_IP_MULTICAST | 1247 | #ifdef CONFIG_IP_MULTICAST |
1251 | igmpv3_del_delrec(in_dev, im->multiaddr); | 1248 | igmpv3_del_delrec(in_dev, im->multiaddr); |
1252 | #endif | 1249 | #endif |
@@ -1260,26 +1257,32 @@ EXPORT_SYMBOL(ip_mc_inc_group); | |||
1260 | 1257 | ||
1261 | /* | 1258 | /* |
1262 | * Resend IGMP JOIN report; used for bonding. | 1259 | * Resend IGMP JOIN report; used for bonding. |
1260 | * Called with rcu_read_lock() | ||
1263 | */ | 1261 | */ |
1264 | void ip_mc_rejoin_group(struct ip_mc_list *im) | 1262 | void ip_mc_rejoin_groups(struct in_device *in_dev) |
1265 | { | 1263 | { |
1266 | #ifdef CONFIG_IP_MULTICAST | 1264 | #ifdef CONFIG_IP_MULTICAST |
1267 | struct in_device *in_dev = im->interface; | 1265 | struct ip_mc_list *im; |
1266 | int type; | ||
1268 | 1267 | ||
1269 | if (im->multiaddr == IGMP_ALL_HOSTS) | 1268 | for_each_pmc_rcu(in_dev, im) { |
1270 | return; | 1269 | if (im->multiaddr == IGMP_ALL_HOSTS) |
1270 | continue; | ||
1271 | 1271 | ||
1272 | if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { | 1272 | /* a failover is happening and switches |
1273 | igmp_mod_timer(im, IGMP_Initial_Report_Delay); | 1273 | * must be notified immediately |
1274 | return; | 1274 | */ |
1275 | if (IGMP_V1_SEEN(in_dev)) | ||
1276 | type = IGMP_HOST_MEMBERSHIP_REPORT; | ||
1277 | else if (IGMP_V2_SEEN(in_dev)) | ||
1278 | type = IGMPV2_HOST_MEMBERSHIP_REPORT; | ||
1279 | else | ||
1280 | type = IGMPV3_HOST_MEMBERSHIP_REPORT; | ||
1281 | igmp_send_report(in_dev, im, type); | ||
1275 | } | 1282 | } |
1276 | /* else, v3 */ | ||
1277 | im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : | ||
1278 | IGMP_Unsolicited_Report_Count; | ||
1279 | igmp_ifc_event(in_dev); | ||
1280 | #endif | 1283 | #endif |
1281 | } | 1284 | } |
1282 | EXPORT_SYMBOL(ip_mc_rejoin_group); | 1285 | EXPORT_SYMBOL(ip_mc_rejoin_groups); |
1283 | 1286 | ||
1284 | /* | 1287 | /* |
1285 | * A socket has left a multicast group on device dev | 1288 | * A socket has left a multicast group on device dev |
@@ -1287,18 +1290,20 @@ EXPORT_SYMBOL(ip_mc_rejoin_group); | |||
1287 | 1290 | ||
1288 | void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) | 1291 | void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) |
1289 | { | 1292 | { |
1290 | struct ip_mc_list *i, **ip; | 1293 | struct ip_mc_list *i; |
1294 | struct ip_mc_list __rcu **ip; | ||
1291 | 1295 | ||
1292 | ASSERT_RTNL(); | 1296 | ASSERT_RTNL(); |
1293 | 1297 | ||
1294 | for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { | 1298 | for (ip = &in_dev->mc_list; |
1299 | (i = rtnl_dereference(*ip)) != NULL; | ||
1300 | ip = &i->next_rcu) { | ||
1295 | if (i->multiaddr == addr) { | 1301 | if (i->multiaddr == addr) { |
1296 | if (--i->users == 0) { | 1302 | if (--i->users == 0) { |
1297 | write_lock_bh(&in_dev->mc_list_lock); | 1303 | *ip = i->next_rcu; |
1298 | *ip = i->next; | ||
1299 | in_dev->mc_count--; | 1304 | in_dev->mc_count--; |
1300 | write_unlock_bh(&in_dev->mc_list_lock); | ||
1301 | igmp_group_dropped(i); | 1305 | igmp_group_dropped(i); |
1306 | ip_mc_clear_src(i); | ||
1302 | 1307 | ||
1303 | if (!in_dev->dead) | 1308 | if (!in_dev->dead) |
1304 | ip_rt_multicast_event(in_dev); | 1309 | ip_rt_multicast_event(in_dev); |
@@ -1316,34 +1321,34 @@ EXPORT_SYMBOL(ip_mc_dec_group); | |||
1316 | 1321 | ||
1317 | void ip_mc_unmap(struct in_device *in_dev) | 1322 | void ip_mc_unmap(struct in_device *in_dev) |
1318 | { | 1323 | { |
1319 | struct ip_mc_list *i; | 1324 | struct ip_mc_list *pmc; |
1320 | 1325 | ||
1321 | ASSERT_RTNL(); | 1326 | ASSERT_RTNL(); |
1322 | 1327 | ||
1323 | for (i = in_dev->mc_list; i; i = i->next) | 1328 | for_each_pmc_rtnl(in_dev, pmc) |
1324 | igmp_group_dropped(i); | 1329 | igmp_group_dropped(pmc); |
1325 | } | 1330 | } |
1326 | 1331 | ||
1327 | void ip_mc_remap(struct in_device *in_dev) | 1332 | void ip_mc_remap(struct in_device *in_dev) |
1328 | { | 1333 | { |
1329 | struct ip_mc_list *i; | 1334 | struct ip_mc_list *pmc; |
1330 | 1335 | ||
1331 | ASSERT_RTNL(); | 1336 | ASSERT_RTNL(); |
1332 | 1337 | ||
1333 | for (i = in_dev->mc_list; i; i = i->next) | 1338 | for_each_pmc_rtnl(in_dev, pmc) |
1334 | igmp_group_added(i); | 1339 | igmp_group_added(pmc); |
1335 | } | 1340 | } |
1336 | 1341 | ||
1337 | /* Device going down */ | 1342 | /* Device going down */ |
1338 | 1343 | ||
1339 | void ip_mc_down(struct in_device *in_dev) | 1344 | void ip_mc_down(struct in_device *in_dev) |
1340 | { | 1345 | { |
1341 | struct ip_mc_list *i; | 1346 | struct ip_mc_list *pmc; |
1342 | 1347 | ||
1343 | ASSERT_RTNL(); | 1348 | ASSERT_RTNL(); |
1344 | 1349 | ||
1345 | for (i=in_dev->mc_list; i; i=i->next) | 1350 | for_each_pmc_rtnl(in_dev, pmc) |
1346 | igmp_group_dropped(i); | 1351 | igmp_group_dropped(pmc); |
1347 | 1352 | ||
1348 | #ifdef CONFIG_IP_MULTICAST | 1353 | #ifdef CONFIG_IP_MULTICAST |
1349 | in_dev->mr_ifc_count = 0; | 1354 | in_dev->mr_ifc_count = 0; |
@@ -1374,7 +1379,6 @@ void ip_mc_init_dev(struct in_device *in_dev) | |||
1374 | in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; | 1379 | in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; |
1375 | #endif | 1380 | #endif |
1376 | 1381 | ||
1377 | rwlock_init(&in_dev->mc_list_lock); | ||
1378 | spin_lock_init(&in_dev->mc_tomb_lock); | 1382 | spin_lock_init(&in_dev->mc_tomb_lock); |
1379 | } | 1383 | } |
1380 | 1384 | ||
@@ -1382,14 +1386,14 @@ void ip_mc_init_dev(struct in_device *in_dev) | |||
1382 | 1386 | ||
1383 | void ip_mc_up(struct in_device *in_dev) | 1387 | void ip_mc_up(struct in_device *in_dev) |
1384 | { | 1388 | { |
1385 | struct ip_mc_list *i; | 1389 | struct ip_mc_list *pmc; |
1386 | 1390 | ||
1387 | ASSERT_RTNL(); | 1391 | ASSERT_RTNL(); |
1388 | 1392 | ||
1389 | ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); | 1393 | ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); |
1390 | 1394 | ||
1391 | for (i=in_dev->mc_list; i; i=i->next) | 1395 | for_each_pmc_rtnl(in_dev, pmc) |
1392 | igmp_group_added(i); | 1396 | igmp_group_added(pmc); |
1393 | } | 1397 | } |
1394 | 1398 | ||
1395 | /* | 1399 | /* |
@@ -1405,43 +1409,40 @@ void ip_mc_destroy_dev(struct in_device *in_dev) | |||
1405 | /* Deactivate timers */ | 1409 | /* Deactivate timers */ |
1406 | ip_mc_down(in_dev); | 1410 | ip_mc_down(in_dev); |
1407 | 1411 | ||
1408 | write_lock_bh(&in_dev->mc_list_lock); | 1412 | while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { |
1409 | while ((i = in_dev->mc_list) != NULL) { | 1413 | in_dev->mc_list = i->next_rcu; |
1410 | in_dev->mc_list = i->next; | ||
1411 | in_dev->mc_count--; | 1414 | in_dev->mc_count--; |
1412 | write_unlock_bh(&in_dev->mc_list_lock); | ||
1413 | igmp_group_dropped(i); | ||
1414 | ip_ma_put(i); | ||
1415 | 1415 | ||
1416 | write_lock_bh(&in_dev->mc_list_lock); | 1416 | /* We've dropped the groups in ip_mc_down already */ |
1417 | ip_mc_clear_src(i); | ||
1418 | ip_ma_put(i); | ||
1417 | } | 1419 | } |
1418 | write_unlock_bh(&in_dev->mc_list_lock); | ||
1419 | } | 1420 | } |
1420 | 1421 | ||
1422 | /* RTNL is locked */ | ||
1421 | 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) |
1422 | { | 1424 | { |
1423 | struct flowi fl = { .nl_u = { .ip4_u = | ||
1424 | { .daddr = imr->imr_multiaddr.s_addr } } }; | ||
1425 | struct rtable *rt; | ||
1426 | struct net_device *dev = NULL; | 1425 | struct net_device *dev = NULL; |
1427 | struct in_device *idev = NULL; | 1426 | struct in_device *idev = NULL; |
1428 | 1427 | ||
1429 | if (imr->imr_ifindex) { | 1428 | if (imr->imr_ifindex) { |
1430 | idev = inetdev_by_index(net, imr->imr_ifindex); | 1429 | idev = inetdev_by_index(net, imr->imr_ifindex); |
1431 | if (idev) | ||
1432 | __in_dev_put(idev); | ||
1433 | return idev; | 1430 | return idev; |
1434 | } | 1431 | } |
1435 | if (imr->imr_address.s_addr) { | 1432 | if (imr->imr_address.s_addr) { |
1436 | dev = ip_dev_find(net, imr->imr_address.s_addr); | 1433 | dev = __ip_dev_find(net, imr->imr_address.s_addr, false); |
1437 | if (!dev) | 1434 | if (!dev) |
1438 | return NULL; | 1435 | return NULL; |
1439 | dev_put(dev); | ||
1440 | } | 1436 | } |
1441 | 1437 | ||
1442 | if (!dev && !ip_route_output_key(net, &rt, &fl)) { | 1438 | if (!dev) { |
1443 | dev = rt->dst.dev; | 1439 | struct rtable *rt = ip_route_output(net, |
1444 | 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 | } | ||
1445 | } | 1446 | } |
1446 | if (dev) { | 1447 | if (dev) { |
1447 | imr->imr_ifindex = dev->ifindex; | 1448 | imr->imr_ifindex = dev->ifindex; |
@@ -1515,18 +1516,18 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, | |||
1515 | 1516 | ||
1516 | if (!in_dev) | 1517 | if (!in_dev) |
1517 | return -ENODEV; | 1518 | return -ENODEV; |
1518 | read_lock(&in_dev->mc_list_lock); | 1519 | rcu_read_lock(); |
1519 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 1520 | for_each_pmc_rcu(in_dev, pmc) { |
1520 | if (*pmca == pmc->multiaddr) | 1521 | if (*pmca == pmc->multiaddr) |
1521 | break; | 1522 | break; |
1522 | } | 1523 | } |
1523 | if (!pmc) { | 1524 | if (!pmc) { |
1524 | /* MCA not found?? bug */ | 1525 | /* MCA not found?? bug */ |
1525 | read_unlock(&in_dev->mc_list_lock); | 1526 | rcu_read_unlock(); |
1526 | return -ESRCH; | 1527 | return -ESRCH; |
1527 | } | 1528 | } |
1528 | spin_lock_bh(&pmc->lock); | 1529 | spin_lock_bh(&pmc->lock); |
1529 | read_unlock(&in_dev->mc_list_lock); | 1530 | rcu_read_unlock(); |
1530 | #ifdef CONFIG_IP_MULTICAST | 1531 | #ifdef CONFIG_IP_MULTICAST |
1531 | sf_markstate(pmc); | 1532 | sf_markstate(pmc); |
1532 | #endif | 1533 | #endif |
@@ -1687,18 +1688,18 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, | |||
1687 | 1688 | ||
1688 | if (!in_dev) | 1689 | if (!in_dev) |
1689 | return -ENODEV; | 1690 | return -ENODEV; |
1690 | read_lock(&in_dev->mc_list_lock); | 1691 | rcu_read_lock(); |
1691 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 1692 | for_each_pmc_rcu(in_dev, pmc) { |
1692 | if (*pmca == pmc->multiaddr) | 1693 | if (*pmca == pmc->multiaddr) |
1693 | break; | 1694 | break; |
1694 | } | 1695 | } |
1695 | if (!pmc) { | 1696 | if (!pmc) { |
1696 | /* MCA not found?? bug */ | 1697 | /* MCA not found?? bug */ |
1697 | read_unlock(&in_dev->mc_list_lock); | 1698 | rcu_read_unlock(); |
1698 | return -ESRCH; | 1699 | return -ESRCH; |
1699 | } | 1700 | } |
1700 | spin_lock_bh(&pmc->lock); | 1701 | spin_lock_bh(&pmc->lock); |
1701 | read_unlock(&in_dev->mc_list_lock); | 1702 | rcu_read_unlock(); |
1702 | 1703 | ||
1703 | #ifdef CONFIG_IP_MULTICAST | 1704 | #ifdef CONFIG_IP_MULTICAST |
1704 | sf_markstate(pmc); | 1705 | sf_markstate(pmc); |
@@ -1795,7 +1796,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1795 | 1796 | ||
1796 | err = -EADDRINUSE; | 1797 | err = -EADDRINUSE; |
1797 | ifindex = imr->imr_ifindex; | 1798 | ifindex = imr->imr_ifindex; |
1798 | for (i = inet->mc_list; i; i = i->next) { | 1799 | for_each_pmc_rtnl(inet, i) { |
1799 | if (i->multi.imr_multiaddr.s_addr == addr && | 1800 | if (i->multi.imr_multiaddr.s_addr == addr && |
1800 | i->multi.imr_ifindex == ifindex) | 1801 | i->multi.imr_ifindex == ifindex) |
1801 | goto done; | 1802 | goto done; |
@@ -1809,7 +1810,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1809 | goto done; | 1810 | goto done; |
1810 | 1811 | ||
1811 | memcpy(&iml->multi, imr, sizeof(*imr)); | 1812 | memcpy(&iml->multi, imr, sizeof(*imr)); |
1812 | iml->next = inet->mc_list; | 1813 | iml->next_rcu = inet->mc_list; |
1813 | iml->sflist = NULL; | 1814 | iml->sflist = NULL; |
1814 | iml->sfmode = MCAST_EXCLUDE; | 1815 | iml->sfmode = MCAST_EXCLUDE; |
1815 | rcu_assign_pointer(inet->mc_list, iml); | 1816 | rcu_assign_pointer(inet->mc_list, iml); |
@@ -1821,19 +1822,10 @@ done: | |||
1821 | } | 1822 | } |
1822 | EXPORT_SYMBOL(ip_mc_join_group); | 1823 | EXPORT_SYMBOL(ip_mc_join_group); |
1823 | 1824 | ||
1824 | static void ip_sf_socklist_reclaim(struct rcu_head *rp) | ||
1825 | { | ||
1826 | struct ip_sf_socklist *psf; | ||
1827 | |||
1828 | psf = container_of(rp, struct ip_sf_socklist, rcu); | ||
1829 | /* sk_omem_alloc should have been decreased by the caller*/ | ||
1830 | kfree(psf); | ||
1831 | } | ||
1832 | |||
1833 | 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, |
1834 | struct in_device *in_dev) | 1826 | struct in_device *in_dev) |
1835 | { | 1827 | { |
1836 | struct ip_sf_socklist *psf = iml->sflist; | 1828 | struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); |
1837 | int err; | 1829 | int err; |
1838 | 1830 | ||
1839 | if (psf == NULL) { | 1831 | if (psf == NULL) { |
@@ -1846,21 +1838,10 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, | |||
1846 | rcu_assign_pointer(iml->sflist, NULL); | 1838 | rcu_assign_pointer(iml->sflist, NULL); |
1847 | /* decrease mem now to avoid the memleak warning */ | 1839 | /* decrease mem now to avoid the memleak warning */ |
1848 | atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); | 1840 | atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); |
1849 | call_rcu(&psf->rcu, ip_sf_socklist_reclaim); | 1841 | kfree_rcu(psf, rcu); |
1850 | return err; | 1842 | return err; |
1851 | } | 1843 | } |
1852 | 1844 | ||
1853 | |||
1854 | static void ip_mc_socklist_reclaim(struct rcu_head *rp) | ||
1855 | { | ||
1856 | struct ip_mc_socklist *iml; | ||
1857 | |||
1858 | iml = container_of(rp, struct ip_mc_socklist, rcu); | ||
1859 | /* sk_omem_alloc should have been decreased by the caller*/ | ||
1860 | kfree(iml); | ||
1861 | } | ||
1862 | |||
1863 | |||
1864 | /* | 1845 | /* |
1865 | * Ask a socket to leave a group. | 1846 | * Ask a socket to leave a group. |
1866 | */ | 1847 | */ |
@@ -1868,7 +1849,8 @@ static void ip_mc_socklist_reclaim(struct rcu_head *rp) | |||
1868 | int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | 1849 | int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) |
1869 | { | 1850 | { |
1870 | struct inet_sock *inet = inet_sk(sk); | 1851 | struct inet_sock *inet = inet_sk(sk); |
1871 | struct ip_mc_socklist *iml, **imlp; | 1852 | struct ip_mc_socklist *iml; |
1853 | struct ip_mc_socklist __rcu **imlp; | ||
1872 | struct in_device *in_dev; | 1854 | struct in_device *in_dev; |
1873 | struct net *net = sock_net(sk); | 1855 | struct net *net = sock_net(sk); |
1874 | __be32 group = imr->imr_multiaddr.s_addr; | 1856 | __be32 group = imr->imr_multiaddr.s_addr; |
@@ -1878,7 +1860,9 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1878 | rtnl_lock(); | 1860 | rtnl_lock(); |
1879 | in_dev = ip_mc_find_dev(net, imr); | 1861 | in_dev = ip_mc_find_dev(net, imr); |
1880 | ifindex = imr->imr_ifindex; | 1862 | ifindex = imr->imr_ifindex; |
1881 | for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { | 1863 | for (imlp = &inet->mc_list; |
1864 | (iml = rtnl_dereference(*imlp)) != NULL; | ||
1865 | imlp = &iml->next_rcu) { | ||
1882 | if (iml->multi.imr_multiaddr.s_addr != group) | 1866 | if (iml->multi.imr_multiaddr.s_addr != group) |
1883 | continue; | 1867 | continue; |
1884 | if (ifindex) { | 1868 | if (ifindex) { |
@@ -1890,14 +1874,14 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1890 | 1874 | ||
1891 | (void) ip_mc_leave_src(sk, iml, in_dev); | 1875 | (void) ip_mc_leave_src(sk, iml, in_dev); |
1892 | 1876 | ||
1893 | rcu_assign_pointer(*imlp, iml->next); | 1877 | *imlp = iml->next_rcu; |
1894 | 1878 | ||
1895 | if (in_dev) | 1879 | if (in_dev) |
1896 | ip_mc_dec_group(in_dev, group); | 1880 | ip_mc_dec_group(in_dev, group); |
1897 | rtnl_unlock(); | 1881 | rtnl_unlock(); |
1898 | /* decrease mem now to avoid the memleak warning */ | 1882 | /* decrease mem now to avoid the memleak warning */ |
1899 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | 1883 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); |
1900 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | 1884 | kfree_rcu(iml, rcu); |
1901 | return 0; | 1885 | return 0; |
1902 | } | 1886 | } |
1903 | if (!in_dev) | 1887 | if (!in_dev) |
@@ -1936,7 +1920,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1936 | } | 1920 | } |
1937 | err = -EADDRNOTAVAIL; | 1921 | err = -EADDRNOTAVAIL; |
1938 | 1922 | ||
1939 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 1923 | for_each_pmc_rtnl(inet, pmc) { |
1940 | if ((pmc->multi.imr_multiaddr.s_addr == | 1924 | if ((pmc->multi.imr_multiaddr.s_addr == |
1941 | imr.imr_multiaddr.s_addr) && | 1925 | imr.imr_multiaddr.s_addr) && |
1942 | (pmc->multi.imr_ifindex == imr.imr_ifindex)) | 1926 | (pmc->multi.imr_ifindex == imr.imr_ifindex)) |
@@ -1960,7 +1944,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1960 | pmc->sfmode = omode; | 1944 | pmc->sfmode = omode; |
1961 | } | 1945 | } |
1962 | 1946 | ||
1963 | psl = pmc->sflist; | 1947 | psl = rtnl_dereference(pmc->sflist); |
1964 | if (!add) { | 1948 | if (!add) { |
1965 | if (!psl) | 1949 | if (!psl) |
1966 | goto done; /* err = -EADDRNOTAVAIL */ | 1950 | goto done; /* err = -EADDRNOTAVAIL */ |
@@ -2014,7 +1998,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
2014 | newpsl->sl_addr[i] = psl->sl_addr[i]; | 1998 | newpsl->sl_addr[i] = psl->sl_addr[i]; |
2015 | /* decrease mem now to avoid the memleak warning */ | 1999 | /* decrease mem now to avoid the memleak warning */ |
2016 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); | 2000 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
2017 | call_rcu(&psl->rcu, ip_sf_socklist_reclaim); | 2001 | kfree_rcu(psl, rcu); |
2018 | } | 2002 | } |
2019 | rcu_assign_pointer(pmc->sflist, newpsl); | 2003 | rcu_assign_pointer(pmc->sflist, newpsl); |
2020 | psl = newpsl; | 2004 | psl = newpsl; |
@@ -2079,7 +2063,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
2079 | goto done; | 2063 | goto done; |
2080 | } | 2064 | } |
2081 | 2065 | ||
2082 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2066 | for_each_pmc_rtnl(inet, pmc) { |
2083 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && | 2067 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && |
2084 | pmc->multi.imr_ifindex == imr.imr_ifindex) | 2068 | pmc->multi.imr_ifindex == imr.imr_ifindex) |
2085 | break; | 2069 | break; |
@@ -2109,13 +2093,13 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
2109 | (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, | 2093 | (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, |
2110 | msf->imsf_fmode, 0, NULL, 0); | 2094 | msf->imsf_fmode, 0, NULL, 0); |
2111 | } | 2095 | } |
2112 | psl = pmc->sflist; | 2096 | psl = rtnl_dereference(pmc->sflist); |
2113 | if (psl) { | 2097 | if (psl) { |
2114 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, | 2098 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
2115 | psl->sl_count, psl->sl_addr, 0); | 2099 | psl->sl_count, psl->sl_addr, 0); |
2116 | /* decrease mem now to avoid the memleak warning */ | 2100 | /* decrease mem now to avoid the memleak warning */ |
2117 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); | 2101 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
2118 | call_rcu(&psl->rcu, ip_sf_socklist_reclaim); | 2102 | kfree_rcu(psl, rcu); |
2119 | } else | 2103 | } else |
2120 | (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, |
2121 | 0, NULL, 0); | 2105 | 0, NULL, 0); |
@@ -2157,7 +2141,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, | |||
2157 | } | 2141 | } |
2158 | err = -EADDRNOTAVAIL; | 2142 | err = -EADDRNOTAVAIL; |
2159 | 2143 | ||
2160 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2144 | for_each_pmc_rtnl(inet, pmc) { |
2161 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && | 2145 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && |
2162 | pmc->multi.imr_ifindex == imr.imr_ifindex) | 2146 | pmc->multi.imr_ifindex == imr.imr_ifindex) |
2163 | break; | 2147 | break; |
@@ -2165,7 +2149,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, | |||
2165 | if (!pmc) /* must have a prior join */ | 2149 | if (!pmc) /* must have a prior join */ |
2166 | goto done; | 2150 | goto done; |
2167 | msf->imsf_fmode = pmc->sfmode; | 2151 | msf->imsf_fmode = pmc->sfmode; |
2168 | psl = pmc->sflist; | 2152 | psl = rtnl_dereference(pmc->sflist); |
2169 | rtnl_unlock(); | 2153 | rtnl_unlock(); |
2170 | if (!psl) { | 2154 | if (!psl) { |
2171 | len = 0; | 2155 | len = 0; |
@@ -2210,7 +2194,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, | |||
2210 | 2194 | ||
2211 | err = -EADDRNOTAVAIL; | 2195 | err = -EADDRNOTAVAIL; |
2212 | 2196 | ||
2213 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2197 | for_each_pmc_rtnl(inet, pmc) { |
2214 | if (pmc->multi.imr_multiaddr.s_addr == addr && | 2198 | if (pmc->multi.imr_multiaddr.s_addr == addr && |
2215 | pmc->multi.imr_ifindex == gsf->gf_interface) | 2199 | pmc->multi.imr_ifindex == gsf->gf_interface) |
2216 | break; | 2200 | break; |
@@ -2218,7 +2202,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, | |||
2218 | if (!pmc) /* must have a prior join */ | 2202 | if (!pmc) /* must have a prior join */ |
2219 | goto done; | 2203 | goto done; |
2220 | gsf->gf_fmode = pmc->sfmode; | 2204 | gsf->gf_fmode = pmc->sfmode; |
2221 | psl = pmc->sflist; | 2205 | psl = rtnl_dereference(pmc->sflist); |
2222 | rtnl_unlock(); | 2206 | rtnl_unlock(); |
2223 | count = psl ? psl->sl_count : 0; | 2207 | count = psl ? psl->sl_count : 0; |
2224 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; | 2208 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; |
@@ -2259,7 +2243,7 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) | |||
2259 | goto out; | 2243 | goto out; |
2260 | 2244 | ||
2261 | rcu_read_lock(); | 2245 | rcu_read_lock(); |
2262 | for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) { | 2246 | for_each_pmc_rcu(inet, pmc) { |
2263 | if (pmc->multi.imr_multiaddr.s_addr == loc_addr && | 2247 | if (pmc->multi.imr_multiaddr.s_addr == loc_addr && |
2264 | pmc->multi.imr_ifindex == dif) | 2248 | pmc->multi.imr_ifindex == dif) |
2265 | break; | 2249 | break; |
@@ -2267,7 +2251,7 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) | |||
2267 | ret = inet->mc_all; | 2251 | ret = inet->mc_all; |
2268 | if (!pmc) | 2252 | if (!pmc) |
2269 | goto unlock; | 2253 | goto unlock; |
2270 | psl = pmc->sflist; | 2254 | psl = rcu_dereference(pmc->sflist); |
2271 | ret = (pmc->sfmode == MCAST_EXCLUDE); | 2255 | ret = (pmc->sfmode == MCAST_EXCLUDE); |
2272 | if (!psl) | 2256 | if (!psl) |
2273 | goto unlock; | 2257 | goto unlock; |
@@ -2302,31 +2286,29 @@ void ip_mc_drop_socket(struct sock *sk) | |||
2302 | return; | 2286 | return; |
2303 | 2287 | ||
2304 | rtnl_lock(); | 2288 | rtnl_lock(); |
2305 | while ((iml = inet->mc_list) != NULL) { | 2289 | while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { |
2306 | struct in_device *in_dev; | 2290 | struct in_device *in_dev; |
2307 | rcu_assign_pointer(inet->mc_list, iml->next); | ||
2308 | 2291 | ||
2292 | inet->mc_list = iml->next_rcu; | ||
2309 | in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); | 2293 | in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); |
2310 | (void) ip_mc_leave_src(sk, iml, in_dev); | 2294 | (void) ip_mc_leave_src(sk, iml, in_dev); |
2311 | if (in_dev != NULL) { | 2295 | if (in_dev != NULL) |
2312 | 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); |
2313 | in_dev_put(in_dev); | ||
2314 | } | ||
2315 | /* decrease mem now to avoid the memleak warning */ | 2297 | /* decrease mem now to avoid the memleak warning */ |
2316 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | 2298 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); |
2317 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | 2299 | kfree_rcu(iml, rcu); |
2318 | } | 2300 | } |
2319 | rtnl_unlock(); | 2301 | rtnl_unlock(); |
2320 | } | 2302 | } |
2321 | 2303 | ||
2322 | 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) | ||
2323 | { | 2306 | { |
2324 | struct ip_mc_list *im; | 2307 | struct ip_mc_list *im; |
2325 | struct ip_sf_list *psf; | 2308 | struct ip_sf_list *psf; |
2326 | int rv = 0; | 2309 | int rv = 0; |
2327 | 2310 | ||
2328 | read_lock(&in_dev->mc_list_lock); | 2311 | for_each_pmc_rcu(in_dev, im) { |
2329 | for (im=in_dev->mc_list; im; im=im->next) { | ||
2330 | if (im->multiaddr == mc_addr) | 2312 | if (im->multiaddr == mc_addr) |
2331 | break; | 2313 | break; |
2332 | } | 2314 | } |
@@ -2347,7 +2329,6 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p | |||
2347 | } else | 2329 | } else |
2348 | rv = 1; /* unspecified source; tentatively allow */ | 2330 | rv = 1; /* unspecified source; tentatively allow */ |
2349 | } | 2331 | } |
2350 | read_unlock(&in_dev->mc_list_lock); | ||
2351 | return rv; | 2332 | return rv; |
2352 | } | 2333 | } |
2353 | 2334 | ||
@@ -2373,13 +2354,11 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) | |||
2373 | in_dev = __in_dev_get_rcu(state->dev); | 2354 | in_dev = __in_dev_get_rcu(state->dev); |
2374 | if (!in_dev) | 2355 | if (!in_dev) |
2375 | continue; | 2356 | continue; |
2376 | read_lock(&in_dev->mc_list_lock); | 2357 | im = rcu_dereference(in_dev->mc_list); |
2377 | im = in_dev->mc_list; | ||
2378 | if (im) { | 2358 | if (im) { |
2379 | state->in_dev = in_dev; | 2359 | state->in_dev = in_dev; |
2380 | break; | 2360 | break; |
2381 | } | 2361 | } |
2382 | read_unlock(&in_dev->mc_list_lock); | ||
2383 | } | 2362 | } |
2384 | return im; | 2363 | return im; |
2385 | } | 2364 | } |
@@ -2387,11 +2366,9 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) | |||
2387 | static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) | 2366 | static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) |
2388 | { | 2367 | { |
2389 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2368 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2390 | im = im->next; | ||
2391 | while (!im) { | ||
2392 | if (likely(state->in_dev != NULL)) | ||
2393 | read_unlock(&state->in_dev->mc_list_lock); | ||
2394 | 2369 | ||
2370 | im = rcu_dereference(im->next_rcu); | ||
2371 | while (!im) { | ||
2395 | state->dev = next_net_device_rcu(state->dev); | 2372 | state->dev = next_net_device_rcu(state->dev); |
2396 | if (!state->dev) { | 2373 | if (!state->dev) { |
2397 | state->in_dev = NULL; | 2374 | state->in_dev = NULL; |
@@ -2400,8 +2377,7 @@ static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_li | |||
2400 | state->in_dev = __in_dev_get_rcu(state->dev); | 2377 | state->in_dev = __in_dev_get_rcu(state->dev); |
2401 | if (!state->in_dev) | 2378 | if (!state->in_dev) |
2402 | continue; | 2379 | continue; |
2403 | read_lock(&state->in_dev->mc_list_lock); | 2380 | im = rcu_dereference(state->in_dev->mc_list); |
2404 | im = state->in_dev->mc_list; | ||
2405 | } | 2381 | } |
2406 | return im; | 2382 | return im; |
2407 | } | 2383 | } |
@@ -2437,10 +2413,8 @@ static void igmp_mc_seq_stop(struct seq_file *seq, void *v) | |||
2437 | __releases(rcu) | 2413 | __releases(rcu) |
2438 | { | 2414 | { |
2439 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2415 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2440 | if (likely(state->in_dev != NULL)) { | 2416 | |
2441 | read_unlock(&state->in_dev->mc_list_lock); | 2417 | state->in_dev = NULL; |
2442 | state->in_dev = NULL; | ||
2443 | } | ||
2444 | state->dev = NULL; | 2418 | state->dev = NULL; |
2445 | rcu_read_unlock(); | 2419 | rcu_read_unlock(); |
2446 | } | 2420 | } |
@@ -2462,7 +2436,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) | |||
2462 | querier = "NONE"; | 2436 | querier = "NONE"; |
2463 | #endif | 2437 | #endif |
2464 | 2438 | ||
2465 | if (state->in_dev->mc_list == im) { | 2439 | if (rcu_dereference(state->in_dev->mc_list) == im) { |
2466 | seq_printf(seq, "%d\t%-10s: %5d %7s\n", | 2440 | seq_printf(seq, "%d\t%-10s: %5d %7s\n", |
2467 | state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); | 2441 | state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); |
2468 | } | 2442 | } |
@@ -2521,8 +2495,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) | |||
2521 | idev = __in_dev_get_rcu(state->dev); | 2495 | idev = __in_dev_get_rcu(state->dev); |
2522 | if (unlikely(idev == NULL)) | 2496 | if (unlikely(idev == NULL)) |
2523 | continue; | 2497 | continue; |
2524 | read_lock(&idev->mc_list_lock); | 2498 | im = rcu_dereference(idev->mc_list); |
2525 | im = idev->mc_list; | ||
2526 | if (likely(im != NULL)) { | 2499 | if (likely(im != NULL)) { |
2527 | spin_lock_bh(&im->lock); | 2500 | spin_lock_bh(&im->lock); |
2528 | psf = im->sources; | 2501 | psf = im->sources; |
@@ -2533,7 +2506,6 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) | |||
2533 | } | 2506 | } |
2534 | spin_unlock_bh(&im->lock); | 2507 | spin_unlock_bh(&im->lock); |
2535 | } | 2508 | } |
2536 | read_unlock(&idev->mc_list_lock); | ||
2537 | } | 2509 | } |
2538 | return psf; | 2510 | return psf; |
2539 | } | 2511 | } |
@@ -2547,9 +2519,6 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l | |||
2547 | spin_unlock_bh(&state->im->lock); | 2519 | spin_unlock_bh(&state->im->lock); |
2548 | state->im = state->im->next; | 2520 | state->im = state->im->next; |
2549 | while (!state->im) { | 2521 | while (!state->im) { |
2550 | if (likely(state->idev != NULL)) | ||
2551 | read_unlock(&state->idev->mc_list_lock); | ||
2552 | |||
2553 | state->dev = next_net_device_rcu(state->dev); | 2522 | state->dev = next_net_device_rcu(state->dev); |
2554 | if (!state->dev) { | 2523 | if (!state->dev) { |
2555 | state->idev = NULL; | 2524 | state->idev = NULL; |
@@ -2558,8 +2527,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l | |||
2558 | state->idev = __in_dev_get_rcu(state->dev); | 2527 | state->idev = __in_dev_get_rcu(state->dev); |
2559 | if (!state->idev) | 2528 | if (!state->idev) |
2560 | continue; | 2529 | continue; |
2561 | read_lock(&state->idev->mc_list_lock); | 2530 | state->im = rcu_dereference(state->idev->mc_list); |
2562 | state->im = state->idev->mc_list; | ||
2563 | } | 2531 | } |
2564 | if (!state->im) | 2532 | if (!state->im) |
2565 | break; | 2533 | break; |
@@ -2605,10 +2573,7 @@ static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) | |||
2605 | spin_unlock_bh(&state->im->lock); | 2573 | spin_unlock_bh(&state->im->lock); |
2606 | state->im = NULL; | 2574 | state->im = NULL; |
2607 | } | 2575 | } |
2608 | if (likely(state->idev != NULL)) { | 2576 | state->idev = NULL; |
2609 | read_unlock(&state->idev->mc_list_lock); | ||
2610 | state->idev = NULL; | ||
2611 | } | ||
2612 | state->dev = NULL; | 2577 | state->dev = NULL; |
2613 | rcu_read_unlock(); | 2578 | rcu_read_unlock(); |
2614 | } | 2579 | } |