]> git.hungrycats.org Git - linux/commitdiff
[IGMPv3/MPDv2]: Bug fixes and ipv4 multiprotocol API.
authorDavid Stevens <dlstevens@us.ibm.com>
Tue, 22 Apr 2003 16:51:48 +0000 (09:51 -0700)
committerArnaldo Carvalho de Melo <acme@conectiva.com.br>
Tue, 22 Apr 2003 16:51:48 +0000 (09:51 -0700)
include/linux/igmp.h
net/ipv4/igmp.c
net/ipv4/ip_output.c
net/ipv4/ip_sockglue.c
net/ipv6/ipv6_sockglue.c
net/ipv6/mcast.c

index 2cc2472d2eb275603d027702b5235696bd8c0917..8144cabdad060c0b4f00386532de4816286dcf2a 100644 (file)
@@ -191,7 +191,7 @@ struct ip_mc_list
          (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp))))
 
 #define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
-#define IGMPV3_MRC(value) IGMPV3_EXP(0x8000, 12, 3, value)
+#define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
 
 extern int ip_check_mc(struct in_device *dev, u32 mc_addr, u32 src_addr, u16 proto);
 extern int igmp_rcv(struct sk_buff *);
@@ -199,10 +199,12 @@ extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
 extern void ip_mc_drop_socket(struct sock *sk);
 extern int ip_mc_source(int add, int omode, struct sock *sk,
-               struct ip_mreq_source *mreqs);
-extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf);
+               struct ip_mreq_source *mreqs, int ifindex);
+extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex);
 extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
                struct ip_msfilter *optval, int *optlen);
+extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
+               struct group_filter *optval, int *optlen);
 extern int ip_mc_sf_allow(struct sock *sk, u32 local, u32 rmt, int dif);
 extern void ip_mr_init(void);
 extern void ip_mc_init_dev(struct in_device *);
index 265eb0604436065df25ea29f63cd060501872a16..1989420b799e72116cda1197f6147eefff9c34b3 100644 (file)
 
 #define IP_MAX_MEMBERSHIPS 20
 
+#ifdef CONFIG_IP_MULTICAST
 /* Parameter names and values are taken from igmp-v2-06 draft */
 
 #define IGMP_V1_Router_Present_Timeout         (400*HZ)
 #define IGMP_V2_SEEN(in_dev) ((in_dev)->mr_v2_seen && \
                time_before(jiffies, (in_dev)->mr_v2_seen))
 
-#ifdef CONFIG_MULTICAST
 static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
-#endif
 static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr);
 static void igmpv3_clear_delrec(struct in_device *in_dev);
 static int sf_setstate(struct ip_mc_list *pmc);
 static void sf_markstate(struct ip_mc_list *pmc);
+#endif
 static void ip_mc_clear_src(struct ip_mc_list *pmc);
 int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
        int sfcount, __u32 *psfsrc, int delta);
@@ -770,11 +770,18 @@ static void igmp_heard_query(struct in_device *in_dev, struct igmphdr *ih,
                        in_dev->mr_v2_seen = jiffies +
                                IGMP_V2_Router_Present_Timeout;
                }
+               /* cancel the interface change timer */
+               in_dev->mr_ifc_count = 0;
+               if (del_timer(&in_dev->mr_ifc_timer))
+                       atomic_dec(&in_dev->refcnt);
+               /* clear deleted report items */
                igmpv3_clear_delrec(in_dev);
        } else if (len < 12) {
                return; /* ignore bogus packet; freed by caller */
        } else { /* v3 */
                max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+               if (!max_delay)
+                       max_delay = 1;  /* can't mod w/ 0 */
                in_dev->mr_maxdelay = max_delay;
                if (ih3->qrv)
                        in_dev->mr_qrv = ih3->qrv;
@@ -951,7 +958,6 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
        in_dev->mc_tomb = pmc;
        write_unlock_bh(&in_dev->mc_lock);
 }
-#endif
 
 static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr)
 {
@@ -997,7 +1003,23 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
                in_dev_put(pmc->interface);
                kfree(pmc);
        }
+       /* clear dead sources, too */
+       read_lock(&in_dev->lock);
+       for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
+               struct ip_sf_list *psf, *psf_next;
+
+               spin_lock_bh(&pmc->lock);
+               psf = pmc->tomb;
+               pmc->tomb = 0;
+               spin_unlock_bh(&pmc->lock);
+               for (; psf; psf=psf_next) {
+                       psf_next = psf->sf_next;
+                       kfree(psf);
+               }
+       }
+       read_unlock(&in_dev->lock);
 }
+#endif
 
 static void igmp_group_dropped(struct ip_mc_list *im)
 {
@@ -1030,8 +1052,8 @@ static void igmp_group_dropped(struct ip_mc_list *im)
 
        igmp_ifc_event(in_dev);
 done:
-       ip_mc_clear_src(im);
 #endif
+       ip_mc_clear_src(im);
 }
 
 static void igmp_group_added(struct ip_mc_list *im)
@@ -1102,7 +1124,7 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
        im->crcount = 0;
        atomic_set(&im->refcnt, 1);
        spin_lock_init(&im->lock);
-#ifdef  CONFIG_IP_MULTICAST
+#ifdef CONFIG_IP_MULTICAST
        im->tm_running=0;
        init_timer(&im->timer);
        im->timer.data=(unsigned long)im;
@@ -1116,7 +1138,9 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
        im->next=in_dev->mc_list;
        in_dev->mc_list=im;
        write_unlock_bh(&in_dev->lock);
+#ifdef CONFIG_IP_MULTICAST
        igmpv3_del_delrec(in_dev, im->multiaddr);
+#endif
        igmp_group_added(im);
        if (in_dev->dev->flags & IFF_UP)
                ip_rt_multicast_event(in_dev);
@@ -1173,7 +1197,9 @@ void ip_mc_down(struct in_device *in_dev)
        for (i=in_dev->mc_list; i; i=i->next)
                igmp_group_dropped(i);
 
+#ifdef CONFIG_IP_MULTICAST
        igmpv3_clear_delrec(in_dev);
+#endif
 
        ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
 }
@@ -1186,12 +1212,12 @@ void ip_mc_up(struct in_device *in_dev)
 
        ASSERT_RTNL();
 
+       in_dev->mc_tomb = 0;
 #ifdef CONFIG_IP_MULTICAST
        in_dev->mr_gq_running = 0;
        init_timer(&in_dev->mr_gq_timer);
        in_dev->mr_gq_timer.data=(unsigned long) in_dev;
        in_dev->mr_gq_timer.function=&igmp_gq_timer_expire;
-       in_dev->mc_tomb = 0;
        in_dev->mr_ifc_count = 0;
        init_timer(&in_dev->mr_ifc_timer);
        in_dev->mr_ifc_timer.data=(unsigned long) in_dev;
@@ -1237,6 +1263,12 @@ static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
        struct net_device *dev = NULL;
        struct in_device *idev = NULL;
 
+       if (imr->imr_ifindex) {
+               idev = inetdev_by_index(imr->imr_ifindex);
+               if (idev)
+                       __in_dev_put(idev);
+               return idev;
+       }
        if (imr->imr_address.s_addr) {
                dev = ip_dev_find(imr->imr_address.s_addr);
                if (!dev)
@@ -1282,13 +1314,16 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
                ip_rt_multicast_event(pmc->interface);
        }
        if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) {
+#ifdef CONFIG_IP_MULTICAST
                struct in_device *in_dev = pmc->interface;
+#endif
 
                /* no more filters for this source */
                if (psf_prev)
                        psf_prev->sf_next = psf->sf_next;
                else
                        pmc->sources = psf->sf_next;
+#ifdef CONFIG_IP_MULTICAST
                if (psf->sf_oldin &&
                    !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
                        psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 
@@ -1297,6 +1332,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
                        pmc->tomb = psf;
                        rv = 1;
                } else
+#endif
                        kfree(psf);
        }
        return rv;
@@ -1327,7 +1363,9 @@ int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
        }
        spin_lock_bh(&pmc->lock);
        read_unlock(&in_dev->lock);
+#ifdef CONFIG_IP_MULTICAST
        sf_markstate(pmc);
+#endif
        if (!delta) {
                if (!pmc->sfcount[sfmode])
                        return -EINVAL;
@@ -1344,10 +1382,13 @@ int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
        if (pmc->sfmode == MCAST_EXCLUDE &&
            pmc->sfcount[MCAST_EXCLUDE] == 0 &&
            pmc->sfcount[MCAST_INCLUDE]) {
+#ifdef CONFIG_IP_MULTICAST
                struct ip_sf_list *psf;
+#endif
 
                /* filter mode change */
                pmc->sfmode = MCAST_INCLUDE;
+#ifdef CONFIG_IP_MULTICAST
                pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 
                        IGMP_Unsolicited_Report_Count;
                in_dev->mr_ifc_count = pmc->crcount;
@@ -1356,6 +1397,7 @@ int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
                igmp_ifc_event(pmc->interface);
        } else if (sf_setstate(pmc) || changerec) {
                igmp_ifc_event(pmc->interface);
+#endif
        }
        spin_unlock_bh(&pmc->lock);
        return err;
@@ -1393,6 +1435,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
        return 0;
 }
 
+#ifdef CONFIG_IP_MULTICAST
 static void sf_markstate(struct ip_mc_list *pmc)
 {
        struct ip_sf_list *psf;
@@ -1428,6 +1471,7 @@ static int sf_setstate(struct ip_mc_list *pmc)
        }
        return rv;
 }
+#endif
 
 /*
  * Add multicast source filter list to the interface list
@@ -1454,7 +1498,9 @@ int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
        spin_lock_bh(&pmc->lock);
        read_unlock(&in_dev->lock);
 
+#ifdef CONFIG_IP_MULTICAST
        sf_markstate(pmc);
+#endif
        isexclude = pmc->sfmode == MCAST_EXCLUDE;
        if (!delta)
                pmc->sfcount[sfmode]++;
@@ -1471,14 +1517,17 @@ int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
                for (j=0; j<i; j++)
                        (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[i]);
        } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
+#ifdef CONFIG_IP_MULTICAST
                struct in_device *in_dev = pmc->interface;
                struct ip_sf_list *psf;
+#endif
 
                /* filter mode change */
                if (pmc->sfcount[MCAST_EXCLUDE])
                        pmc->sfmode = MCAST_EXCLUDE;
                else if (pmc->sfcount[MCAST_INCLUDE])
                        pmc->sfmode = MCAST_INCLUDE;
+#ifdef CONFIG_IP_MULTICAST
                /* else no filters; keep old mode for reports */
 
                pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 
@@ -1487,8 +1536,10 @@ int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
                for (psf=pmc->sources; psf; psf = psf->sf_next)
                        psf->sf_crcount = 0;
                igmp_ifc_event(in_dev);
-       } else if (sf_setstate(pmc))
+       } else if (sf_setstate(pmc)) {
                igmp_ifc_event(in_dev);
+#endif
+       }
        spin_unlock_bh(&pmc->lock);
        return err;
 }
@@ -1530,13 +1581,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 
        rtnl_shlock();
 
-       if (!imr->imr_ifindex)
-               in_dev = ip_mc_find_dev(imr);
-       else {
-               in_dev = inetdev_by_index(imr->imr_ifindex);
-               if (in_dev)
-                       __in_dev_put(in_dev);
-       }
+       in_dev = ip_mc_find_dev(imr);
 
        if (!in_dev) {
                iml = NULL;
@@ -1638,13 +1683,13 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
 }
 
 int ip_mc_source(int add, int omode, struct sock *sk, struct
-       ip_mreq_source *mreqs)
+       ip_mreq_source *mreqs, int ifindex)
 {
        int err;
-       struct ip_mreqn imr;
+       struct ip_mreqn imr;
        u32 addr = mreqs->imr_multiaddr;
        struct ip_mc_socklist *pmc;
-       struct in_device *in_dev;
+       struct in_device *in_dev = 0;
        struct inet_opt *inet = inet_sk(sk);
        struct ip_sf_socklist *psl;
        int i, j, rv;
@@ -1656,7 +1701,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
 
        imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
        imr.imr_address.s_addr = mreqs->imr_interface;
-       imr.imr_ifindex = 0;
+       imr.imr_ifindex = ifindex;
        in_dev = ip_mc_find_dev(&imr);
 
        if (!in_dev) {
@@ -1753,7 +1798,7 @@ done:
        return err;
 }
 
-int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf)
+int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
 {
        int err;
        struct ip_mreqn imr;
@@ -1773,7 +1818,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf)
 
        imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
        imr.imr_address.s_addr = msf->imsf_interface;
-       imr.imr_ifindex = 0;
+       imr.imr_ifindex = ifindex;
        in_dev = ip_mc_find_dev(&imr);
 
        if (!in_dev) {
@@ -1783,7 +1828,8 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf)
        err = -EADDRNOTAVAIL;
 
        for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
-               if (memcmp(&pmc->multi, &imr, sizeof(imr)) == 0)
+               if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr &&
+                   pmc->multi.imr_ifindex == imr.imr_ifindex)
                        break;
        }
        if (!pmc)               /* must have a prior join */
@@ -1834,9 +1880,6 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
 
        if (!MULTICAST(addr))
                return -EINVAL;
-       if (msf->imsf_fmode != MCAST_INCLUDE &&
-           msf->imsf_fmode != MCAST_EXCLUDE)
-               return -EINVAL;
 
        rtnl_shlock();
 
@@ -1852,7 +1895,8 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
        err = -EADDRNOTAVAIL;
 
        for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
-               if (memcmp(&pmc->multi, &imr, sizeof(imr)) == 0)
+               if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr &&
+                   pmc->multi.imr_ifindex == imr.imr_ifindex)
                        break;
        }
        if (!pmc)               /* must have a prior join */
@@ -1882,6 +1926,61 @@ done:
        return err;
 }
 
+int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
+       struct group_filter *optval, int *optlen)
+{
+       int err, i, count, copycount;
+       struct sockaddr_in *psin;
+       u32 addr;
+       struct ip_mc_socklist *pmc;
+       struct inet_opt *inet = inet_sk(sk);
+       struct ip_sf_socklist *psl;
+
+       psin = (struct sockaddr_in *)&gsf->gf_group;
+       if (psin->sin_family != AF_INET)
+               return -EINVAL;
+       addr = psin->sin_addr.s_addr;
+       if (!MULTICAST(addr))
+               return -EINVAL;
+
+       rtnl_shlock();
+
+       err = -EADDRNOTAVAIL;
+
+       for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+               if (pmc->multi.imr_multiaddr.s_addr == addr &&
+                   pmc->multi.imr_ifindex == gsf->gf_interface)
+                       break;
+       }
+       if (!pmc)               /* must have a prior join */
+               goto done;
+       gsf->gf_fmode = pmc->sfmode;
+       psl = pmc->sflist;
+       rtnl_shunlock();
+       count = psl ? psl->sl_count : 0;
+       copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
+       gsf->gf_numsrc = count;
+       if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
+           copy_to_user((void *)optval, gsf, GROUP_FILTER_SIZE(0))) {
+               return -EFAULT;
+       }
+       for (i=0; i<copycount; i++) {
+               struct sockaddr_in *psin;
+               struct sockaddr_storage ss;
+
+               psin = (struct sockaddr_in *)&ss;
+               memset(&ss, 0, sizeof(ss));
+               psin->sin_family = AF_INET;
+               psin->sin_addr.s_addr = psl->sl_addr[i];
+               if (copy_to_user((void *)&optval->gf_slist[i], &ss, sizeof(ss)))
+                       return -EFAULT;
+       }
+       return 0;
+done:
+       rtnl_shunlock();
+       return err;
+}
+
 /*
  * check if a multicast source filter allows delivery for a given <src,dst,intf>
  */
@@ -1972,8 +2071,6 @@ int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
 }
 
 
-#ifdef CONFIG_IP_MULTICAST
- 
 int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length)
 {
        off_t pos=0, begin=0;
@@ -1991,7 +2088,9 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length)
                if (in_dev == NULL)
                        continue;
 
+#ifdef CONFIG_IP_MULTICAST
                querier = IGMP_V1_SEEN(in_dev) ? "V1" : "V2";
+#endif
 
                len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
                             dev->ifindex, dev->name, dev->mc_count, querier);
@@ -2049,11 +2148,8 @@ int ip_mcf_procinfo(char *buffer, char **start, off_t offset, int length)
 
                for (imc=in_dev->mc_list; imc; imc=imc->next) {
                        struct ip_sf_list *psf;
-                       unsigned long icount, xcount;
 
                        spin_lock_bh(&imc->lock);
-                       icount = imc->sfcount[MCAST_INCLUDE];
-                       xcount = imc->sfcount[MCAST_EXCLUDE];
                        for (psf=imc->sources; psf; psf=psf->sf_next) {
                                if (first) {
                                        len += sprintf(buffer+len, "%3s %6s "
@@ -2080,33 +2176,6 @@ int ip_mcf_procinfo(char *buffer, char **start, off_t offset, int length)
                                        in_dev_put(in_dev);
                                        goto done;
                                }
-                               icount -= psf->sf_count[MCAST_INCLUDE];
-                               xcount -= psf->sf_count[MCAST_EXCLUDE];
-                       }
-                       if (icount > 0 || xcount > 0) {
-                               if (first) {
-                                       len += sprintf(buffer+len, "%3s %6s "
-                                               "%10s %10s %6s %6s\n", "Idx",
-                                               "Device", "MCA", "SRC", "INC",
-                                               "EXC");
-                                       first = 0;
-                               }
-                               len += sprintf(buffer+len, "%3d %6.6s 0x%08x "
-                                       "%10s %6lu %6lu\n", dev->ifindex,
-                                       dev->name, ntohl(imc->multiaddr),
-                                       "NONE", icount, xcount);
-                               pos=begin+len;
-                               if(pos<offset)
-                               {
-                                       len=0;
-                                       begin=pos;
-                               }
-                               if(pos>offset+length) {
-                                       spin_unlock_bh(&imc->lock);
-                                       read_unlock(&in_dev->lock);
-                                       in_dev_put(in_dev);
-                                       goto done;
-                               }
                        }
                        spin_unlock_bh(&imc->lock);
                }
@@ -2125,5 +2194,3 @@ done:
        return len;
 }
 
-#endif
-
index 9a4db884dc1fb74945899830fd355dc03a225ede..3f657dcde315f25c21c0e8e3c5290b2505b2c759 100644 (file)
@@ -1312,6 +1312,6 @@ void __init ip_init(void)
 
 #ifdef CONFIG_IP_MULTICAST
        proc_net_create("igmp", 0, ip_mc_procinfo);
-       proc_net_create("mcfilter", 0, ip_mcf_procinfo);
 #endif
+       proc_net_create("mcfilter", 0, ip_mcf_procinfo);
 }
index a8a561411dea598054a631fa91d3078073df92d8..82ab1fc2faa327f14c63aeb005eabff53b346ed1 100644 (file)
@@ -631,7 +631,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                kfree(msf);
                                break;
                        }
-                       err = ip_mc_msfilter(sk, msf);
+                       err = ip_mc_msfilter(sk, msf, 0);
                        kfree(msf);
                        break;
                }
@@ -670,7 +670,142 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                omode = MCAST_INCLUDE;
                                add = 0;
                        }
-                       err = ip_mc_source(add, omode, sk, &mreqs);
+                       err = ip_mc_source(add, omode, sk, &mreqs, 0);
+                       break;
+               }
+               case MCAST_JOIN_GROUP:
+               case MCAST_LEAVE_GROUP: 
+               {
+                       struct group_req greq;
+                       struct sockaddr_in *psin;
+                       struct ip_mreqn mreq;
+
+                       if (optlen < sizeof(struct group_req))
+                               goto e_inval;
+                       err = -EFAULT;
+                       if(copy_from_user(&greq, optval, sizeof(greq)))
+                               break;
+                       psin = (struct sockaddr_in *)&greq.gr_group;
+                       if (psin->sin_family != AF_INET)
+                               goto e_inval;
+                       memset(&mreq, 0, sizeof(mreq));
+                       mreq.imr_multiaddr = psin->sin_addr;
+                       mreq.imr_ifindex = greq.gr_interface;
+
+                       if (optname == MCAST_JOIN_GROUP)
+                               err = ip_mc_join_group(sk, &mreq);
+                       else
+                               err = ip_mc_leave_group(sk, &mreq);
+                       break;
+               }
+               case MCAST_JOIN_SOURCE_GROUP:
+               case MCAST_LEAVE_SOURCE_GROUP:
+               case MCAST_BLOCK_SOURCE:
+               case MCAST_UNBLOCK_SOURCE:
+               {
+                       struct group_source_req greqs;
+                       struct ip_mreq_source mreqs;
+                       struct sockaddr_in *psin;
+                       int omode, add;
+
+                       if (optlen != sizeof(struct group_source_req))
+                               goto e_inval;
+                       if (copy_from_user(&greqs, optval, sizeof(greqs))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       if (greqs.gsr_group.ss_family != AF_INET ||
+                           greqs.gsr_source.ss_family != AF_INET) {
+                               err = -EADDRNOTAVAIL;
+                               break;
+                       }
+                       psin = (struct sockaddr_in *)&greqs.gsr_group;
+                       mreqs.imr_multiaddr = psin->sin_addr.s_addr;
+                       psin = (struct sockaddr_in *)&greqs.gsr_source;
+                       mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
+                       mreqs.imr_interface = 0; /* use index for mc_source */
+
+                       if (optname == MCAST_BLOCK_SOURCE) {
+                               omode = MCAST_EXCLUDE;
+                               add = 1;
+                       } else if (optname == MCAST_UNBLOCK_SOURCE) {
+                               omode = MCAST_EXCLUDE;
+                               add = 0;
+                       } else if (optname == MCAST_JOIN_SOURCE_GROUP) {
+                               struct ip_mreqn mreq;
+
+                               psin = (struct sockaddr_in *)&greqs.gsr_group;
+                               mreq.imr_multiaddr = psin->sin_addr;
+                               mreq.imr_address.s_addr = 0;
+                               mreq.imr_ifindex = greqs.gsr_interface;
+                               err = ip_mc_join_group(sk, &mreq);
+                               if (err)
+                                       break;
+                               omode = MCAST_INCLUDE;
+                               add = 1;
+                       } else /* MCAST_LEAVE_SOURCE_GROUP */ {
+                               omode = MCAST_INCLUDE;
+                               add = 0;
+                       }
+                       err = ip_mc_source(add, omode, sk, &mreqs,
+                               greqs.gsr_interface);
+                       break;
+               }
+               case MCAST_MSFILTER:
+               {
+                       struct sockaddr_in *psin;
+                       struct ip_msfilter *msf = 0;
+                       struct group_filter *gsf = 0;
+                       int msize, i, ifindex;
+
+                       if (optlen < GROUP_FILTER_SIZE(0))
+                               goto e_inval;
+                       gsf = (struct group_filter *)kmalloc(optlen,GFP_KERNEL);
+                       if (gsf == 0) {
+                               err = -ENOBUFS;
+                               break;
+                       }
+                       err = -EFAULT;
+                       if (copy_from_user(gsf, optval, optlen)) {
+                               goto mc_msf_out;
+                       }
+                       if (GROUP_FILTER_SIZE(gsf->gf_numsrc) < optlen) {
+                               err = EINVAL;
+                               goto mc_msf_out;
+                       }
+                       msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
+                       msf = (struct ip_msfilter *)kmalloc(msize,GFP_KERNEL);
+                       if (msf == 0) {
+                               err = -ENOBUFS;
+                               goto mc_msf_out;
+                       }
+                       ifindex = gsf->gf_interface;
+                       psin = (struct sockaddr_in *)&gsf->gf_group;
+                       if (psin->sin_family != AF_INET) {
+                               err = -EADDRNOTAVAIL;
+                               goto mc_msf_out;
+                       }
+                       msf->imsf_multiaddr = psin->sin_addr.s_addr;
+                       msf->imsf_interface = 0;
+                       msf->imsf_fmode = gsf->gf_fmode;
+                       msf->imsf_numsrc = gsf->gf_numsrc;
+                       err = -EADDRNOTAVAIL;
+                       for (i=0; i<gsf->gf_numsrc; ++i) {
+                               psin = (struct sockaddr_in *)&gsf->gf_slist[i];
+
+                               if (psin->sin_family != AF_INET)
+                                       goto mc_msf_out;
+                               msf->imsf_slist[i] = psin->sin_addr.s_addr;
+                       }
+                       kfree(gsf);
+                       gsf = 0;
+
+                       err = ip_mc_msfilter(sk, msf, ifindex);
+mc_msf_out:
+                       if (msf)
+                               kfree(msf);
+                       if (gsf)
+                               kfree(gsf);
                        break;
                }
                case IP_ROUTER_ALERT:   
@@ -826,15 +961,37 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
                        struct ip_msfilter msf;
                        int err;
 
-                       if (len < IP_MSFILTER_SIZE(0))
+                       if (len < IP_MSFILTER_SIZE(0)) {
+                               release_sock(sk);
                                return -EINVAL;
-                       if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0)))
+                       }
+                       if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) {
+                               release_sock(sk);
                                return -EFAULT;
+                       }
                        err = ip_mc_msfget(sk, &msf,
                                (struct ip_msfilter *)optval, optlen);
                        release_sock(sk);
                        return err;
                }
+               case MCAST_MSFILTER:
+               {
+                       struct group_filter gsf;
+                       int err;
+
+                       if (len < GROUP_FILTER_SIZE(0)) {
+                               release_sock(sk);
+                               return -EINVAL;
+                       }
+                       if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) {
+                               release_sock(sk);
+                               return -EFAULT;
+                       }
+                       err = ip_mc_gsfget(sk, &gsf,
+                               (struct group_filter *)optval, optlen);
+                       release_sock(sk);
+                       return err;
+               }
                case IP_PKTOPTIONS:             
                {
                        struct msghdr msg;
index 4ab428df1864054f45bca7ffcb11782d1ede120b..681416c6045bccd5926128a1faf8a9761e17fda5 100644 (file)
@@ -120,6 +120,12 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
        return 0;
 }
 
+extern int ip6_mc_source(int add, int omode, struct sock *sk,
+       struct group_source_req *pgsr);
+extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
+extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
+       struct group_filter *optval, int *optlen);
+
 
 int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, 
                    int optlen)
@@ -393,12 +399,13 @@ done:
                        break;
                }
                psin6 = (struct sockaddr_in6 *)&greq.gr_group;
-               if (optname == IPV6_ADD_MEMBERSHIP)
+               if (optname == MCAST_JOIN_GROUP)
                        retv = ipv6_sock_mc_join(sk, greq.gr_interface,
                                &psin6->sin6_addr);
                else
                        retv = ipv6_sock_mc_drop(sk, greq.gr_interface,
                                &psin6->sin6_addr);
+               break;
        }
        case MCAST_JOIN_SOURCE_GROUP:
        case MCAST_LEAVE_SOURCE_GROUP:
@@ -414,7 +421,8 @@ done:
                        retv = -EFAULT;
                        break;
                }
-               if (greqs.gsr_group.ss_family != AF_INET6) {
+               if (greqs.gsr_group.ss_family != AF_INET6 ||
+                   greqs.gsr_source.ss_family != AF_INET6) {
                        retv = -EADDRNOTAVAIL;
                        break;
                }
index 176963371c72739a1bfc887cb9183ad8dfe5f512..57877738d350dfe2d5c0ec4e727ca17f7b2e4b8b 100644 (file)
@@ -768,7 +768,7 @@ static void mld_clear_delrec(struct inet6_dev *idev)
                psf = pmc->mca_tomb;
                pmc->mca_tomb = 0;
                spin_unlock_bh(&pmc->mca_lock);
-               for (psf=pmc->mca_tomb; psf; psf=psf_next) {
+               for (; psf; psf=psf_next) {
                        psf_next = psf->sf_next;
                        kfree(psf);
                }
@@ -1042,6 +1042,8 @@ int igmp6_event_query(struct sk_buff *skb)
                mld_clear_delrec(idev);
        } else if (len >= 28) {
                max_delay = MLDV2_MRC(ntohs(mlh2->mrc))*(HZ/10);
+               if (!max_delay)
+                       max_delay = 1;
                idev->mc_maxdelay = max_delay;
                if (mlh2->qrv)
                        idev->mc_qrv = mlh2->qrv;
@@ -2096,8 +2098,6 @@ static int ip6_mcf_read_proc(char *buffer, char **start, off_t offset,
                        unsigned long icount, xcount, i;
 
                        spin_lock_bh(&imc->mca_lock);
-                       icount = imc->mca_sfcount[MCAST_INCLUDE];
-                       xcount = imc->mca_sfcount[MCAST_EXCLUDE];
                        for (psf=imc->mca_sources; psf; psf=psf->sf_next) {
                                if (first) {
                                        len += sprintf(buffer+len, "%3s %6s "
@@ -2130,36 +2130,6 @@ static int ip6_mcf_read_proc(char *buffer, char **start, off_t offset,
                                        in6_dev_put(idev);
                                        goto done;
                                }
-                               icount -= psf->sf_count[MCAST_INCLUDE];
-                               xcount -= psf->sf_count[MCAST_EXCLUDE];
-                       }
-                       if (icount > 0 || xcount > 0) {
-                               if (first) {
-                                       len += sprintf(buffer+len, "%3s %6s "
-                                               "%32s %32s %6s %6s\n", "Idx",
-                                               "Device", "Multicast Address",
-                                               "Source Address", "INC", "EXC");
-                                       first = 0;
-                               }
-                               len += sprintf(buffer+len,"%3d %6.6s ",
-                                       dev->ifindex, dev->name);
-
-                               for (i=0; i<16; i++)
-                                       len += sprintf(buffer+len, "%02x",
-                                               imc->mca_addr.s6_addr[i]);
-                               len += sprintf(buffer+len, " %32s %6lu %6lu\n",
-                                       "NONE", icount, xcount);
-                               pos = begin+len;
-                               if (pos < offset) {
-                                       len=0;
-                                       begin=pos;
-                               }
-                               if (pos > offset+length) {
-                                       spin_unlock_bh(&imc->mca_lock);
-                                       read_unlock_bh(&idev->lock);
-                                       in6_dev_put(idev);
-                                       goto done;
-                               }
                        }
                        spin_unlock_bh(&imc->mca_lock);
                }