diff options
Diffstat (limited to 'net/ipv6/mcast.c')
| -rw-r--r-- | net/ipv6/mcast.c | 68 | 
1 files changed, 47 insertions, 21 deletions
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 393b6e6f50a9..562fcd14fdea 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c  | |||
| @@ -188,6 +188,16 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr) | |||
| 188 | if (!ipv6_addr_is_multicast(addr)) | 188 | if (!ipv6_addr_is_multicast(addr)) | 
| 189 | return -EINVAL; | 189 | return -EINVAL; | 
| 190 | 190 | ||
| 191 | read_lock_bh(&ipv6_sk_mc_lock); | ||
| 192 | for (mc_lst=np->ipv6_mc_list; mc_lst; mc_lst=mc_lst->next) { | ||
| 193 | if ((ifindex == 0 || mc_lst->ifindex == ifindex) && | ||
| 194 | ipv6_addr_equal(&mc_lst->addr, addr)) { | ||
| 195 | read_unlock_bh(&ipv6_sk_mc_lock); | ||
| 196 | return -EADDRINUSE; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | read_unlock_bh(&ipv6_sk_mc_lock); | ||
| 200 | |||
| 191 | mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL); | 201 | mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL); | 
| 192 | 202 | ||
| 193 | if (mc_lst == NULL) | 203 | if (mc_lst == NULL) | 
| @@ -349,6 +359,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
| 349 | struct ipv6_pinfo *inet6 = inet6_sk(sk); | 359 | struct ipv6_pinfo *inet6 = inet6_sk(sk); | 
| 350 | struct ip6_sf_socklist *psl; | 360 | struct ip6_sf_socklist *psl; | 
| 351 | int i, j, rv; | 361 | int i, j, rv; | 
| 362 | int leavegroup = 0; | ||
| 352 | int err; | 363 | int err; | 
| 353 | 364 | ||
| 354 | if (pgsr->gsr_group.ss_family != AF_INET6 || | 365 | if (pgsr->gsr_group.ss_family != AF_INET6 || | 
| @@ -368,6 +379,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
| 368 | 379 | ||
| 369 | err = -EADDRNOTAVAIL; | 380 | err = -EADDRNOTAVAIL; | 
| 370 | 381 | ||
| 382 | read_lock_bh(&ipv6_sk_mc_lock); | ||
| 371 | for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { | 383 | for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { | 
| 372 | if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) | 384 | if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) | 
| 373 | continue; | 385 | continue; | 
| @@ -401,6 +413,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
| 401 | if (rv) /* source not found */ | 413 | if (rv) /* source not found */ | 
| 402 | goto done; | 414 | goto done; | 
| 403 | 415 | ||
| 416 | /* special case - (INCLUDE, empty) == LEAVE_GROUP */ | ||
| 417 | if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { | ||
| 418 | leavegroup = 1; | ||
| 419 | goto done; | ||
| 420 | } | ||
| 421 | |||
| 404 | /* update the interface filter */ | 422 | /* update the interface filter */ | 
| 405 | ip6_mc_del_src(idev, group, omode, 1, source, 1); | 423 | ip6_mc_del_src(idev, group, omode, 1, source, 1); | 
| 406 | 424 | ||
| @@ -453,9 +471,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
| 453 | /* update the interface list */ | 471 | /* update the interface list */ | 
| 454 | ip6_mc_add_src(idev, group, omode, 1, source, 1); | 472 | ip6_mc_add_src(idev, group, omode, 1, source, 1); | 
| 455 | done: | 473 | done: | 
| 474 | read_unlock_bh(&ipv6_sk_mc_lock); | ||
| 456 | read_unlock_bh(&idev->lock); | 475 | read_unlock_bh(&idev->lock); | 
| 457 | in6_dev_put(idev); | 476 | in6_dev_put(idev); | 
| 458 | dev_put(dev); | 477 | dev_put(dev); | 
| 478 | if (leavegroup) | ||
| 479 | return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); | ||
| 459 | return err; | 480 | return err; | 
| 460 | } | 481 | } | 
| 461 | 482 | ||
| @@ -1280,15 +1301,6 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size) | |||
| 1280 | return NULL; | 1301 | return NULL; | 
| 1281 | 1302 | ||
| 1282 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | 1303 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | 
| 1283 | if (dev->hard_header) { | ||
| 1284 | unsigned char ha[MAX_ADDR_LEN]; | ||
| 1285 | |||
| 1286 | ndisc_mc_map(&mld2_all_mcr, ha, dev, 1); | ||
| 1287 | if (dev->hard_header(skb, dev, ETH_P_IPV6,ha,NULL,size) < 0) { | ||
| 1288 | kfree_skb(skb); | ||
| 1289 | return NULL; | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | 1304 | ||
| 1293 | if (ipv6_get_lladdr(dev, &addr_buf)) { | 1305 | if (ipv6_get_lladdr(dev, &addr_buf)) { | 
| 1294 | /* <draft-ietf-magma-mld-source-05.txt>: | 1306 | /* <draft-ietf-magma-mld-source-05.txt>: | 
| @@ -1312,6 +1324,30 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size) | |||
| 1312 | return skb; | 1324 | return skb; | 
| 1313 | } | 1325 | } | 
| 1314 | 1326 | ||
| 1327 | static inline int mld_dev_queue_xmit2(struct sk_buff *skb) | ||
| 1328 | { | ||
| 1329 | struct net_device *dev = skb->dev; | ||
| 1330 | |||
| 1331 | if (dev->hard_header) { | ||
| 1332 | unsigned char ha[MAX_ADDR_LEN]; | ||
| 1333 | int err; | ||
| 1334 | |||
| 1335 | ndisc_mc_map(&skb->nh.ipv6h->daddr, ha, dev, 1); | ||
| 1336 | err = dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len); | ||
| 1337 | if (err < 0) { | ||
| 1338 | kfree_skb(skb); | ||
| 1339 | return err; | ||
| 1340 | } | ||
| 1341 | } | ||
| 1342 | return dev_queue_xmit(skb); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | static inline int mld_dev_queue_xmit(struct sk_buff *skb) | ||
| 1346 | { | ||
| 1347 | return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dev, | ||
| 1348 | mld_dev_queue_xmit2); | ||
| 1349 | } | ||
| 1350 | |||
| 1315 | static void mld_sendpack(struct sk_buff *skb) | 1351 | static void mld_sendpack(struct sk_buff *skb) | 
| 1316 | { | 1352 | { | 
| 1317 | struct ipv6hdr *pip6 = skb->nh.ipv6h; | 1353 | struct ipv6hdr *pip6 = skb->nh.ipv6h; | 
| @@ -1329,7 +1365,7 @@ static void mld_sendpack(struct sk_buff *skb) | |||
| 1329 | pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, | 1365 | pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, | 
| 1330 | IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); | 1366 | IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); | 
| 1331 | err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, | 1367 | err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, | 
| 1332 | dev_queue_xmit); | 1368 | mld_dev_queue_xmit); | 
| 1333 | if (!err) { | 1369 | if (!err) { | 
| 1334 | ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS); | 1370 | ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS); | 
| 1335 | IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS); | 1371 | IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS); | 
| @@ -1635,12 +1671,6 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) | |||
| 1635 | } | 1671 | } | 
| 1636 | 1672 | ||
| 1637 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | 1673 | skb_reserve(skb, LL_RESERVED_SPACE(dev)); | 
| 1638 | if (dev->hard_header) { | ||
| 1639 | unsigned char ha[MAX_ADDR_LEN]; | ||
| 1640 | ndisc_mc_map(snd_addr, ha, dev, 1); | ||
| 1641 | if (dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len) < 0) | ||
| 1642 | goto out; | ||
| 1643 | } | ||
| 1644 | 1674 | ||
| 1645 | if (ipv6_get_lladdr(dev, &addr_buf)) { | 1675 | if (ipv6_get_lladdr(dev, &addr_buf)) { | 
| 1646 | /* <draft-ietf-magma-mld-source-05.txt>: | 1676 | /* <draft-ietf-magma-mld-source-05.txt>: | 
| @@ -1668,7 +1698,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) | |||
| 1668 | idev = in6_dev_get(skb->dev); | 1698 | idev = in6_dev_get(skb->dev); | 
| 1669 | 1699 | ||
| 1670 | err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, | 1700 | err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev, | 
| 1671 | dev_queue_xmit); | 1701 | mld_dev_queue_xmit); | 
| 1672 | if (!err) { | 1702 | if (!err) { | 
| 1673 | if (type == ICMPV6_MGM_REDUCTION) | 1703 | if (type == ICMPV6_MGM_REDUCTION) | 
| 1674 | ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS); | 1704 | ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS); | 
| @@ -1682,10 +1712,6 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) | |||
| 1682 | if (likely(idev != NULL)) | 1712 | if (likely(idev != NULL)) | 
| 1683 | in6_dev_put(idev); | 1713 | in6_dev_put(idev); | 
| 1684 | return; | 1714 | return; | 
| 1685 | |||
| 1686 | out: | ||
| 1687 | IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); | ||
| 1688 | kfree_skb(skb); | ||
| 1689 | } | 1715 | } | 
| 1690 | 1716 | ||
| 1691 | static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, | 1717 | static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, | 
