]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/mcast.c
[IGMP]: workaround for IGMP v1/v2 bug
[~andy/linux] / net / ipv6 / mcast.c
index 39a96c768102f3915e347fbf3880ace5445db5fc..fd939da090c451f848102667eecfcb60995f8a7d 100644 (file)
@@ -164,7 +164,7 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
 #define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
 #define MLDV2_EXP(thresh, nbmant, nbexp, value) \
        ((value) < (thresh) ? (value) : \
-       ((MLDV2_MASK(value, nbmant) | (1<<(nbmant+nbexp))) << \
+       ((MLDV2_MASK(value, nbmant) | (1<<(nbmant))) << \
        (MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
 
 #define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)
@@ -545,8 +545,10 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
                        sock_kfree_s(sk, newpsl, IP6_SFLSIZE(newpsl->sl_max));
                        goto done;
                }
-       } else
+       } else {
                newpsl = NULL;
+               (void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0);
+       }
        psl = pmc->sflist;
        if (psl) {
                (void) ip6_mc_del_src(idev, group, pmc->sfmode,
@@ -1087,7 +1089,7 @@ static void mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
 
 int igmp6_event_query(struct sk_buff *skb)
 {
-       struct mld2_query *mlh2 = (struct mld2_query *) skb->h.raw;
+       struct mld2_query *mlh2 = NULL;
        struct ifmcaddr6 *ma;
        struct in6_addr *group;
        unsigned long max_delay;
@@ -1140,6 +1142,13 @@ int igmp6_event_query(struct sk_buff *skb)
                /* clear deleted report items */
                mld_clear_delrec(idev);
        } else if (len >= 28) {
+               int srcs_offset = sizeof(struct mld2_query) - 
+                                 sizeof(struct icmp6hdr);
+               if (!pskb_may_pull(skb, srcs_offset)) {
+                       in6_dev_put(idev);
+                       return -EINVAL;
+               }
+               mlh2 = (struct mld2_query *) skb->h.raw;
                max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;
                if (!max_delay)
                        max_delay = 1;
@@ -1156,7 +1165,15 @@ int igmp6_event_query(struct sk_buff *skb)
                        return 0;
                }
                /* mark sources to include, if group & source-specific */
-               mark = mlh2->nsrcs != 0;
+               if (mlh2->nsrcs != 0) {
+                       if (!pskb_may_pull(skb, srcs_offset + 
+                               mlh2->nsrcs * sizeof(struct in6_addr))) {
+                               in6_dev_put(idev);
+                               return -EINVAL;
+                       }
+                       mlh2 = (struct mld2_query *) skb->h.raw;
+                       mark = 1;
+               }
        } else {
                in6_dev_put(idev);
                return -EINVAL;
@@ -1214,6 +1231,11 @@ int igmp6_event_report(struct sk_buff *skb)
        if (skb->pkt_type == PACKET_LOOPBACK)
                return 0;
 
+       /* send our report if the MC router may not have heard this report */
+       if (skb->pkt_type != PACKET_MULTICAST &&
+           skb->pkt_type != PACKET_BROADCAST)
+               return 0;
+
        if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
                return -EINVAL;