* Vinay Kulkarni
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/times.h>
+
+#include <net/arp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
time_before(jiffies, (in_dev)->mr_v2_seen)))
static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
-static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr);
+static void igmpv3_del_delrec(struct in_device *in_dev, __be32 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);
+static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
+ int sfcount, __be32 *psfsrc, int delta);
static void ip_ma_put(struct ip_mc_list *im)
{
case IGMPV3_MODE_IS_EXCLUDE:
if (gdeleted || sdeleted)
return 0;
- return !(pmc->gsquery && !psf->sf_gsresp);
+ if (!(pmc->gsquery && !psf->sf_gsresp)) {
+ if (pmc->sfmode == MCAST_INCLUDE)
+ return 1;
+ /* don't include if this source is excluded
+ * in all filters
+ */
+ if (psf->sf_count[MCAST_INCLUDE])
+ return type == IGMPV3_MODE_IS_INCLUDE;
+ return pmc->sfcount[MCAST_EXCLUDE] ==
+ psf->sf_count[MCAST_EXCLUDE];
+ }
+ return 0;
case IGMPV3_CHANGE_TO_INCLUDE:
if (gdeleted || sdeleted)
return 0;
skb = alloc_skb(size + LL_RESERVED_SPACE(dev), GFP_ATOMIC);
if (skb == NULL)
- return 0;
+ return NULL;
{
struct flowi fl = { .oif = dev->ifindex,
.proto = IPPROTO_IGMP };
if (ip_route_output_key(&rt, &fl)) {
kfree_skb(skb);
- return 0;
+ return NULL;
}
}
if (rt->rt_src == 0) {
kfree_skb(skb);
ip_rt_put(rt);
- return 0;
+ return NULL;
}
skb->dst = &rt->u.dst;
if (!skb)
skb = igmpv3_newpack(dev, dev->mtu);
if (!skb)
- return 0;
+ return NULL;
pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
pgr->grec_type = type;
pgr->grec_auxwords = 0;
{
struct net_device *dev = pmc->interface->dev;
struct igmpv3_report *pih;
- struct igmpv3_grec *pgr = 0;
+ struct igmpv3_grec *pgr = NULL;
struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
- int scount, first, isquery, truncate;
+ int scount, stotal, first, isquery, truncate;
if (pmc->multiaddr == IGMP_ALL_HOSTS)
return skb;
truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
type == IGMPV3_CHANGE_TO_EXCLUDE;
+ stotal = scount = 0;
+
psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
- if (!*psf_list) {
- if (type == IGMPV3_ALLOW_NEW_SOURCES ||
- type == IGMPV3_BLOCK_OLD_SOURCES)
- return skb;
- if (pmc->crcount || isquery) {
- /* make sure we have room for group header and at
- * least one source.
- */
- if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)+
- sizeof(__u32)) {
- igmpv3_sendpack(skb);
- skb = 0; /* add_grhead will get a new one */
- }
- skb = add_grhead(skb, pmc, type, &pgr);
- }
- return skb;
- }
- pih = skb ? (struct igmpv3_report *)skb->h.igmph : 0;
+ if (!*psf_list)
+ goto empty_source;
+
+ pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL;
/* EX and TO_EX get a fresh packet, if needed */
if (truncate) {
}
}
first = 1;
- scount = 0;
- psf_prev = 0;
+ psf_prev = NULL;
for (psf=*psf_list; psf; psf=psf_next) {
- u32 *psrc;
+ __be32 *psrc;
psf_next = psf->sf_next;
if (isquery)
psf->sf_gsresp = 0;
- if (AVAILABLE(skb) < sizeof(u32) +
+ if (AVAILABLE(skb) < sizeof(__be32) +
first*sizeof(struct igmpv3_grec)) {
if (truncate && !first)
break; /* truncate these */
skb = add_grhead(skb, pmc, type, &pgr);
first = 0;
}
- psrc = (u32 *)skb_put(skb, sizeof(u32));
+ if (!skb)
+ return NULL;
+ psrc = (__be32 *)skb_put(skb, sizeof(__be32));
*psrc = psf->sf_inaddr;
- scount++;
+ scount++; stotal++;
if ((type == IGMPV3_ALLOW_NEW_SOURCES ||
type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
psf->sf_crcount--;
}
psf_prev = psf;
}
+
+empty_source:
+ if (!stotal) {
+ if (type == IGMPV3_ALLOW_NEW_SOURCES ||
+ type == IGMPV3_BLOCK_OLD_SOURCES)
+ return skb;
+ if (pmc->crcount || isquery) {
+ /* make sure we have room for group header */
+ if (skb && AVAILABLE(skb)<sizeof(struct igmpv3_grec)) {
+ igmpv3_sendpack(skb);
+ skb = NULL; /* add_grhead will get a new one */
+ }
+ skb = add_grhead(skb, pmc, type, &pgr);
+ }
+ }
if (pgr)
pgr->grec_nsrcs = htons(scount);
static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
{
- struct sk_buff *skb = 0;
+ struct sk_buff *skb = NULL;
int type;
if (!pmc) {
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
if (pmc->multiaddr == IGMP_ALL_HOSTS)
continue;
skb = add_grec(skb, pmc, type, 0, 0);
spin_unlock_bh(&pmc->lock);
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
} else {
spin_lock_bh(&pmc->lock);
if (pmc->sfcount[MCAST_EXCLUDE])
{
struct ip_sf_list *psf_prev, *psf_next, *psf;
- psf_prev = 0;
+ psf_prev = NULL;
for (psf=*ppsf; psf; psf = psf_next) {
psf_next = psf->sf_next;
if (psf->sf_crcount == 0) {
static void igmpv3_send_cr(struct in_device *in_dev)
{
struct ip_mc_list *pmc, *pmc_prev, *pmc_next;
- struct sk_buff *skb = 0;
+ struct sk_buff *skb = NULL;
int type, dtype;
- read_lock(&in_dev->lock);
- write_lock_bh(&in_dev->mc_lock);
+ read_lock(&in_dev->mc_list_lock);
+ spin_lock_bh(&in_dev->mc_tomb_lock);
/* deleted MCA's */
- pmc_prev = 0;
+ pmc_prev = NULL;
for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) {
pmc_next = pmc->next;
if (pmc->sfmode == MCAST_INCLUDE) {
skb = add_grec(skb, pmc, dtype, 1, 1);
}
if (pmc->crcount) {
- pmc->crcount--;
if (pmc->sfmode == MCAST_EXCLUDE) {
type = IGMPV3_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 1, 0);
}
+ pmc->crcount--;
if (pmc->crcount == 0) {
igmpv3_clear_zeros(&pmc->tomb);
igmpv3_clear_zeros(&pmc->sources);
} else
pmc_prev = pmc;
}
- write_unlock_bh(&in_dev->mc_lock);
+ spin_unlock_bh(&in_dev->mc_tomb_lock);
/* change recs */
for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
/* filter mode changes */
if (pmc->crcount) {
- pmc->crcount--;
if (pmc->sfmode == MCAST_EXCLUDE)
type = IGMPV3_CHANGE_TO_EXCLUDE;
else
type = IGMPV3_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 0, 0);
+ pmc->crcount--;
}
spin_unlock_bh(&pmc->lock);
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
+
if (!skb)
return;
(void) igmpv3_sendpack(skb);
struct igmphdr *ih;
struct rtable *rt;
struct net_device *dev = in_dev->dev;
- u32 group = pmc ? pmc->multiaddr : 0;
- u32 dst;
+ __be32 group = pmc ? pmc->multiaddr : 0;
+ __be32 dst;
if (type == IGMPV3_HOST_MEMBERSHIP_REPORT)
return igmpv3_send_report(in_dev, pmc);
struct in_device *in_dev = (struct in_device *)data;
in_dev->mr_gq_running = 0;
- igmpv3_send_report(in_dev, 0);
+ igmpv3_send_report(in_dev, NULL);
__in_dev_put(in_dev);
}
ip_ma_put(im);
}
-static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
+/* mark EXCLUDE-mode sources */
+static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
{
struct ip_sf_list *psf;
int i, scount;
+ scount = 0;
+ for (psf=pmc->sources; psf; psf=psf->sf_next) {
+ if (scount == nsrcs)
+ break;
+ for (i=0; i<nsrcs; i++) {
+ /* skip inactive filters */
+ if (pmc->sfcount[MCAST_INCLUDE] ||
+ pmc->sfcount[MCAST_EXCLUDE] !=
+ psf->sf_count[MCAST_EXCLUDE])
+ continue;
+ if (srcs[i] == psf->sf_inaddr) {
+ scount++;
+ break;
+ }
+ }
+ }
+ pmc->gsquery = 0;
+ if (scount == nsrcs) /* all sources excluded */
+ return 0;
+ return 1;
+}
+
+static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
+{
+ struct ip_sf_list *psf;
+ int i, scount;
+
+ if (pmc->sfmode == MCAST_EXCLUDE)
+ return igmp_xmarksources(pmc, nsrcs, srcs);
+
+ /* mark INCLUDE-mode sources */
scount = 0;
for (psf=pmc->sources; psf; psf=psf->sf_next) {
if (scount == nsrcs)
break;
}
}
+ if (!scount) {
+ pmc->gsquery = 0;
+ return 0;
+ }
+ pmc->gsquery = 1;
+ return 1;
}
-static void igmp_heard_report(struct in_device *in_dev, u32 group)
+static void igmp_heard_report(struct in_device *in_dev, __be32 group)
{
struct ip_mc_list *im;
if (group == IGMP_ALL_HOSTS)
return;
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
for (im=in_dev->mc_list; im!=NULL; im=im->next) {
if (im->multiaddr == group) {
igmp_stop_timer(im);
break;
}
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
}
static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
struct igmphdr *ih = skb->h.igmph;
struct igmpv3_query *ih3 = (struct igmpv3_query *)ih;
struct ip_mc_list *im;
- u32 group = ih->group;
+ __be32 group = ih->group;
int max_delay;
int mark = 0;
ih3 = (struct igmpv3_query *) skb->h.raw;
if (ih3->nsrcs) {
if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
- + ntohs(ih3->nsrcs)*sizeof(__u32)))
+ + ntohs(ih3->nsrcs)*sizeof(__be32)))
return;
ih3 = (struct igmpv3_query *) skb->h.raw;
}
* - Use the igmp->igmp_code field as the maximum
* delay possible
*/
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
for (im=in_dev->mc_list; im!=NULL; im=im->next) {
+ int changed;
+
if (group && group != im->multiaddr)
continue;
if (im->multiaddr == IGMP_ALL_HOSTS)
im->gsquery = im->gsquery && mark;
else
im->gsquery = mark;
- if (im->gsquery)
- igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
+ changed = !im->gsquery ||
+ igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
spin_unlock_bh(&im->lock);
- igmp_mod_timer(im, max_delay);
+ if (changed)
+ igmp_mod_timer(im, max_delay);
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
}
int igmp_rcv(struct sk_buff *skb)
return 0;
}
- if (!pskb_may_pull(skb, sizeof(struct igmphdr)) ||
- (u16)csum_fold(skb_checksum(skb, 0, len, 0))) {
- in_dev_put(in_dev);
- kfree_skb(skb);
- return 0;
+ if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
+ goto drop;
+
+ switch (skb->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ if (!csum_fold(skb->csum))
+ break;
+ /* fall through */
+ case CHECKSUM_NONE:
+ skb->csum = 0;
+ if (__skb_checksum_complete(skb))
+ goto drop;
}
ih = skb->h.igmph;
/* Is it our report looped back? */
if (((struct rtable*)skb->dst)->fl.iif == 0)
break;
- igmp_heard_report(in_dev, ih->group);
+ /* don't rely on MC router hearing unicast reports */
+ if (skb->pkt_type == PACKET_MULTICAST ||
+ skb->pkt_type == PACKET_BROADCAST)
+ igmp_heard_report(in_dev, ih->group);
break;
case IGMP_PIM:
#ifdef CONFIG_IP_PIMSM_V1
case IGMP_MTRACE_RESP:
break;
default:
- NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type));
+ break;
}
+
+drop:
in_dev_put(in_dev);
kfree_skb(skb);
return 0;
* Add a filter to a device
*/
-static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
+static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr)
{
char buf[MAX_ADDR_LEN];
struct net_device *dev = in_dev->dev;
* Remove a filter from a device
*/
-static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
+static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
{
char buf[MAX_ADDR_LEN];
struct net_device *dev = in_dev->dev;
* for deleted items allows change reports to use common code with
* non-deleted or query-response MCA's.
*/
- pmc = (struct ip_mc_list *)kmalloc(sizeof(*pmc), GFP_KERNEL);
+ pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
if (!pmc)
return;
- memset(pmc, 0, sizeof(*pmc));
spin_lock_bh(&im->lock);
pmc->interface = im->interface;
in_dev_hold(in_dev);
pmc->tomb = im->tomb;
pmc->sources = im->sources;
- im->tomb = im->sources = 0;
+ im->tomb = im->sources = NULL;
for (psf=pmc->sources; psf; psf=psf->sf_next)
psf->sf_crcount = pmc->crcount;
}
spin_unlock_bh(&im->lock);
- write_lock_bh(&in_dev->mc_lock);
+ spin_lock_bh(&in_dev->mc_tomb_lock);
pmc->next = in_dev->mc_tomb;
in_dev->mc_tomb = pmc;
- write_unlock_bh(&in_dev->mc_lock);
+ spin_unlock_bh(&in_dev->mc_tomb_lock);
}
-static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr)
+static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr)
{
struct ip_mc_list *pmc, *pmc_prev;
struct ip_sf_list *psf, *psf_next;
- write_lock_bh(&in_dev->mc_lock);
- pmc_prev = 0;
+ spin_lock_bh(&in_dev->mc_tomb_lock);
+ pmc_prev = NULL;
for (pmc=in_dev->mc_tomb; pmc; pmc=pmc->next) {
if (pmc->multiaddr == multiaddr)
break;
else
in_dev->mc_tomb = pmc->next;
}
- write_unlock_bh(&in_dev->mc_lock);
+ spin_unlock_bh(&in_dev->mc_tomb_lock);
if (pmc) {
for (psf=pmc->tomb; psf; psf=psf_next) {
psf_next = psf->sf_next;
{
struct ip_mc_list *pmc, *nextpmc;
- write_lock_bh(&in_dev->mc_lock);
+ spin_lock_bh(&in_dev->mc_tomb_lock);
pmc = in_dev->mc_tomb;
- in_dev->mc_tomb = 0;
- write_unlock_bh(&in_dev->mc_lock);
+ in_dev->mc_tomb = NULL;
+ spin_unlock_bh(&in_dev->mc_tomb_lock);
for (; pmc; pmc = nextpmc) {
nextpmc = pmc->next;
kfree(pmc);
}
/* clear dead sources, too */
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_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;
+ pmc->tomb = NULL;
spin_unlock_bh(&pmc->lock);
for (; psf; psf=psf_next) {
psf_next = psf->sf_next;
kfree(psf);
}
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
}
#endif
* A socket has joined a multicast group on device dev.
*/
-void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
+void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
{
struct ip_mc_list *im;
for (im=in_dev->mc_list; im; im=im->next) {
if (im->multiaddr == addr) {
im->users++;
- ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, 0, 0);
+ ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0);
goto out;
}
}
- im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
+ im = kmalloc(sizeof(*im), GFP_KERNEL);
if (!im)
goto out;
im->sfmode = MCAST_EXCLUDE;
im->sfcount[MCAST_INCLUDE] = 0;
im->sfcount[MCAST_EXCLUDE] = 1;
- im->sources = 0;
- im->tomb = 0;
+ im->sources = NULL;
+ im->tomb = NULL;
im->crcount = 0;
atomic_set(&im->refcnt, 1);
spin_lock_init(&im->lock);
im->gsquery = 0;
#endif
im->loaded = 0;
- write_lock_bh(&in_dev->lock);
+ write_lock_bh(&in_dev->mc_list_lock);
im->next=in_dev->mc_list;
in_dev->mc_list=im;
- write_unlock_bh(&in_dev->lock);
+ write_unlock_bh(&in_dev->mc_list_lock);
#ifdef CONFIG_IP_MULTICAST
igmpv3_del_delrec(in_dev, im->multiaddr);
#endif
* A socket has left a multicast group on device dev
*/
-void ip_mc_dec_group(struct in_device *in_dev, u32 addr)
+void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
{
struct ip_mc_list *i, **ip;
for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
if (i->multiaddr==addr) {
if (--i->users == 0) {
- write_lock_bh(&in_dev->lock);
+ write_lock_bh(&in_dev->mc_list_lock);
*ip = i->next;
- write_unlock_bh(&in_dev->lock);
+ write_unlock_bh(&in_dev->mc_list_lock);
igmp_group_dropped(i);
if (!in_dev->dead)
{
ASSERT_RTNL();
- in_dev->mc_tomb = 0;
+ in_dev->mc_tomb = NULL;
#ifdef CONFIG_IP_MULTICAST
in_dev->mr_gq_running = 0;
init_timer(&in_dev->mr_gq_timer);
in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;
#endif
- in_dev->mc_lock = RW_LOCK_UNLOCKED;
+ rwlock_init(&in_dev->mc_list_lock);
+ spin_lock_init(&in_dev->mc_tomb_lock);
}
/* Device going up */
/* Deactivate timers */
ip_mc_down(in_dev);
- write_lock_bh(&in_dev->lock);
+ write_lock_bh(&in_dev->mc_list_lock);
while ((i = in_dev->mc_list) != NULL) {
in_dev->mc_list = i->next;
- write_unlock_bh(&in_dev->lock);
+ write_unlock_bh(&in_dev->mc_list_lock);
igmp_group_dropped(i);
ip_ma_put(i);
- write_lock_bh(&in_dev->lock);
+ write_lock_bh(&in_dev->mc_list_lock);
}
- write_unlock_bh(&in_dev->lock);
+ write_unlock_bh(&in_dev->mc_list_lock);
}
static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
dev = ip_dev_find(imr->imr_address.s_addr);
if (!dev)
return NULL;
- __dev_put(dev);
+ dev_put(dev);
}
if (!dev && !ip_route_output_key(&rt, &fl)) {
}
if (dev) {
imr->imr_ifindex = dev->ifindex;
- idev = __in_dev_get(dev);
+ idev = __in_dev_get_rtnl(dev);
}
return idev;
}
/*
* Join a socket to a group
*/
-int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
-int sysctl_igmp_max_msf = IP_MAX_MSF;
+int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS;
+int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF;
static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
- __u32 *psfsrc)
+ __be32 *psfsrc)
{
struct ip_sf_list *psf, *psf_prev;
int rv = 0;
- psf_prev = 0;
+ psf_prev = NULL;
for (psf=pmc->sources; psf; psf=psf->sf_next) {
if (psf->sf_inaddr == *psfsrc)
break;
#define igmp_ifc_event(x) do { } while (0)
#endif
-int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
- int sfcount, __u32 *psfsrc, int delta)
+static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
+ int sfcount, __be32 *psfsrc, int delta)
{
struct ip_mc_list *pmc;
int changerec = 0;
if (!in_dev)
return -ENODEV;
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
if (*pmca == pmc->multiaddr)
break;
}
if (!pmc) {
/* MCA not found?? bug */
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
return -ESRCH;
}
spin_lock_bh(&pmc->lock);
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
#ifdef CONFIG_IP_MULTICAST
sf_markstate(pmc);
#endif
* Add multicast single-source filter to the interface list
*/
static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
- __u32 *psfsrc, int delta)
+ __be32 *psfsrc, int delta)
{
struct ip_sf_list *psf, *psf_prev;
- psf_prev = 0;
+ psf_prev = NULL;
for (psf=pmc->sources; psf; psf=psf->sf_next) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
}
if (!psf) {
- psf = (struct ip_sf_list *)kmalloc(sizeof(*psf), GFP_ATOMIC);
+ psf = kzalloc(sizeof(*psf), GFP_ATOMIC);
if (!psf)
return -ENOBUFS;
- memset(psf, 0, sizeof(*psf));
psf->sf_inaddr = *psfsrc;
if (psf_prev) {
psf_prev->sf_next = psf;
static int sf_setstate(struct ip_mc_list *pmc)
{
- struct ip_sf_list *psf;
+ struct ip_sf_list *psf, *dpsf;
int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
int qrv = pmc->interface->mr_qrv;
int new_in, rv;
!psf->sf_count[MCAST_INCLUDE];
} else
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
- if (new_in != psf->sf_oldin) {
- psf->sf_crcount = qrv;
+ if (new_in) {
+ if (!psf->sf_oldin) {
+ struct ip_sf_list *prev = NULL;
+
+ for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next) {
+ if (dpsf->sf_inaddr == psf->sf_inaddr)
+ break;
+ prev = dpsf;
+ }
+ if (dpsf) {
+ if (prev)
+ prev->sf_next = dpsf->sf_next;
+ else
+ pmc->tomb = dpsf->sf_next;
+ kfree(dpsf);
+ }
+ psf->sf_crcount = qrv;
+ rv++;
+ }
+ } else if (psf->sf_oldin) {
+
+ psf->sf_crcount = 0;
+ /*
+ * add or update "delete" records if an active filter
+ * is now inactive
+ */
+ for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next)
+ if (dpsf->sf_inaddr == psf->sf_inaddr)
+ break;
+ if (!dpsf) {
+ dpsf = (struct ip_sf_list *)
+ kmalloc(sizeof(*dpsf), GFP_ATOMIC);
+ if (!dpsf)
+ continue;
+ *dpsf = *psf;
+ /* pmc->lock held by callers */
+ dpsf->sf_next = pmc->tomb;
+ pmc->tomb = dpsf;
+ }
+ dpsf->sf_crcount = qrv;
rv++;
}
}
/*
* Add multicast source filter list to the interface list
*/
-int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
- int sfcount, __u32 *psfsrc, int delta)
+static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
+ int sfcount, __be32 *psfsrc, int delta)
{
struct ip_mc_list *pmc;
int isexclude;
if (!in_dev)
return -ENODEV;
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
if (*pmca == pmc->multiaddr)
break;
}
if (!pmc) {
/* MCA not found?? bug */
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
return -ESRCH;
}
spin_lock_bh(&pmc->lock);
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
#ifdef CONFIG_IP_MULTICAST
sf_markstate(pmc);
nextpsf = psf->sf_next;
kfree(psf);
}
- pmc->tomb = 0;
+ pmc->tomb = NULL;
for (psf=pmc->sources; psf; psf=nextpsf) {
nextpsf = psf->sf_next;
kfree(psf);
}
- pmc->sources = 0;
+ pmc->sources = NULL;
pmc->sfmode = MCAST_EXCLUDE;
- pmc->sfcount[MCAST_EXCLUDE] = 0;
+ pmc->sfcount[MCAST_INCLUDE] = 0;
pmc->sfcount[MCAST_EXCLUDE] = 1;
}
int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
{
int err;
- u32 addr = imr->imr_multiaddr.s_addr;
- struct ip_mc_socklist *iml, *i;
+ __be32 addr = imr->imr_multiaddr.s_addr;
+ struct ip_mc_socklist *iml=NULL, *i;
struct in_device *in_dev;
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+ int ifindex;
int count = 0;
if (!MULTICAST(addr))
return -EINVAL;
- rtnl_shlock();
+ rtnl_lock();
in_dev = ip_mc_find_dev(imr);
goto done;
}
- iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
-
err = -EADDRINUSE;
+ ifindex = imr->imr_ifindex;
for (i = inet->mc_list; i; i = i->next) {
- if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
- /* New style additions are reference counted */
- if (imr->imr_address.s_addr == 0) {
- i->count++;
- err = 0;
- }
+ if (i->multi.imr_multiaddr.s_addr == addr &&
+ i->multi.imr_ifindex == ifindex)
goto done;
- }
count++;
}
err = -ENOBUFS;
- if (iml == NULL || count >= sysctl_igmp_max_memberships)
+ if (count >= sysctl_igmp_max_memberships)
+ goto done;
+ iml = sock_kmalloc(sk,sizeof(*iml),GFP_KERNEL);
+ if (iml == NULL)
goto done;
+
memcpy(&iml->multi, imr, sizeof(*imr));
iml->next = inet->mc_list;
- iml->count = 1;
iml->sflist = NULL;
iml->sfmode = MCAST_EXCLUDE;
inet->mc_list = iml;
ip_mc_inc_group(in_dev, addr);
- iml = NULL;
err = 0;
-
done:
- rtnl_shunlock();
- if (iml)
- sock_kfree_s(sk, iml, sizeof(*iml));
+ rtnl_unlock();
return err;
}
-int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
- struct in_device *in_dev)
+static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
+ struct in_device *in_dev)
{
int err;
if (iml->sflist == 0) {
/* any-source empty exclude case */
return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
- iml->sfmode, 0, 0, 0);
+ iml->sfmode, 0, NULL, 0);
}
err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
iml->sfmode, iml->sflist->sl_count,
iml->sflist->sl_addr, 0);
sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max));
- iml->sflist = 0;
+ iml->sflist = NULL;
return err;
}
int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ip_mc_socklist *iml, **imlp;
+ struct in_device *in_dev;
+ __be32 group = imr->imr_multiaddr.s_addr;
+ u32 ifindex;
+ int ret = -EADDRNOTAVAIL;
rtnl_lock();
+ in_dev = ip_mc_find_dev(imr);
+ ifindex = imr->imr_ifindex;
for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) {
- if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
- iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
- (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
- struct in_device *in_dev;
-
- in_dev = inetdev_by_index(iml->multi.imr_ifindex);
- if (in_dev)
- (void) ip_mc_leave_src(sk, iml, in_dev);
- if (--iml->count) {
- rtnl_unlock();
- if (in_dev)
- in_dev_put(in_dev);
- return 0;
- }
+ if (iml->multi.imr_multiaddr.s_addr != group)
+ continue;
+ if (ifindex) {
+ if (iml->multi.imr_ifindex != ifindex)
+ continue;
+ } else if (imr->imr_address.s_addr && imr->imr_address.s_addr !=
+ iml->multi.imr_address.s_addr)
+ continue;
- *imlp = iml->next;
+ (void) ip_mc_leave_src(sk, iml, in_dev);
- if (in_dev) {
- ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
- in_dev_put(in_dev);
- }
- rtnl_unlock();
- sock_kfree_s(sk, iml, sizeof(*iml));
- return 0;
- }
+ *imlp = iml->next;
+
+ if (in_dev)
+ ip_mc_dec_group(in_dev, group);
+ rtnl_unlock();
+ sock_kfree_s(sk, iml, sizeof(*iml));
+ return 0;
}
+ if (!in_dev)
+ ret = -ENODEV;
rtnl_unlock();
- return -EADDRNOTAVAIL;
+ return ret;
}
int ip_mc_source(int add, int omode, struct sock *sk, struct
{
int err;
struct ip_mreqn imr;
- u32 addr = mreqs->imr_multiaddr;
+ __be32 addr = mreqs->imr_multiaddr;
struct ip_mc_socklist *pmc;
- struct in_device *in_dev = 0;
- struct inet_opt *inet = inet_sk(sk);
+ struct in_device *in_dev = NULL;
+ struct inet_sock *inet = inet_sk(sk);
struct ip_sf_socklist *psl;
+ int leavegroup = 0;
int i, j, rv;
if (!MULTICAST(addr))
return -EINVAL;
- rtnl_shlock();
+ rtnl_lock();
imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
imr.imr_address.s_addr = mreqs->imr_interface;
err = -EADDRNOTAVAIL;
for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
- if (memcmp(&pmc->multi, mreqs, 2*sizeof(__u32)) == 0)
+ if (pmc->multi.imr_multiaddr.s_addr == imr.imr_multiaddr.s_addr
+ && pmc->multi.imr_ifindex == imr.imr_ifindex)
break;
}
- if (!pmc) /* must have a prior join */
+ if (!pmc) { /* must have a prior join */
+ err = -EINVAL;
goto done;
+ }
/* if a source filter was set, must be the same mode as before */
if (pmc->sflist) {
- if (pmc->sfmode != omode)
+ if (pmc->sfmode != omode) {
+ err = -EINVAL;
goto done;
+ }
} else if (pmc->sfmode != omode) {
/* allow mode switches for empty-set filters */
- ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, 0, 0);
+ ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0);
ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0,
- 0, 0);
+ NULL, 0);
pmc->sfmode = omode;
}
psl = pmc->sflist;
if (!add) {
if (!psl)
- goto done;
+ goto done; /* err = -EADDRNOTAVAIL */
rv = !0;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
- sizeof(__u32));
- if (rv >= 0)
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
+ sizeof(__be32));
+ if (rv == 0)
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
+ goto done; /* err = -EADDRNOTAVAIL */
+
+ /* special case - (INCLUDE, empty) == LEAVE_GROUP */
+ if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
+ leavegroup = 1;
goto done;
+ }
/* update the interface filter */
ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
if (psl)
count += psl->sl_max;
- newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk,
- IP_SFLSIZE(count), GFP_KERNEL);
+ newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL);
if (!newpsl) {
err = -ENOBUFS;
goto done;
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
- sizeof(__u32));
- if (rv >= 0)
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
+ sizeof(__be32));
+ if (rv == 0)
break;
}
if (rv == 0) /* address already there is an error */
ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
&mreqs->imr_sourceaddr, 1);
done:
- rtnl_shunlock();
+ rtnl_unlock();
+ if (leavegroup)
+ return ip_mc_leave_group(sk, &imr);
return err;
}
int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
{
- int err;
+ int err = 0;
struct ip_mreqn imr;
- u32 addr = msf->imsf_multiaddr;
+ __be32 addr = msf->imsf_multiaddr;
struct ip_mc_socklist *pmc;
struct in_device *in_dev;
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ip_sf_socklist *newpsl, *psl;
+ int leavegroup = 0;
if (!MULTICAST(addr))
return -EINVAL;
msf->imsf_fmode != MCAST_EXCLUDE)
return -EINVAL;
- rtnl_shlock();
+ rtnl_lock();
imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
imr.imr_address.s_addr = msf->imsf_interface;
err = -ENODEV;
goto done;
}
- err = -EADDRNOTAVAIL;
+
+ /* special case - (INCLUDE, empty) == LEAVE_GROUP */
+ if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) {
+ leavegroup = 1;
+ goto done;
+ }
for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
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 */
+ if (!pmc) { /* must have a prior join */
+ err = -EINVAL;
goto done;
+ }
if (msf->imsf_numsrc) {
- newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk,
- IP_SFLSIZE(msf->imsf_numsrc), GFP_KERNEL);
+ newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc),
+ GFP_KERNEL);
if (!newpsl) {
err = -ENOBUFS;
goto done;
sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max));
goto done;
}
- } else
- newpsl = 0;
+ } else {
+ newpsl = NULL;
+ (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
+ msf->imsf_fmode, 0, NULL, 0);
+ }
psl = pmc->sflist;
if (psl) {
(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
} else
(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
- 0, 0, 0);
+ 0, NULL, 0);
pmc->sflist = newpsl;
pmc->sfmode = msf->imsf_fmode;
+ err = 0;
done:
- rtnl_shunlock();
+ rtnl_unlock();
+ if (leavegroup)
+ err = ip_mc_leave_group(sk, &imr);
return err;
}
{
int err, len, count, copycount;
struct ip_mreqn imr;
- u32 addr = msf->imsf_multiaddr;
+ __be32 addr = msf->imsf_multiaddr;
struct ip_mc_socklist *pmc;
struct in_device *in_dev;
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ip_sf_socklist *psl;
if (!MULTICAST(addr))
return -EINVAL;
- rtnl_shlock();
+ rtnl_lock();
imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
imr.imr_address.s_addr = msf->imsf_interface;
goto done;
msf->imsf_fmode = pmc->sfmode;
psl = pmc->sflist;
- rtnl_shunlock();
+ rtnl_unlock();
if (!psl) {
len = 0;
count = 0;
return -EFAULT;
return 0;
done:
- rtnl_shunlock();
+ rtnl_unlock();
return err;
}
{
int err, i, count, copycount;
struct sockaddr_in *psin;
- u32 addr;
+ __be32 addr;
struct ip_mc_socklist *pmc;
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ip_sf_socklist *psl;
psin = (struct sockaddr_in *)&gsf->gf_group;
if (!MULTICAST(addr))
return -EINVAL;
- rtnl_shlock();
+ rtnl_lock();
err = -EADDRNOTAVAIL;
goto done;
gsf->gf_fmode = pmc->sfmode;
psl = pmc->sflist;
- rtnl_shunlock();
+ rtnl_unlock();
count = psl ? psl->sl_count : 0;
copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
gsf->gf_numsrc = count;
}
return 0;
done:
- rtnl_shunlock();
+ rtnl_unlock();
return err;
}
/*
* check if a multicast source filter allows delivery for a given <src,dst,intf>
*/
-int ip_mc_sf_allow(struct sock *sk, u32 loc_addr, u32 rmt_addr, int dif)
+int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ip_mc_socklist *pmc;
struct ip_sf_socklist *psl;
int i;
void ip_mc_drop_socket(struct sock *sk)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ip_mc_socklist *iml;
if (inet->mc_list == NULL)
struct in_device *in_dev;
inet->mc_list = iml->next;
- if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) {
- (void) ip_mc_leave_src(sk, iml, in_dev);
+ in_dev = inetdev_by_index(iml->multi.imr_ifindex);
+ (void) ip_mc_leave_src(sk, iml, in_dev);
+ if (in_dev != NULL) {
ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
in_dev_put(in_dev);
}
sock_kfree_s(sk, iml, sizeof(*iml));
-
}
rtnl_unlock();
}
-int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
+int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto)
{
struct ip_mc_list *im;
struct ip_sf_list *psf;
int rv = 0;
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
for (im=in_dev->mc_list; im; im=im->next) {
if (im->multiaddr == mc_addr)
break;
} else
rv = 1; /* unspecified source; tentatively allow */
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
return rv;
}
in_dev = in_dev_get(state->dev);
if (!in_dev)
continue;
- read_lock(&in_dev->lock);
+ read_lock(&in_dev->mc_list_lock);
im = in_dev->mc_list;
if (im) {
state->in_dev = in_dev;
break;
}
- read_unlock(&in_dev->lock);
+ read_unlock(&in_dev->mc_list_lock);
in_dev_put(in_dev);
}
return im;
im = im->next;
while (!im) {
if (likely(state->in_dev != NULL)) {
- read_unlock(&state->in_dev->lock);
+ read_unlock(&state->in_dev->mc_list_lock);
in_dev_put(state->in_dev);
}
state->dev = state->dev->next;
state->in_dev = in_dev_get(state->dev);
if (!state->in_dev)
continue;
- read_lock(&state->in_dev->lock);
+ read_lock(&state->in_dev->mc_list_lock);
im = state->in_dev->mc_list;
}
return im;
{
struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
if (likely(state->in_dev != NULL)) {
- read_unlock(&state->in_dev->lock);
+ read_unlock(&state->in_dev->mc_list_lock);
in_dev_put(state->in_dev);
state->in_dev = NULL;
}
static int igmp_mc_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
- seq_printf(seq,
- "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n");
+ seq_puts(seq,
+ "Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n");
else {
struct ip_mc_list *im = (struct ip_mc_list *)v;
struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
}
seq_printf(seq,
- "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
+ "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n",
im->multiaddr, im->users,
im->tm_running, im->tm_running ?
jiffies_to_clock_t(im->timer.expires-jiffies) : 0,
{
struct seq_file *seq;
int rc = -ENOMEM;
- struct igmp_mc_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ struct igmp_mc_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
seq = file->private_data;
seq->private = s;
- memset(s, 0, sizeof(*s));
out:
return rc;
out_kfree:
idev = in_dev_get(state->dev);
if (unlikely(idev == NULL))
continue;
- read_lock_bh(&idev->lock);
+ read_lock(&idev->mc_list_lock);
im = idev->mc_list;
if (likely(im != NULL)) {
spin_lock_bh(&im->lock);
}
spin_unlock_bh(&im->lock);
}
- read_unlock_bh(&idev->lock);
+ read_unlock(&idev->mc_list_lock);
in_dev_put(idev);
}
return psf;
state->im = state->im->next;
while (!state->im) {
if (likely(state->idev != NULL)) {
- read_unlock_bh(&state->idev->lock);
+ read_unlock(&state->idev->mc_list_lock);
in_dev_put(state->idev);
}
state->dev = state->dev->next;
state->idev = in_dev_get(state->dev);
if (!state->idev)
continue;
- read_lock_bh(&state->idev->lock);
+ read_lock(&state->idev->mc_list_lock);
state->im = state->idev->mc_list;
}
if (!state->im)
state->im = NULL;
}
if (likely(state->idev != NULL)) {
- read_unlock_bh(&state->idev->lock);
+ read_unlock(&state->idev->mc_list_lock);
in_dev_put(state->idev);
state->idev = NULL;
}
{
struct seq_file *seq;
int rc = -ENOMEM;
- struct igmp_mcf_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ struct igmp_mcf_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
seq = file->private_data;
seq->private = s;
- memset(s, 0, sizeof(*s));
out:
return rc;
out_kfree: