diff options
Diffstat (limited to 'net/ipv6/mcast.c')
-rw-r--r-- | net/ipv6/mcast.c | 29 |
1 files changed, 22 insertions, 7 deletions
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 562fcd14fdea..29fed6e58d0a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -281,7 +281,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr) | |||
281 | } | 281 | } |
282 | write_unlock_bh(&ipv6_sk_mc_lock); | 282 | write_unlock_bh(&ipv6_sk_mc_lock); |
283 | 283 | ||
284 | return -ENOENT; | 284 | return -EADDRNOTAVAIL; |
285 | } | 285 | } |
286 | 286 | ||
287 | static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex) | 287 | static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex) |
@@ -386,12 +386,16 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
386 | if (ipv6_addr_equal(&pmc->addr, group)) | 386 | if (ipv6_addr_equal(&pmc->addr, group)) |
387 | break; | 387 | break; |
388 | } | 388 | } |
389 | if (!pmc) /* must have a prior join */ | 389 | if (!pmc) { /* must have a prior join */ |
390 | err = -EINVAL; | ||
390 | goto done; | 391 | goto done; |
392 | } | ||
391 | /* if a source filter was set, must be the same mode as before */ | 393 | /* if a source filter was set, must be the same mode as before */ |
392 | if (pmc->sflist) { | 394 | if (pmc->sflist) { |
393 | if (pmc->sfmode != omode) | 395 | if (pmc->sfmode != omode) { |
396 | err = -EINVAL; | ||
394 | goto done; | 397 | goto done; |
398 | } | ||
395 | } else if (pmc->sfmode != omode) { | 399 | } else if (pmc->sfmode != omode) { |
396 | /* allow mode switches for empty-set filters */ | 400 | /* allow mode switches for empty-set filters */ |
397 | ip6_mc_add_src(idev, group, omode, 0, NULL, 0); | 401 | ip6_mc_add_src(idev, group, omode, 0, NULL, 0); |
@@ -402,7 +406,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
402 | psl = pmc->sflist; | 406 | psl = pmc->sflist; |
403 | if (!add) { | 407 | if (!add) { |
404 | if (!psl) | 408 | if (!psl) |
405 | goto done; | 409 | goto done; /* err = -EADDRNOTAVAIL */ |
406 | rv = !0; | 410 | rv = !0; |
407 | for (i=0; i<psl->sl_count; i++) { | 411 | for (i=0; i<psl->sl_count; i++) { |
408 | rv = memcmp(&psl->sl_addr[i], source, | 412 | rv = memcmp(&psl->sl_addr[i], source, |
@@ -411,7 +415,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
411 | break; | 415 | break; |
412 | } | 416 | } |
413 | if (rv) /* source not found */ | 417 | if (rv) /* source not found */ |
414 | goto done; | 418 | goto done; /* err = -EADDRNOTAVAIL */ |
415 | 419 | ||
416 | /* special case - (INCLUDE, empty) == LEAVE_GROUP */ | 420 | /* special case - (INCLUDE, empty) == LEAVE_GROUP */ |
417 | if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { | 421 | if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { |
@@ -488,6 +492,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
488 | struct inet6_dev *idev; | 492 | struct inet6_dev *idev; |
489 | struct ipv6_pinfo *inet6 = inet6_sk(sk); | 493 | struct ipv6_pinfo *inet6 = inet6_sk(sk); |
490 | struct ip6_sf_socklist *newpsl, *psl; | 494 | struct ip6_sf_socklist *newpsl, *psl; |
495 | int leavegroup = 0; | ||
491 | int i, err; | 496 | int i, err; |
492 | 497 | ||
493 | group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; | 498 | group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; |
@@ -503,7 +508,12 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
503 | if (!idev) | 508 | if (!idev) |
504 | return -ENODEV; | 509 | return -ENODEV; |
505 | dev = idev->dev; | 510 | dev = idev->dev; |
506 | err = -EADDRNOTAVAIL; | 511 | |
512 | err = 0; | ||
513 | if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) { | ||
514 | leavegroup = 1; | ||
515 | goto done; | ||
516 | } | ||
507 | 517 | ||
508 | for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { | 518 | for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { |
509 | if (pmc->ifindex != gsf->gf_interface) | 519 | if (pmc->ifindex != gsf->gf_interface) |
@@ -511,8 +521,10 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
511 | if (ipv6_addr_equal(&pmc->addr, group)) | 521 | if (ipv6_addr_equal(&pmc->addr, group)) |
512 | break; | 522 | break; |
513 | } | 523 | } |
514 | if (!pmc) /* must have a prior join */ | 524 | if (!pmc) { /* must have a prior join */ |
525 | err = -EINVAL; | ||
515 | goto done; | 526 | goto done; |
527 | } | ||
516 | if (gsf->gf_numsrc) { | 528 | if (gsf->gf_numsrc) { |
517 | newpsl = (struct ip6_sf_socklist *)sock_kmalloc(sk, | 529 | newpsl = (struct ip6_sf_socklist *)sock_kmalloc(sk, |
518 | IP6_SFLSIZE(gsf->gf_numsrc), GFP_ATOMIC); | 530 | IP6_SFLSIZE(gsf->gf_numsrc), GFP_ATOMIC); |
@@ -544,10 +556,13 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
544 | (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); | 556 | (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); |
545 | pmc->sflist = newpsl; | 557 | pmc->sflist = newpsl; |
546 | pmc->sfmode = gsf->gf_fmode; | 558 | pmc->sfmode = gsf->gf_fmode; |
559 | err = 0; | ||
547 | done: | 560 | done: |
548 | read_unlock_bh(&idev->lock); | 561 | read_unlock_bh(&idev->lock); |
549 | in6_dev_put(idev); | 562 | in6_dev_put(idev); |
550 | dev_put(dev); | 563 | dev_put(dev); |
564 | if (leavegroup) | ||
565 | err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group); | ||
551 | return err; | 566 | return err; |
552 | } | 567 | } |
553 | 568 | ||