aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid L Stevens <dlstevens@us.ibm.com>2006-01-18 17:20:56 -0500
committerDavid S. Miller <davem@davemloft.net>2006-01-18 17:20:56 -0500
commitad12583f46bcb6ce93ccd99fa063c0d701146b2e (patch)
tree58d68cf1a60cd3cf2b8ee0e9fbdcb38454e2082e /net
parent7ac5459ec0f074022818af35c589b9e2b406d7c3 (diff)
[IPV4]: Fix multiple bugs in IGMPv3
1) fix "mld_marksources()" to a) send nothing when all queried sources are excluded b) send full exclude report when source queried sources are not excluded c) don't schedule a timer when there's nothing to report 2) fix "add_grec()" to send empty-source records when it should The original check doesn't account for a non-empty source list with all sources inactive; the new code keeps that short-circuit case, and also generates the group header with an empty list if needed. 3) fix mca_crcount decrement to be after add_grec(), which needs its original value 4) add/remove delete records and prevent current advertisements when an exclude-mode filter moves from "active" to "inactive" or vice versa based on new filter additions. Items 1-3 are just IPv4 versions of the IPv6 bugs found by Yan Zheng and fixed earlier. Item #4 is a related bug that affects exclude-mode change records only (but not queries) and also occurs in IPv6 (IPv6 version coming soon). Signed-off-by: David L Stevens <dlstevens@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/igmp.c152
1 files changed, 122 insertions, 30 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 192092b89e53..d8ce7133cd8f 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -233,7 +233,18 @@ static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type,
233 case IGMPV3_MODE_IS_EXCLUDE: 233 case IGMPV3_MODE_IS_EXCLUDE:
234 if (gdeleted || sdeleted) 234 if (gdeleted || sdeleted)
235 return 0; 235 return 0;
236 return !(pmc->gsquery && !psf->sf_gsresp); 236 if (!(pmc->gsquery && !psf->sf_gsresp)) {
237 if (pmc->sfmode == MCAST_INCLUDE)
238 return 1;
239 /* don't include if this source is excluded
240 * in all filters
241 */
242 if (psf->sf_count[MCAST_INCLUDE])
243 return type == IGMPV3_MODE_IS_INCLUDE;
244 return pmc->sfcount[MCAST_EXCLUDE] ==
245 psf->sf_count[MCAST_EXCLUDE];
246 }
247 return 0;
237 case IGMPV3_CHANGE_TO_INCLUDE: 248 case IGMPV3_CHANGE_TO_INCLUDE:
238 if (gdeleted || sdeleted) 249 if (gdeleted || sdeleted)
239 return 0; 250 return 0;
@@ -385,7 +396,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
385 struct igmpv3_report *pih; 396 struct igmpv3_report *pih;
386 struct igmpv3_grec *pgr = NULL; 397 struct igmpv3_grec *pgr = NULL;
387 struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 398 struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
388 int scount, first, isquery, truncate; 399 int scount, stotal, first, isquery, truncate;
389 400
390 if (pmc->multiaddr == IGMP_ALL_HOSTS) 401 if (pmc->multiaddr == IGMP_ALL_HOSTS)
391 return skb; 402 return skb;
@@ -395,25 +406,13 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
395 truncate = type == IGMPV3_MODE_IS_EXCLUDE || 406 truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
396 type == IGMPV3_CHANGE_TO_EXCLUDE; 407 type == IGMPV3_CHANGE_TO_EXCLUDE;
397 408
409 stotal = scount = 0;
410
398 psf_list = sdeleted ? &pmc->tomb : &pmc->sources; 411 psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
399 412
400 if (!*psf_list) { 413 if (!*psf_list)
401 if (type == IGMPV3_ALLOW_NEW_SOURCES || 414 goto empty_source;
402 type == IGMPV3_BLOCK_OLD_SOURCES) 415
403 return skb;
404 if (pmc->crcount || isquery) {
405 /* make sure we have room for group header and at
406 * least one source.
407 */
408 if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)+
409 sizeof(__u32)) {
410 igmpv3_sendpack(skb);
411 skb = NULL; /* add_grhead will get a new one */
412 }
413 skb = add_grhead(skb, pmc, type, &pgr);
414 }
415 return skb;
416 }
417 pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL; 416 pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL;
418 417
419 /* EX and TO_EX get a fresh packet, if needed */ 418 /* EX and TO_EX get a fresh packet, if needed */
@@ -426,7 +425,6 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
426 } 425 }
427 } 426 }
428 first = 1; 427 first = 1;
429 scount = 0;
430 psf_prev = NULL; 428 psf_prev = NULL;
431 for (psf=*psf_list; psf; psf=psf_next) { 429 for (psf=*psf_list; psf; psf=psf_next) {
432 u32 *psrc; 430 u32 *psrc;
@@ -460,7 +458,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
460 } 458 }
461 psrc = (u32 *)skb_put(skb, sizeof(u32)); 459 psrc = (u32 *)skb_put(skb, sizeof(u32));
462 *psrc = psf->sf_inaddr; 460 *psrc = psf->sf_inaddr;
463 scount++; 461 scount++; stotal++;
464 if ((type == IGMPV3_ALLOW_NEW_SOURCES || 462 if ((type == IGMPV3_ALLOW_NEW_SOURCES ||
465 type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) { 463 type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
466 psf->sf_crcount--; 464 psf->sf_crcount--;
@@ -475,6 +473,21 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
475 } 473 }
476 psf_prev = psf; 474 psf_prev = psf;
477 } 475 }
476
477empty_source:
478 if (!stotal) {
479 if (type == IGMPV3_ALLOW_NEW_SOURCES ||
480 type == IGMPV3_BLOCK_OLD_SOURCES)
481 return skb;
482 if (pmc->crcount || isquery) {
483 /* make sure we have room for group header */
484 if (skb && AVAILABLE(skb)<sizeof(struct igmpv3_grec)) {
485 igmpv3_sendpack(skb);
486 skb = NULL; /* add_grhead will get a new one */
487 }
488 skb = add_grhead(skb, pmc, type, &pgr);
489 }
490 }
478 if (pgr) 491 if (pgr)
479 pgr->grec_nsrcs = htons(scount); 492 pgr->grec_nsrcs = htons(scount);
480 493
@@ -557,11 +570,11 @@ static void igmpv3_send_cr(struct in_device *in_dev)
557 skb = add_grec(skb, pmc, dtype, 1, 1); 570 skb = add_grec(skb, pmc, dtype, 1, 1);
558 } 571 }
559 if (pmc->crcount) { 572 if (pmc->crcount) {
560 pmc->crcount--;
561 if (pmc->sfmode == MCAST_EXCLUDE) { 573 if (pmc->sfmode == MCAST_EXCLUDE) {
562 type = IGMPV3_CHANGE_TO_INCLUDE; 574 type = IGMPV3_CHANGE_TO_INCLUDE;
563 skb = add_grec(skb, pmc, type, 1, 0); 575 skb = add_grec(skb, pmc, type, 1, 0);
564 } 576 }
577 pmc->crcount--;
565 if (pmc->crcount == 0) { 578 if (pmc->crcount == 0) {
566 igmpv3_clear_zeros(&pmc->tomb); 579 igmpv3_clear_zeros(&pmc->tomb);
567 igmpv3_clear_zeros(&pmc->sources); 580 igmpv3_clear_zeros(&pmc->sources);
@@ -594,12 +607,12 @@ static void igmpv3_send_cr(struct in_device *in_dev)
594 607
595 /* filter mode changes */ 608 /* filter mode changes */
596 if (pmc->crcount) { 609 if (pmc->crcount) {
597 pmc->crcount--;
598 if (pmc->sfmode == MCAST_EXCLUDE) 610 if (pmc->sfmode == MCAST_EXCLUDE)
599 type = IGMPV3_CHANGE_TO_EXCLUDE; 611 type = IGMPV3_CHANGE_TO_EXCLUDE;
600 else 612 else
601 type = IGMPV3_CHANGE_TO_INCLUDE; 613 type = IGMPV3_CHANGE_TO_INCLUDE;
602 skb = add_grec(skb, pmc, type, 0, 0); 614 skb = add_grec(skb, pmc, type, 0, 0);
615 pmc->crcount--;
603 } 616 }
604 spin_unlock_bh(&pmc->lock); 617 spin_unlock_bh(&pmc->lock);
605 } 618 }
@@ -735,11 +748,43 @@ static void igmp_timer_expire(unsigned long data)
735 ip_ma_put(im); 748 ip_ma_put(im);
736} 749}
737 750
738static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs) 751/* mark EXCLUDE-mode sources */
752static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
753{
754 struct ip_sf_list *psf;
755 int i, scount;
756
757 scount = 0;
758 for (psf=pmc->sources; psf; psf=psf->sf_next) {
759 if (scount == nsrcs)
760 break;
761 for (i=0; i<nsrcs; i++) {
762 /* skip inactive filters */
763 if (pmc->sfcount[MCAST_INCLUDE] ||
764 pmc->sfcount[MCAST_EXCLUDE] !=
765 psf->sf_count[MCAST_EXCLUDE])
766 continue;
767 if (srcs[i] == psf->sf_inaddr) {
768 scount++;
769 break;
770 }
771 }
772 }
773 pmc->gsquery = 0;
774 if (scount == nsrcs) /* all sources excluded */
775 return 0;
776 return 1;
777}
778
779static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
739{ 780{
740 struct ip_sf_list *psf; 781 struct ip_sf_list *psf;
741 int i, scount; 782 int i, scount;
742 783
784 if (pmc->sfmode == MCAST_EXCLUDE)
785 return igmp_xmarksources(pmc, nsrcs, srcs);
786
787 /* mark INCLUDE-mode sources */
743 scount = 0; 788 scount = 0;
744 for (psf=pmc->sources; psf; psf=psf->sf_next) { 789 for (psf=pmc->sources; psf; psf=psf->sf_next) {
745 if (scount == nsrcs) 790 if (scount == nsrcs)
@@ -751,6 +796,12 @@ static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
751 break; 796 break;
752 } 797 }
753 } 798 }
799 if (!scount) {
800 pmc->gsquery = 0;
801 return 0;
802 }
803 pmc->gsquery = 1;
804 return 1;
754} 805}
755 806
756static void igmp_heard_report(struct in_device *in_dev, u32 group) 807static void igmp_heard_report(struct in_device *in_dev, u32 group)
@@ -845,6 +896,8 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
845 */ 896 */
846 read_lock(&in_dev->mc_list_lock); 897 read_lock(&in_dev->mc_list_lock);
847 for (im=in_dev->mc_list; im!=NULL; im=im->next) { 898 for (im=in_dev->mc_list; im!=NULL; im=im->next) {
899 int changed;
900
848 if (group && group != im->multiaddr) 901 if (group && group != im->multiaddr)
849 continue; 902 continue;
850 if (im->multiaddr == IGMP_ALL_HOSTS) 903 if (im->multiaddr == IGMP_ALL_HOSTS)
@@ -854,10 +907,11 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
854 im->gsquery = im->gsquery && mark; 907 im->gsquery = im->gsquery && mark;
855 else 908 else
856 im->gsquery = mark; 909 im->gsquery = mark;
857 if (im->gsquery) 910 changed = !im->gsquery ||
858 igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); 911 igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
859 spin_unlock_bh(&im->lock); 912 spin_unlock_bh(&im->lock);
860 igmp_mod_timer(im, max_delay); 913 if (changed)
914 igmp_mod_timer(im, max_delay);
861 } 915 }
862 read_unlock(&in_dev->mc_list_lock); 916 read_unlock(&in_dev->mc_list_lock);
863} 917}
@@ -1510,7 +1564,7 @@ static void sf_markstate(struct ip_mc_list *pmc)
1510 1564
1511static int sf_setstate(struct ip_mc_list *pmc) 1565static int sf_setstate(struct ip_mc_list *pmc)
1512{ 1566{
1513 struct ip_sf_list *psf; 1567 struct ip_sf_list *psf, *dpsf;
1514 int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; 1568 int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
1515 int qrv = pmc->interface->mr_qrv; 1569 int qrv = pmc->interface->mr_qrv;
1516 int new_in, rv; 1570 int new_in, rv;
@@ -1522,8 +1576,46 @@ static int sf_setstate(struct ip_mc_list *pmc)
1522 !psf->sf_count[MCAST_INCLUDE]; 1576 !psf->sf_count[MCAST_INCLUDE];
1523 } else 1577 } else
1524 new_in = psf->sf_count[MCAST_INCLUDE] != 0; 1578 new_in = psf->sf_count[MCAST_INCLUDE] != 0;
1525 if (new_in != psf->sf_oldin) { 1579 if (new_in) {
1526 psf->sf_crcount = qrv; 1580 if (!psf->sf_oldin) {
1581 struct ip_sf_list *prev = 0;
1582
1583 for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next) {
1584 if (dpsf->sf_inaddr == psf->sf_inaddr)
1585 break;
1586 prev = dpsf;
1587 }
1588 if (dpsf) {
1589 if (prev)
1590 prev->sf_next = dpsf->sf_next;
1591 else
1592 pmc->tomb = dpsf->sf_next;
1593 kfree(dpsf);
1594 }
1595 psf->sf_crcount = qrv;
1596 rv++;
1597 }
1598 } else if (psf->sf_oldin) {
1599
1600 psf->sf_crcount = 0;
1601 /*
1602 * add or update "delete" records if an active filter
1603 * is now inactive
1604 */
1605 for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next)
1606 if (dpsf->sf_inaddr == psf->sf_inaddr)
1607 break;
1608 if (!dpsf) {
1609 dpsf = (struct ip_sf_list *)
1610 kmalloc(sizeof(*dpsf), GFP_ATOMIC);
1611 if (!dpsf)
1612 continue;
1613 *dpsf = *psf;
1614 /* pmc->lock held by callers */
1615 dpsf->sf_next = pmc->tomb;
1616 pmc->tomb = dpsf;
1617 }
1618 dpsf->sf_crcount = qrv;
1527 rv++; 1619 rv++;
1528 } 1620 }
1529 } 1621 }