Hi

When ifmcaddr6's mca_sources is not NULL, but none of the sources in the list 
can be included in report,
(For example: when filter mode is exclude and the only source in the list has 
include count greater than zero.)
add_grec(...) may returns without add any data to the sk_buff. So 
MLD2_CHANGE_TO_EXCLUDE or MLD2_MODE_IS_EXCLUDE
report may be eliminated.

You can check this bug by test1.c in attachments. You will notice that there is 
no MLD2_CHANGE_TO_EXCLUDE report.

Regards

Signed-off-by: Yan Zheng<[EMAIL PROTECTED]>

Index:net/ipv6/mcast.c
==============================================================
--- linux-2.6.14/net/ipv6/mcast.c       2005-11-09 16:00:48.000000000 +0800
+++ linux/net/ipv6/mcast.c      2005-11-09 19:47:50.000000000 +0800
@@ -1445,18 +1445,21 @@ static struct sk_buff *add_grec(struct s
        struct mld2_report *pmr;
        struct mld2_grec *pgr = NULL;
        struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list;
-       int scount, first, isquery, truncate;
+       int scount, first, isquery, ischange, truncate;

        if (pmc->mca_flags & MAF_NOREPORT)
                return skb;

        isquery = type == MLD2_MODE_IS_INCLUDE ||
                  type == MLD2_MODE_IS_EXCLUDE;
+       ischange = type == MLD2_CHANGE_TO_INCLUDE ||
+ type == MLD2_CHANGE_TO_EXCLUDE; truncate = type == MLD2_MODE_IS_EXCLUDE ||
-                   type == MLD2_CHANGE_TO_EXCLUDE;
+                  type == MLD2_CHANGE_TO_EXCLUDE;

        psf_list = sdeleted ? &pmc->mca_tomb : &pmc->mca_sources;

+#if 0
        if (!*psf_list) {
                if (type == MLD2_ALLOW_NEW_SOURCES ||
                    type == MLD2_BLOCK_OLD_SOURCES)
@@ -1474,12 +1477,15 @@ static struct sk_buff *add_grec(struct s
                }
                return skb;
        }
+#endif
        pmr = skb ? (struct mld2_report *)skb->h.raw : NULL;

        /* EX and TO_EX get a fresh packet, if needed */
-       if (truncate) {
-               if (pmr && pmr->ngrec &&
-                   AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
+       if (truncate || ischange) {
+               int min_len;
+ min_len = truncate ? grec_size(pmc, type, gdeleted, sdeleted) : + (sizeof(struct mld2_grec) + sizeof(struct in6_addr));
+               if (pmr && pmr->ngrec && AVAILABLE(skb) < min_len) {
                        if (skb)
                                mld_sendpack(skb);
                        skb = mld_newpack(dev, dev->mtu);
@@ -1488,6 +1494,10 @@ static struct sk_buff *add_grec(struct s
        first = 1;
        scount = 0;
        psf_prev = NULL;
+       if (ischange || type == MLD2_MODE_IS_EXCLUDE) {
+               skb = add_grhead(skb, pmc, type, &pgr);
+               first = 0;
+       }
        for (psf=*psf_list; psf; psf=psf_next) {
                struct in6_addr *psrc;




#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#define IFINDEX 5  //Please adjust me first.

int main(int argc, char argv[])
{
	int sockfds[2];
	struct ipv6_mreq req;
	struct group_filter filter;
	struct sockaddr_in6 *psin6;
	req.ipv6mr_interface = IFINDEX;
	inet_pton(PF_INET6, "FF02::2000", &req.ipv6mr_multiaddr);

	sockfds[0] = socket(PF_INET6, SOCK_DGRAM, 0);
	sockfds[1] = socket(PF_INET6, SOCK_DGRAM, 0);

	filter.gf_interface = IFINDEX;
	filter.gf_fmode = MCAST_INCLUDE;
	filter.gf_numsrc = 1;
	psin6 = (struct sockaddr_in6 *)&filter.gf_group;
	psin6->sin6_family = AF_INET6;
	inet_pton(PF_INET6, "FF02::2000", &psin6->sin6_addr);
	psin6 = (struct sockaddr_in6 *)&filter.gf_slist[0];
	psin6->sin6_family = AF_INET6;
	inet_pton(PF_INET6, "2002:de12:1780::1", &psin6->sin6_addr);

	setsockopt(sockfds[0], SOL_IPV6, IPV6_ADD_MEMBERSHIP, &req, sizeof(req));
	setsockopt(sockfds[0], SOL_IPV6, MCAST_MSFILTER, &filter, sizeof(filter));
	sleep(10); //wait state change reports
	printf("change mode to exclude\n");
	setsockopt(sockfds[1], SOL_IPV6, IPV6_ADD_MEMBERSHIP, &req, sizeof(req));

	pause();
	return 0;
}



Reply via email to