Blame view

net/ipv4/igmp.c 63.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   *	Linux NET3:	Internet Group Management Protocol  [IGMP]
   *
   *	This code implements the IGMP protocol as defined in RFC1112. There has
   *	been a further revision of this protocol since which is now supported.
   *
   *	If you have trouble with this module be careful what gcc you have used,
   *	the older version didn't come out right using gcc 2.5.8, the newer one
   *	seems to fall out with gcc 2.6.2.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
   *	Authors:
113aa838e   Alan Cox   net: Rationalise ...
12
   *		Alan Cox <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
   *
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   *
   *	Fixes:
   *
   *		Alan Cox	:	Added lots of __inline__ to optimise
   *					the memory usage of all the tiny little
   *					functions.
   *		Alan Cox	:	Dumped the header building experiment.
   *		Alan Cox	:	Minor tweaks ready for multicast routing
   *					and extended IGMP protocol.
   *		Alan Cox	:	Removed a load of inline directives. Gcc 2.5.8
   *					writes utterly bogus code otherwise (sigh)
   *					fixed IGMP loopback to behave in the manner
   *					desired by mrouted, fixed the fact it has been
   *					broken since 1.3.6 and cleaned up a few minor
   *					points.
   *
   *		Chih-Jen Chang	:	Tried to revise IGMP to Version 2
   *		Tsu-Sheng Tsao		E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
36
   *					The enhancements are mainly based on Steve Deering's
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
43
44
45
46
47
48
49
   * 					ipmulti-3.5 source code.
   *		Chih-Jen Chang	:	Added the igmp_get_mrouter_info and
   *		Tsu-Sheng Tsao		igmp_set_mrouter_info to keep track of
   *					the mrouted version on that device.
   *		Chih-Jen Chang	:	Added the max_resp_time parameter to
   *		Tsu-Sheng Tsao		igmp_heard_query(). Using this parameter
   *					to identify the multicast router version
   *					and do what the IGMP version 2 specified.
   *		Chih-Jen Chang	:	Added a timer to revert to IGMP V2 router
   *		Tsu-Sheng Tsao		if the specified time expired.
   *		Alan Cox	:	Stop IGMP from 0.0.0.0 being accepted.
   *		Alan Cox	:	Use GFP_ATOMIC in the right places.
   *		Christian Daudt :	igmp timer wasn't set for local group
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
50
51
   *					memberships but was being deleted,
   *					which caused a "del_timer() called
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
   *					from %p with timer not initialized
  "
   *					message (960131).
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
55
   *		Christian Daudt :	removed del_timer from
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
   *					igmp_timer_expire function (960205).
   *             Christian Daudt :       igmp_heard_report now only calls
   *                                     igmp_timer_expire if tm->running is
   *                                     true (960216).
   *		Malcolm Beattie :	ttl comparison wrong in igmp_rcv made
   *					igmp_heard_query never trigger. Expiry
   *					miscalculation fixed in igmp_heard_query
   *					and random() made to return unsigned to
   *					prevent negative expiry times.
   *		Alexey Kuznetsov:	Wrong group leaving behaviour, backport
   *					fix from pending 2.1.x patches.
   *		Alan Cox:		Forget to enable FDDI support earlier.
   *		Alexey Kuznetsov:	Fixed leaving groups on device down.
   *		Alexey Kuznetsov:	Accordance to igmp-v2-06 draft.
   *		David L Stevens:	IGMPv3 support, with help from
   *					Vinay Kulkarni
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
74
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  #include <asm/uaccess.h>
  #include <asm/system.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/jiffies.h>
  #include <linux/string.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
  #include <linux/in.h>
  #include <linux/inet.h>
  #include <linux/netdevice.h>
  #include <linux/skbuff.h>
  #include <linux/inetdevice.h>
  #include <linux/igmp.h>
  #include <linux/if_arp.h>
  #include <linux/rtnetlink.h>
  #include <linux/times.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
92

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
93
  #include <net/net_namespace.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
94
  #include <net/arp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  #include <net/ip.h>
  #include <net/protocol.h>
  #include <net/route.h>
  #include <net/sock.h>
  #include <net/checksum.h>
  #include <linux/netfilter_ipv4.h>
  #ifdef CONFIG_IP_MROUTE
  #include <linux/mroute.h>
  #endif
  #ifdef CONFIG_PROC_FS
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
  #endif
  
  #define IP_MAX_MEMBERSHIPS	20
  #define IP_MAX_MSF		10
  
  #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_Router_Present_Timeout		(400*HZ)
  #define IGMP_Unsolicited_Report_Interval	(10*HZ)
  #define IGMP_Query_Response_Interval		(10*HZ)
  #define IGMP_Unsolicited_Report_Count		2
  
  
  #define IGMP_Initial_Report_Delay		(1)
  
  /* IGMP_Initial_Report_Delay is not from IGMP specs!
   * IGMP specs require to report membership immediately after
   * joining a group, but we delay the first report by a
   * small interval. It seems more natural and still does not
   * contradict to specs provided this delay is small enough.
   */
42f811b8b   Herbert Xu   [IPV4]: Convert I...
130
  #define IGMP_V1_SEEN(in_dev) \
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
131
  	(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \
42f811b8b   Herbert Xu   [IPV4]: Convert I...
132
133
134
135
  	 IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1 || \
  	 ((in_dev)->mr_v1_seen && \
  	  time_before(jiffies, (in_dev)->mr_v1_seen)))
  #define IGMP_V2_SEEN(in_dev) \
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
136
  	(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \
42f811b8b   Herbert Xu   [IPV4]: Convert I...
137
138
139
  	 IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2 || \
  	 ((in_dev)->mr_v2_seen && \
  	  time_before(jiffies, (in_dev)->mr_v2_seen)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  
  static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
63007727e   Al Viro   [IPV4]: trivial i...
142
  static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
  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);
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
148
149
  static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
  			 int sfcount, __be32 *psfsrc, int delta);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  
  static void ip_ma_put(struct ip_mc_list *im)
  {
  	if (atomic_dec_and_test(&im->refcnt)) {
  		in_dev_put(im->interface);
  		kfree(im);
  	}
  }
  
  #ifdef CONFIG_IP_MULTICAST
  
  /*
   *	Timer management
   */
  
  static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
  {
  	spin_lock_bh(&im->lock);
  	if (del_timer(&im->timer))
  		atomic_dec(&im->refcnt);
a7e9ff735   Jianjun Kong   net: clean up net...
170
  	im->tm_running = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
178
  	im->reporter = 0;
  	im->unsolicit_count = 0;
  	spin_unlock_bh(&im->lock);
  }
  
  /* It must be called with locked im->lock */
  static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
  {
a7e9ff735   Jianjun Kong   net: clean up net...
179
  	int tv = net_random() % max_delay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180

a7e9ff735   Jianjun Kong   net: clean up net...
181
  	im->tm_running = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  	if (!mod_timer(&im->timer, jiffies+tv+2))
  		atomic_inc(&im->refcnt);
  }
  
  static void igmp_gq_start_timer(struct in_device *in_dev)
  {
  	int tv = net_random() % in_dev->mr_maxdelay;
  
  	in_dev->mr_gq_running = 1;
  	if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2))
  		in_dev_hold(in_dev);
  }
  
  static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
  {
  	int tv = net_random() % delay;
  
  	if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))
  		in_dev_hold(in_dev);
  }
  
  static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
  {
  	spin_lock_bh(&im->lock);
  	im->unsolicit_count = 0;
  	if (del_timer(&im->timer)) {
  		if ((long)(im->timer.expires-jiffies) < max_delay) {
  			add_timer(&im->timer);
a7e9ff735   Jianjun Kong   net: clean up net...
210
  			im->tm_running = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  			spin_unlock_bh(&im->lock);
  			return;
  		}
  		atomic_dec(&im->refcnt);
  	}
  	igmp_start_timer(im, max_delay);
  	spin_unlock_bh(&im->lock);
  }
  
  
  /*
   *	Send an IGMP report.
   */
  
  #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
  
  
  static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type,
  	int gdeleted, int sdeleted)
  {
  	switch (type) {
  	case IGMPV3_MODE_IS_INCLUDE:
  	case IGMPV3_MODE_IS_EXCLUDE:
  		if (gdeleted || sdeleted)
  			return 0;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
236
237
238
239
240
241
242
243
244
245
246
247
  		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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  	case IGMPV3_CHANGE_TO_INCLUDE:
  		if (gdeleted || sdeleted)
  			return 0;
  		return psf->sf_count[MCAST_INCLUDE] != 0;
  	case IGMPV3_CHANGE_TO_EXCLUDE:
  		if (gdeleted || sdeleted)
  			return 0;
  		if (pmc->sfcount[MCAST_EXCLUDE] == 0 ||
  		    psf->sf_count[MCAST_INCLUDE])
  			return 0;
  		return pmc->sfcount[MCAST_EXCLUDE] ==
  			psf->sf_count[MCAST_EXCLUDE];
  	case IGMPV3_ALLOW_NEW_SOURCES:
  		if (gdeleted || !psf->sf_crcount)
  			return 0;
  		return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted;
  	case IGMPV3_BLOCK_OLD_SOURCES:
  		if (pmc->sfmode == MCAST_INCLUDE)
  			return gdeleted || (psf->sf_crcount && sdeleted);
  		return psf->sf_crcount && !gdeleted && !sdeleted;
  	}
  	return 0;
  }
  
  static int
  igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
  {
  	struct ip_sf_list *psf;
  	int scount = 0;
  
  	for (psf=pmc->sources; psf; psf=psf->sf_next) {
  		if (!is_in(pmc, psf, type, gdeleted, sdeleted))
  			continue;
  		scount++;
  	}
  	return scount;
  }
  
  static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
  {
  	struct sk_buff *skb;
  	struct rtable *rt;
  	struct iphdr *pip;
  	struct igmpv3_report *pig;
877acedc0   Daniel Lezcano   netns: Fix crash ...
292
  	struct net *net = dev_net(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293

f5184d267   Johannes Berg   net: Allow netdev...
294
  	skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
  	if (skb == NULL)
  		return NULL;
  
  	{
  		struct flowi fl = { .oif = dev->ifindex,
  				    .nl_u = { .ip4_u = {
  				    .daddr = IGMPV3_ALL_MCR } },
  				    .proto = IPPROTO_IGMP };
877acedc0   Daniel Lezcano   netns: Fix crash ...
303
  		if (ip_route_output_key(net, &rt, &fl)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
308
309
310
311
312
  			kfree_skb(skb);
  			return NULL;
  		}
  	}
  	if (rt->rt_src == 0) {
  		kfree_skb(skb);
  		ip_rt_put(rt);
  		return NULL;
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
313
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
  	skb->dev = dev;
  
  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
317
  	skb_reset_network_header(skb);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
318
  	pip = ip_hdr(skb);
7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
319
  	skb_put(skb, sizeof(struct iphdr) + 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
325
326
327
328
329
  
  	pip->version  = 4;
  	pip->ihl      = (sizeof(struct iphdr)+4)>>2;
  	pip->tos      = 0xc0;
  	pip->frag_off = htons(IP_DF);
  	pip->ttl      = 1;
  	pip->daddr    = rt->rt_dst;
  	pip->saddr    = rt->rt_src;
  	pip->protocol = IPPROTO_IGMP;
  	pip->tot_len  = 0;	/* filled in later */
d8d1f30b9   Changli Gao   net-next: remove ...
330
  	ip_select_ident(pip, &rt->dst, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
  	((u8*)&pip[1])[0] = IPOPT_RA;
  	((u8*)&pip[1])[1] = 4;
  	((u8*)&pip[1])[2] = 0;
  	((u8*)&pip[1])[3] = 0;
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
335
  	skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4;
d10ba34b0   Arnaldo Carvalho de Melo   [SK_BUFF]: More s...
336
  	skb_put(skb, sizeof(*pig));
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
337
  	pig = igmpv3_report_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
342
343
344
345
346
347
  	pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT;
  	pig->resv1 = 0;
  	pig->csum = 0;
  	pig->resv2 = 0;
  	pig->ngrec = 0;
  	return skb;
  }
  
  static int igmpv3_sendpack(struct sk_buff *skb)
  {
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
348
  	struct igmphdr *pig = igmp_hdr(skb);
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
349
  	const int igmplen = skb->tail - skb->transport_header;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350

d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
351
  	pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352

c439cb2e4   Herbert Xu   [IPV4]: Add ip_lo...
353
  	return ip_local_out(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
  }
  
  static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
  {
a7e9ff735   Jianjun Kong   net: clean up net...
358
  	return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  }
  
  static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
  	int type, struct igmpv3_grec **ppgr)
  {
  	struct net_device *dev = pmc->interface->dev;
  	struct igmpv3_report *pih;
  	struct igmpv3_grec *pgr;
  
  	if (!skb)
  		skb = igmpv3_newpack(dev, dev->mtu);
  	if (!skb)
  		return NULL;
  	pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
  	pgr->grec_type = type;
  	pgr->grec_auxwords = 0;
  	pgr->grec_nsrcs = 0;
  	pgr->grec_mca = pmc->multiaddr;
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
377
  	pih = igmpv3_report_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  	pih->ngrec = htons(ntohs(pih->ngrec)+1);
  	*ppgr = pgr;
  	return skb;
  }
  
  #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \
  	skb_tailroom(skb)) : 0)
  
  static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
  	int type, int gdeleted, int sdeleted)
  {
  	struct net_device *dev = pmc->interface->dev;
  	struct igmpv3_report *pih;
  	struct igmpv3_grec *pgr = NULL;
  	struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
393
  	int scount, stotal, first, isquery, truncate;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
399
400
401
  
  	if (pmc->multiaddr == IGMP_ALL_HOSTS)
  		return skb;
  
  	isquery = type == IGMPV3_MODE_IS_INCLUDE ||
  		  type == IGMPV3_MODE_IS_EXCLUDE;
  	truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
  		    type == IGMPV3_CHANGE_TO_EXCLUDE;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
402
  	stotal = scount = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  	psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
404
405
  	if (!*psf_list)
  		goto empty_source;
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
406
  	pih = skb ? igmpv3_report_hdr(skb) : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
414
415
416
417
  
  	/* EX and TO_EX get a fresh packet, if needed */
  	if (truncate) {
  		if (pih && pih->ngrec &&
  		    AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
  			if (skb)
  				igmpv3_sendpack(skb);
  			skb = igmpv3_newpack(dev, dev->mtu);
  		}
  	}
  	first = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
  	psf_prev = NULL;
  	for (psf=*psf_list; psf; psf=psf_next) {
ea4d9e722   Al Viro   [IPV4]: struct ip...
420
  		__be32 *psrc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
  
  		psf_next = psf->sf_next;
  
  		if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
  			psf_prev = psf;
  			continue;
  		}
  
  		/* clear marks on query responses */
  		if (isquery)
  			psf->sf_gsresp = 0;
63007727e   Al Viro   [IPV4]: trivial i...
432
  		if (AVAILABLE(skb) < sizeof(__be32) +
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  		    first*sizeof(struct igmpv3_grec)) {
  			if (truncate && !first)
  				break;	 /* truncate these */
  			if (pgr)
  				pgr->grec_nsrcs = htons(scount);
  			if (skb)
  				igmpv3_sendpack(skb);
  			skb = igmpv3_newpack(dev, dev->mtu);
  			first = 1;
  			scount = 0;
  		}
  		if (first) {
  			skb = add_grhead(skb, pmc, type, &pgr);
  			first = 0;
  		}
cc63f70b8   Alexey Dobriyan   [IPV4/IPV6] multi...
448
449
  		if (!skb)
  			return NULL;
63007727e   Al Viro   [IPV4]: trivial i...
450
  		psrc = (__be32 *)skb_put(skb, sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
  		*psrc = psf->sf_inaddr;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
452
  		scount++; stotal++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
456
457
458
459
460
461
462
463
464
465
466
  		if ((type == IGMPV3_ALLOW_NEW_SOURCES ||
  		     type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
  			psf->sf_crcount--;
  			if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
  				if (psf_prev)
  					psf_prev->sf_next = psf->sf_next;
  				else
  					*psf_list = psf->sf_next;
  				kfree(psf);
  				continue;
  			}
  		}
  		psf_prev = psf;
  	}
ad12583f4   David L Stevens   [IPV4]: Fix multi...
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
  
  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);
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
  	if (pgr)
  		pgr->grec_nsrcs = htons(scount);
  
  	if (isquery)
  		pmc->gsquery = 0;	/* clear query state on report */
  	return skb;
  }
  
  static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
  {
  	struct sk_buff *skb = NULL;
  	int type;
  
  	if (!pmc) {
  		read_lock(&in_dev->mc_list_lock);
  		for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
  			if (pmc->multiaddr == IGMP_ALL_HOSTS)
  				continue;
  			spin_lock_bh(&pmc->lock);
  			if (pmc->sfcount[MCAST_EXCLUDE])
  				type = IGMPV3_MODE_IS_EXCLUDE;
  			else
  				type = IGMPV3_MODE_IS_INCLUDE;
  			skb = add_grec(skb, pmc, type, 0, 0);
  			spin_unlock_bh(&pmc->lock);
  		}
  		read_unlock(&in_dev->mc_list_lock);
  	} else {
  		spin_lock_bh(&pmc->lock);
  		if (pmc->sfcount[MCAST_EXCLUDE])
  			type = IGMPV3_MODE_IS_EXCLUDE;
  		else
  			type = IGMPV3_MODE_IS_INCLUDE;
  		skb = add_grec(skb, pmc, type, 0, 0);
  		spin_unlock_bh(&pmc->lock);
  	}
  	if (!skb)
  		return 0;
  	return igmpv3_sendpack(skb);
  }
  
  /*
   * remove zero-count source records from a source filter list
   */
  static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
  {
  	struct ip_sf_list *psf_prev, *psf_next, *psf;
  
  	psf_prev = NULL;
  	for (psf=*ppsf; psf; psf = psf_next) {
  		psf_next = psf->sf_next;
  		if (psf->sf_crcount == 0) {
  			if (psf_prev)
  				psf_prev->sf_next = psf->sf_next;
  			else
  				*ppsf = psf->sf_next;
  			kfree(psf);
  		} else
  			psf_prev = psf;
  	}
  }
  
  static void igmpv3_send_cr(struct in_device *in_dev)
  {
  	struct ip_mc_list *pmc, *pmc_prev, *pmc_next;
  	struct sk_buff *skb = NULL;
  	int type, dtype;
  
  	read_lock(&in_dev->mc_list_lock);
  	spin_lock_bh(&in_dev->mc_tomb_lock);
  
  	/* deleted MCA's */
  	pmc_prev = NULL;
  	for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) {
  		pmc_next = pmc->next;
  		if (pmc->sfmode == MCAST_INCLUDE) {
  			type = IGMPV3_BLOCK_OLD_SOURCES;
  			dtype = IGMPV3_BLOCK_OLD_SOURCES;
  			skb = add_grec(skb, pmc, type, 1, 0);
  			skb = add_grec(skb, pmc, dtype, 1, 1);
  		}
  		if (pmc->crcount) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
  			if (pmc->sfmode == MCAST_EXCLUDE) {
  				type = IGMPV3_CHANGE_TO_INCLUDE;
  				skb = add_grec(skb, pmc, type, 1, 0);
  			}
ad12583f4   David L Stevens   [IPV4]: Fix multi...
568
  			pmc->crcount--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  			if (pmc->crcount == 0) {
  				igmpv3_clear_zeros(&pmc->tomb);
  				igmpv3_clear_zeros(&pmc->sources);
  			}
  		}
  		if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
  			if (pmc_prev)
  				pmc_prev->next = pmc_next;
  			else
  				in_dev->mc_tomb = pmc_next;
  			in_dev_put(pmc->interface);
  			kfree(pmc);
  		} else
  			pmc_prev = pmc;
  	}
  	spin_unlock_bh(&in_dev->mc_tomb_lock);
  
  	/* change recs */
  	for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
  		spin_lock_bh(&pmc->lock);
  		if (pmc->sfcount[MCAST_EXCLUDE]) {
  			type = IGMPV3_BLOCK_OLD_SOURCES;
  			dtype = IGMPV3_ALLOW_NEW_SOURCES;
  		} else {
  			type = IGMPV3_ALLOW_NEW_SOURCES;
  			dtype = IGMPV3_BLOCK_OLD_SOURCES;
  		}
  		skb = add_grec(skb, pmc, type, 0, 0);
  		skb = add_grec(skb, pmc, dtype, 0, 1);	/* deleted sources */
  
  		/* filter mode changes */
  		if (pmc->crcount) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
604
605
  			if (pmc->sfmode == MCAST_EXCLUDE)
  				type = IGMPV3_CHANGE_TO_EXCLUDE;
  			else
  				type = IGMPV3_CHANGE_TO_INCLUDE;
  			skb = add_grec(skb, pmc, type, 0, 0);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
606
  			pmc->crcount--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
  		}
  		spin_unlock_bh(&pmc->lock);
  	}
  	read_unlock(&in_dev->mc_list_lock);
  
  	if (!skb)
  		return;
  	(void) igmpv3_sendpack(skb);
  }
  
  static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
  	int type)
  {
  	struct sk_buff *skb;
  	struct iphdr *iph;
  	struct igmphdr *ih;
  	struct rtable *rt;
  	struct net_device *dev = in_dev->dev;
877acedc0   Daniel Lezcano   netns: Fix crash ...
625
  	struct net *net = dev_net(dev);
63007727e   Al Viro   [IPV4]: trivial i...
626
627
  	__be32	group = pmc ? pmc->multiaddr : 0;
  	__be32	dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
633
634
635
636
637
638
639
  
  	if (type == IGMPV3_HOST_MEMBERSHIP_REPORT)
  		return igmpv3_send_report(in_dev, pmc);
  	else if (type == IGMP_HOST_LEAVE_MESSAGE)
  		dst = IGMP_ALL_ROUTER;
  	else
  		dst = group;
  
  	{
  		struct flowi fl = { .oif = dev->ifindex,
  				    .nl_u = { .ip4_u = { .daddr = dst } },
  				    .proto = IPPROTO_IGMP };
877acedc0   Daniel Lezcano   netns: Fix crash ...
640
  		if (ip_route_output_key(net, &rt, &fl))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
646
  			return -1;
  	}
  	if (rt->rt_src == 0) {
  		ip_rt_put(rt);
  		return -1;
  	}
a7e9ff735   Jianjun Kong   net: clean up net...
647
  	skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
  	if (skb == NULL) {
  		ip_rt_put(rt);
  		return -1;
  	}
d8d1f30b9   Changli Gao   net-next: remove ...
652
  	skb_dst_set(skb, &rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
654
  
  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
655
  	skb_reset_network_header(skb);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
656
  	iph = ip_hdr(skb);
7e28ecc28   Arnaldo Carvalho de Melo   [SK_BUFF]: Use sk...
657
  	skb_put(skb, sizeof(struct iphdr) + 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
661
662
663
664
665
666
  
  	iph->version  = 4;
  	iph->ihl      = (sizeof(struct iphdr)+4)>>2;
  	iph->tos      = 0xc0;
  	iph->frag_off = htons(IP_DF);
  	iph->ttl      = 1;
  	iph->daddr    = dst;
  	iph->saddr    = rt->rt_src;
  	iph->protocol = IPPROTO_IGMP;
d8d1f30b9   Changli Gao   net-next: remove ...
667
  	ip_select_ident(iph, &rt->dst, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
670
671
  	((u8*)&iph[1])[0] = IPOPT_RA;
  	((u8*)&iph[1])[1] = 4;
  	((u8*)&iph[1])[2] = 0;
  	((u8*)&iph[1])[3] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
  
  	ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
a7e9ff735   Jianjun Kong   net: clean up net...
674
675
676
677
678
  	ih->type = type;
  	ih->code = 0;
  	ih->csum = 0;
  	ih->group = group;
  	ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679

c439cb2e4   Herbert Xu   [IPV4]: Add ip_lo...
680
  	return ip_local_out(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
  }
  
  static void igmp_gq_timer_expire(unsigned long data)
  {
  	struct in_device *in_dev = (struct in_device *)data;
  
  	in_dev->mr_gq_running = 0;
  	igmpv3_send_report(in_dev, NULL);
  	__in_dev_put(in_dev);
  }
  
  static void igmp_ifc_timer_expire(unsigned long data)
  {
  	struct in_device *in_dev = (struct in_device *)data;
  
  	igmpv3_send_cr(in_dev);
  	if (in_dev->mr_ifc_count) {
  		in_dev->mr_ifc_count--;
  		igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval);
  	}
  	__in_dev_put(in_dev);
  }
  
  static void igmp_ifc_event(struct in_device *in_dev)
  {
  	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))
  		return;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
708
  	in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
713
714
715
716
717
718
719
  		IGMP_Unsolicited_Report_Count;
  	igmp_ifc_start_timer(in_dev, 1);
  }
  
  
  static void igmp_timer_expire(unsigned long data)
  {
  	struct ip_mc_list *im=(struct ip_mc_list *)data;
  	struct in_device *in_dev = im->interface;
  
  	spin_lock(&im->lock);
a7e9ff735   Jianjun Kong   net: clean up net...
720
  	im->tm_running = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
  
  	if (im->unsolicit_count) {
  		im->unsolicit_count--;
  		igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
  	}
  	im->reporter = 1;
  	spin_unlock(&im->lock);
  
  	if (IGMP_V1_SEEN(in_dev))
  		igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT);
  	else if (IGMP_V2_SEEN(in_dev))
  		igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT);
  	else
  		igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT);
  
  	ip_ma_put(im);
  }
ad12583f4   David L Stevens   [IPV4]: Fix multi...
738
  /* mark EXCLUDE-mode sources */
ea4d9e722   Al Viro   [IPV4]: struct ip...
739
  static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
744
745
746
747
  {
  	struct ip_sf_list *psf;
  	int i, scount;
  
  	scount = 0;
  	for (psf=pmc->sources; psf; psf=psf->sf_next) {
  		if (scount == nsrcs)
  			break;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
  		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;
  }
ea4d9e722   Al Viro   [IPV4]: struct ip...
765
  static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
ad12583f4   David L Stevens   [IPV4]: Fix multi...
766
767
768
769
770
771
772
773
774
775
776
777
  {
  	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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
781
782
783
784
  		for (i=0; i<nsrcs; i++)
  			if (srcs[i] == psf->sf_inaddr) {
  				psf->sf_gsresp = 1;
  				scount++;
  				break;
  			}
  	}
ad12583f4   David L Stevens   [IPV4]: Fix multi...
785
786
787
788
789
790
  	if (!scount) {
  		pmc->gsquery = 0;
  		return 0;
  	}
  	pmc->gsquery = 1;
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  }
63007727e   Al Viro   [IPV4]: trivial i...
792
  static void igmp_heard_report(struct in_device *in_dev, __be32 group)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
  {
  	struct ip_mc_list *im;
  
  	/* Timers are only set for non-local groups */
  
  	if (group == IGMP_ALL_HOSTS)
  		return;
  
  	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->mc_list_lock);
  }
  
  static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
  	int len)
  {
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
814
815
  	struct igmphdr 		*ih = igmp_hdr(skb);
  	struct igmpv3_query *ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
  	struct ip_mc_list	*im;
63007727e   Al Viro   [IPV4]: trivial i...
817
  	__be32			group = ih->group;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
821
822
823
824
  	int			max_delay;
  	int			mark = 0;
  
  
  	if (len == 8) {
  		if (ih->code == 0) {
  			/* Alas, old v1 router presents here. */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
825

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
  			max_delay = IGMP_Query_Response_Interval;
  			in_dev->mr_v1_seen = jiffies +
  				IGMP_V1_Router_Present_Timeout;
  			group = 0;
  		} else {
  			/* v2 router present */
  			max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);
  			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))
  			__in_dev_put(in_dev);
  		/* clear deleted report items */
  		igmpv3_clear_delrec(in_dev);
  	} else if (len < 12) {
  		return;	/* ignore bogus packet; freed by caller */
  	} else { /* v3 */
  		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
  			return;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
847

d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
848
  		ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
  		if (ih3->nsrcs) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
850
  			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
63007727e   Al Viro   [IPV4]: trivial i...
851
  					   + ntohs(ih3->nsrcs)*sizeof(__be32)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
  				return;
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
853
  			ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
  		}
  
  		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;
  		if (!group) { /* general query */
  			if (ih3->nsrcs)
  				return;	/* no sources allowed */
  			igmp_gq_start_timer(in_dev);
  			return;
  		}
  		/* mark sources to include, if group & source-specific */
  		mark = ih3->nsrcs != 0;
  	}
  
  	/*
  	 * - Start the timers in all of our membership records
  	 *   that the query applies to for the interface on
  	 *   which the query arrived excl. those that belong
  	 *   to a "local" group (224.0.0.X)
  	 * - For timers already running check if they need to
  	 *   be reset.
  	 * - Use the igmp->igmp_code field as the maximum
  	 *   delay possible
  	 */
  	read_lock(&in_dev->mc_list_lock);
  	for (im=in_dev->mc_list; im!=NULL; im=im->next) {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
884
  		int changed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
889
890
891
892
893
  		if (group && group != im->multiaddr)
  			continue;
  		if (im->multiaddr == IGMP_ALL_HOSTS)
  			continue;
  		spin_lock_bh(&im->lock);
  		if (im->tm_running)
  			im->gsquery = im->gsquery && mark;
  		else
  			im->gsquery = mark;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
894
  		changed = !im->gsquery ||
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
895
  			igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
  		spin_unlock_bh(&im->lock);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
897
898
  		if (changed)
  			igmp_mod_timer(im, max_delay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
901
  	}
  	read_unlock(&in_dev->mc_list_lock);
  }
9a57a9d29   Eric Dumazet   igmp: avoid two a...
902
  /* called in rcu_read_lock() section */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903
904
905
906
  int igmp_rcv(struct sk_buff *skb)
  {
  	/* This basically follows the spec line by line -- see RFC1112 */
  	struct igmphdr *ih;
9a57a9d29   Eric Dumazet   igmp: avoid two a...
907
  	struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  	int len = skb->len;
cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
909
910
  	if (in_dev == NULL)
  		goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911

fb286bb29   Herbert Xu   [NET]: Detect har...
912
  	if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
9a57a9d29   Eric Dumazet   igmp: avoid two a...
913
  		goto drop;
fb286bb29   Herbert Xu   [NET]: Detect har...
914
915
  
  	switch (skb->ip_summed) {
84fa7933a   Patrick McHardy   [NET]: Replace CH...
916
  	case CHECKSUM_COMPLETE:
d3bc23e7e   Al Viro   [NET]: Annotate c...
917
  		if (!csum_fold(skb->csum))
fb286bb29   Herbert Xu   [NET]: Detect har...
918
919
920
921
922
  			break;
  		/* fall through */
  	case CHECKSUM_NONE:
  		skb->csum = 0;
  		if (__skb_checksum_complete(skb))
9a57a9d29   Eric Dumazet   igmp: avoid two a...
923
  			goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
  	}
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
925
  	ih = igmp_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
929
930
931
  	switch (ih->type) {
  	case IGMP_HOST_MEMBERSHIP_QUERY:
  		igmp_heard_query(in_dev, skb, len);
  		break;
  	case IGMP_HOST_MEMBERSHIP_REPORT:
  	case IGMPV2_HOST_MEMBERSHIP_REPORT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
  		/* Is it our report looped back? */
511c3f92a   Eric Dumazet   net: skb->rtable ...
933
  		if (skb_rtable(skb)->fl.iif == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
  			break;
24c692750   David Stevens   [IGMP]: workaroun...
935
936
937
938
  		/* 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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
940
941
  		break;
  	case IGMP_PIM:
  #ifdef CONFIG_IP_PIMSM_V1
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
  		return pim_rcv_v1(skb);
  #endif
c6b471e64   Herbert Xu   inet: Remove bogu...
944
  	case IGMPV3_HOST_MEMBERSHIP_REPORT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
947
948
949
950
951
  	case IGMP_DVMRP:
  	case IGMP_TRACE:
  	case IGMP_HOST_LEAVE_MESSAGE:
  	case IGMP_MTRACE:
  	case IGMP_MTRACE_RESP:
  		break;
  	default:
dd1c1853e   Linus Torvalds   Fix ipv4/igmp.c c...
952
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953
  	}
fb286bb29   Herbert Xu   [NET]: Detect har...
954

cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
955
  drop:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956
957
958
959
960
961
962
963
964
965
  	kfree_skb(skb);
  	return 0;
  }
  
  #endif
  
  
  /*
   *	Add a filter to a device
   */
63007727e   Al Viro   [IPV4]: trivial i...
966
  static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
968
969
970
971
972
973
974
975
976
977
978
  {
  	char buf[MAX_ADDR_LEN];
  	struct net_device *dev = in_dev->dev;
  
  	/* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
  	   We will get multicast token leakage, when IFF_MULTICAST
  	   is changed. This check should be done in dev->set_multicast_list
  	   routine. Something sort of:
  	   if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
  	   --ANK
  	   */
  	if (arp_mc_map(addr, buf, dev, 0) == 0)
22bedad3c   Jiri Pirko   net: convert mult...
979
  		dev_mc_add(dev, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
981
982
983
984
  }
  
  /*
   *	Remove a filter from a device
   */
63007727e   Al Viro   [IPV4]: trivial i...
985
  static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
987
988
989
990
  {
  	char buf[MAX_ADDR_LEN];
  	struct net_device *dev = in_dev->dev;
  
  	if (arp_mc_map(addr, buf, dev, 0) == 0)
22bedad3c   Jiri Pirko   net: convert mult...
991
  		dev_mc_del(dev, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
  }
  
  #ifdef CONFIG_IP_MULTICAST
  /*
   * deleted ip_mc_list manipulation
   */
  static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
  {
  	struct ip_mc_list *pmc;
  
  	/* this is an "ip_mc_list" for convenience; only the fields below
  	 * are actually used. In particular, the refcnt and users are not
  	 * used for management of the delete list. Using the same structure
  	 * for deleted items allows change reports to use common code with
  	 * non-deleted or query-response MCA's.
  	 */
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
1008
  	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
1010
  	if (!pmc)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
  	spin_lock_bh(&im->lock);
  	pmc->interface = im->interface;
  	in_dev_hold(in_dev);
  	pmc->multiaddr = im->multiaddr;
  	pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
  		IGMP_Unsolicited_Report_Count;
  	pmc->sfmode = im->sfmode;
  	if (pmc->sfmode == MCAST_INCLUDE) {
  		struct ip_sf_list *psf;
  
  		pmc->tomb = im->tomb;
  		pmc->sources = im->sources;
  		im->tomb = im->sources = NULL;
  		for (psf=pmc->sources; psf; psf=psf->sf_next)
  			psf->sf_crcount = pmc->crcount;
  	}
  	spin_unlock_bh(&im->lock);
  
  	spin_lock_bh(&in_dev->mc_tomb_lock);
  	pmc->next = in_dev->mc_tomb;
  	in_dev->mc_tomb = pmc;
  	spin_unlock_bh(&in_dev->mc_tomb_lock);
  }
63007727e   Al Viro   [IPV4]: trivial i...
1034
  static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
  {
  	struct ip_mc_list *pmc, *pmc_prev;
  	struct ip_sf_list *psf, *psf_next;
  
  	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;
  		pmc_prev = pmc;
  	}
  	if (pmc) {
  		if (pmc_prev)
  			pmc_prev->next = pmc->next;
  		else
  			in_dev->mc_tomb = pmc->next;
  	}
  	spin_unlock_bh(&in_dev->mc_tomb_lock);
  	if (pmc) {
  		for (psf=pmc->tomb; psf; psf=psf_next) {
  			psf_next = psf->sf_next;
  			kfree(psf);
  		}
  		in_dev_put(pmc->interface);
  		kfree(pmc);
  	}
  }
  
  static void igmpv3_clear_delrec(struct in_device *in_dev)
  {
  	struct ip_mc_list *pmc, *nextpmc;
  
  	spin_lock_bh(&in_dev->mc_tomb_lock);
  	pmc = in_dev->mc_tomb;
  	in_dev->mc_tomb = NULL;
  	spin_unlock_bh(&in_dev->mc_tomb_lock);
  
  	for (; pmc; pmc = nextpmc) {
  		nextpmc = pmc->next;
  		ip_mc_clear_src(pmc);
  		in_dev_put(pmc->interface);
  		kfree(pmc);
  	}
  	/* clear dead sources, too */
  	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 = NULL;
  		spin_unlock_bh(&pmc->lock);
  		for (; psf; psf=psf_next) {
  			psf_next = psf->sf_next;
  			kfree(psf);
  		}
  	}
  	read_unlock(&in_dev->mc_list_lock);
  }
  #endif
  
  static void igmp_group_dropped(struct ip_mc_list *im)
  {
  	struct in_device *in_dev = im->interface;
  #ifdef CONFIG_IP_MULTICAST
  	int reporter;
  #endif
  
  	if (im->loaded) {
  		im->loaded = 0;
  		ip_mc_filter_del(in_dev, im->multiaddr);
  	}
  
  #ifdef CONFIG_IP_MULTICAST
  	if (im->multiaddr == IGMP_ALL_HOSTS)
  		return;
  
  	reporter = im->reporter;
  	igmp_stop_timer(im);
  
  	if (!in_dev->dead) {
  		if (IGMP_V1_SEEN(in_dev))
  			goto done;
  		if (IGMP_V2_SEEN(in_dev)) {
  			if (reporter)
  				igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE);
  			goto done;
  		}
  		/* IGMPv3 */
  		igmpv3_add_delrec(in_dev, im);
  
  		igmp_ifc_event(in_dev);
  	}
  done:
  #endif
  	ip_mc_clear_src(im);
  }
  
  static void igmp_group_added(struct ip_mc_list *im)
  {
  	struct in_device *in_dev = im->interface;
  
  	if (im->loaded == 0) {
  		im->loaded = 1;
  		ip_mc_filter_add(in_dev, im->multiaddr);
  	}
  
  #ifdef CONFIG_IP_MULTICAST
  	if (im->multiaddr == IGMP_ALL_HOSTS)
  		return;
  
  	if (in_dev->dead)
  		return;
  	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
  		spin_lock_bh(&im->lock);
  		igmp_start_timer(im, IGMP_Initial_Report_Delay);
  		spin_unlock_bh(&im->lock);
  		return;
  	}
  	/* else, v3 */
  
  	im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
  		IGMP_Unsolicited_Report_Count;
  	igmp_ifc_event(in_dev);
  #endif
  }
  
  
  /*
   *	Multicast list managers
   */
  
  
  /*
   *	A socket has joined a multicast group on device dev.
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1171
  void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
  {
  	struct ip_mc_list *im;
  
  	ASSERT_RTNL();
  
  	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, NULL, 0);
  			goto out;
  		}
  	}
8b3a70058   Kris Katterjohn   [NET]: Remove mor...
1184
  	im = kmalloc(sizeof(*im), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1185
1186
  	if (!im)
  		goto out;
a7e9ff735   Jianjun Kong   net: clean up net...
1187
1188
  	im->users = 1;
  	im->interface = in_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1189
  	in_dev_hold(in_dev);
a7e9ff735   Jianjun Kong   net: clean up net...
1190
  	im->multiaddr = addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
  	/* initial mode is (EX, empty) */
  	im->sfmode = MCAST_EXCLUDE;
  	im->sfcount[MCAST_INCLUDE] = 0;
  	im->sfcount[MCAST_EXCLUDE] = 1;
  	im->sources = NULL;
  	im->tomb = NULL;
  	im->crcount = 0;
  	atomic_set(&im->refcnt, 1);
  	spin_lock_init(&im->lock);
  #ifdef CONFIG_IP_MULTICAST
a7e9ff735   Jianjun Kong   net: clean up net...
1201
  	im->tm_running = 0;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1202
  	setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1203
1204
1205
1206
1207
1208
  	im->unsolicit_count = IGMP_Unsolicited_Report_Count;
  	im->reporter = 0;
  	im->gsquery = 0;
  #endif
  	im->loaded = 0;
  	write_lock_bh(&in_dev->mc_list_lock);
a7e9ff735   Jianjun Kong   net: clean up net...
1209
1210
  	im->next = in_dev->mc_list;
  	in_dev->mc_list = im;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1211
  	in_dev->mc_count++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
  	write_unlock_bh(&in_dev->mc_list_lock);
  #ifdef CONFIG_IP_MULTICAST
  	igmpv3_del_delrec(in_dev, im->multiaddr);
  #endif
  	igmp_group_added(im);
  	if (!in_dev->dead)
  		ip_rt_multicast_event(in_dev);
  out:
  	return;
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
1222
  EXPORT_SYMBOL(ip_mc_inc_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1223
1224
  
  /*
a816c7c71   Jay Vosburgh   bonding: Improve ...
1225
1226
1227
1228
   *	Resend IGMP JOIN report; used for bonding.
   */
  void ip_mc_rejoin_group(struct ip_mc_list *im)
  {
08882669e   Geert Uytterhoeven   [IPV4]: Fix warni...
1229
  #ifdef CONFIG_IP_MULTICAST
a816c7c71   Jay Vosburgh   bonding: Improve ...
1230
  	struct in_device *in_dev = im->interface;
a816c7c71   Jay Vosburgh   bonding: Improve ...
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
  	if (im->multiaddr == IGMP_ALL_HOSTS)
  		return;
  
  	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
  		igmp_mod_timer(im, IGMP_Initial_Report_Delay);
  		return;
  	}
  	/* else, v3 */
  	im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
  		IGMP_Unsolicited_Report_Count;
  	igmp_ifc_event(in_dev);
  #endif
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
1244
  EXPORT_SYMBOL(ip_mc_rejoin_group);
a816c7c71   Jay Vosburgh   bonding: Improve ...
1245
1246
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1247
1248
   *	A socket has left a multicast group on device dev
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1249
  void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1250
1251
  {
  	struct ip_mc_list *i, **ip;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1252

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1253
  	ASSERT_RTNL();
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1254

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1255
  	for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
a7e9ff735   Jianjun Kong   net: clean up net...
1256
  		if (i->multiaddr == addr) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1257
1258
1259
  			if (--i->users == 0) {
  				write_lock_bh(&in_dev->mc_list_lock);
  				*ip = i->next;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1260
  				in_dev->mc_count--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
  				write_unlock_bh(&in_dev->mc_list_lock);
  				igmp_group_dropped(i);
  
  				if (!in_dev->dead)
  					ip_rt_multicast_event(in_dev);
  
  				ip_ma_put(i);
  				return;
  			}
  			break;
  		}
  	}
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
1274
  EXPORT_SYMBOL(ip_mc_dec_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1275

75c78500d   Moni Shoua   bonding: remap mu...
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
  /* Device changing type */
  
  void ip_mc_unmap(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
  
  	for (i = in_dev->mc_list; i; i = i->next)
  		igmp_group_dropped(i);
  }
  
  void ip_mc_remap(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
  
  	for (i = in_dev->mc_list; i; i = i->next)
  		igmp_group_added(i);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
  /* Device going down */
  
  void ip_mc_down(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
  
  	for (i=in_dev->mc_list; i; i=i->next)
  		igmp_group_dropped(i);
  
  #ifdef CONFIG_IP_MULTICAST
  	in_dev->mr_ifc_count = 0;
  	if (del_timer(&in_dev->mr_ifc_timer))
  		__in_dev_put(in_dev);
  	in_dev->mr_gq_running = 0;
  	if (del_timer(&in_dev->mr_gq_timer))
  		__in_dev_put(in_dev);
  	igmpv3_clear_delrec(in_dev);
  #endif
  
  	ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
  }
  
  void ip_mc_init_dev(struct in_device *in_dev)
  {
  	ASSERT_RTNL();
  
  	in_dev->mc_tomb = NULL;
  #ifdef CONFIG_IP_MULTICAST
  	in_dev->mr_gq_running = 0;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1328
1329
  	setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire,
  			(unsigned long)in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1330
  	in_dev->mr_ifc_count = 0;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1331
  	in_dev->mc_count     = 0;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1332
1333
  	setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire,
  			(unsigned long)in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
  	in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;
  #endif
  
  	rwlock_init(&in_dev->mc_list_lock);
  	spin_lock_init(&in_dev->mc_tomb_lock);
  }
  
  /* Device going up */
  
  void ip_mc_up(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
  
  	ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
  
  	for (i=in_dev->mc_list; i; i=i->next)
  		igmp_group_added(i);
  }
  
  /*
   *	Device is about to be destroyed: clean up.
   */
  
  void ip_mc_destroy_dev(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
  
  	/* Deactivate timers */
  	ip_mc_down(in_dev);
  
  	write_lock_bh(&in_dev->mc_list_lock);
  	while ((i = in_dev->mc_list) != NULL) {
  		in_dev->mc_list = i->next;
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
1371
  		in_dev->mc_count--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1372
  		write_unlock_bh(&in_dev->mc_list_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
1374
1375
1376
1377
1378
1379
  		igmp_group_dropped(i);
  		ip_ma_put(i);
  
  		write_lock_bh(&in_dev->mc_list_lock);
  	}
  	write_unlock_bh(&in_dev->mc_list_lock);
  }
877acedc0   Daniel Lezcano   netns: Fix crash ...
1380
  static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381
1382
1383
1384
1385
1386
1387
1388
  {
  	struct flowi fl = { .nl_u = { .ip4_u =
  				      { .daddr = imr->imr_multiaddr.s_addr } } };
  	struct rtable *rt;
  	struct net_device *dev = NULL;
  	struct in_device *idev = NULL;
  
  	if (imr->imr_ifindex) {
877acedc0   Daniel Lezcano   netns: Fix crash ...
1389
  		idev = inetdev_by_index(net, imr->imr_ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1390
1391
1392
1393
1394
  		if (idev)
  			__in_dev_put(idev);
  		return idev;
  	}
  	if (imr->imr_address.s_addr) {
877acedc0   Daniel Lezcano   netns: Fix crash ...
1395
  		dev = ip_dev_find(net, imr->imr_address.s_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1396
1397
  		if (!dev)
  			return NULL;
153330618   Stephen Hemminger   [NET]: dev_put/de...
1398
  		dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399
  	}
877acedc0   Daniel Lezcano   netns: Fix crash ...
1400
  	if (!dev && !ip_route_output_key(net, &rt, &fl)) {
d8d1f30b9   Changli Gao   net-next: remove ...
1401
  		dev = rt->dst.dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1402
1403
1404
1405
  		ip_rt_put(rt);
  	}
  	if (dev) {
  		imr->imr_ifindex = dev->ifindex;
e5ed63991   Herbert Xu   [IPV4]: Replace _...
1406
  		idev = __in_dev_get_rtnl(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1407
1408
1409
1410
1411
1412
1413
  	}
  	return idev;
  }
  
  /*
   *	Join a socket to a group
   */
ab32ea5d8   Brian Haley   [NET/IPV4/IPV6]: ...
1414
1415
  int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS;
  int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1416
1417
1418
  
  
  static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1419
  	__be32 *psfsrc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
  {
  	struct ip_sf_list *psf, *psf_prev;
  	int rv = 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->sf_count[sfmode] == 0) {
  		/* source filter not found, or count wrong =>  bug */
  		return -ESRCH;
  	}
  	psf->sf_count[sfmode]--;
  	if (psf->sf_count[sfmode] == 0) {
  		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)) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1451
  			psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
  				IGMP_Unsolicited_Report_Count;
  			psf->sf_next = pmc->tomb;
  			pmc->tomb = psf;
  			rv = 1;
  		} else
  #endif
  			kfree(psf);
  	}
  	return rv;
  }
  
  #ifndef CONFIG_IP_MULTICAST
  #define igmp_ifc_event(x)	do { } while (0)
  #endif
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1466
1467
  static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
  			 int sfcount, __be32 *psfsrc, int delta)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
  {
  	struct ip_mc_list *pmc;
  	int	changerec = 0;
  	int	i, err;
  
  	if (!in_dev)
  		return -ENODEV;
  	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->mc_list_lock);
  		return -ESRCH;
  	}
  	spin_lock_bh(&pmc->lock);
  	read_unlock(&in_dev->mc_list_lock);
  #ifdef CONFIG_IP_MULTICAST
  	sf_markstate(pmc);
  #endif
  	if (!delta) {
  		err = -EINVAL;
  		if (!pmc->sfcount[sfmode])
  			goto out_unlock;
  		pmc->sfcount[sfmode]--;
  	}
  	err = 0;
  	for (i=0; i<sfcount; i++) {
  		int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]);
  
  		changerec |= rv > 0;
  		if (!err && rv < 0)
  			err = rv;
  	}
  	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
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1514
  		pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
  			IGMP_Unsolicited_Report_Count;
  		in_dev->mr_ifc_count = pmc->crcount;
  		for (psf=pmc->sources; psf; psf = psf->sf_next)
  			psf->sf_crcount = 0;
  		igmp_ifc_event(pmc->interface);
  	} else if (sf_setstate(pmc) || changerec) {
  		igmp_ifc_event(pmc->interface);
  #endif
  	}
  out_unlock:
  	spin_unlock_bh(&pmc->lock);
  	return err;
  }
  
  /*
   * Add multicast single-source filter to the interface list
   */
  static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1533
  	__be32 *psfsrc, int delta)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
  {
  	struct ip_sf_list *psf, *psf_prev;
  
  	psf_prev = NULL;
  	for (psf=pmc->sources; psf; psf=psf->sf_next) {
  		if (psf->sf_inaddr == *psfsrc)
  			break;
  		psf_prev = psf;
  	}
  	if (!psf) {
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
1544
  		psf = kzalloc(sizeof(*psf), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1545
1546
  		if (!psf)
  			return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
  		psf->sf_inaddr = *psfsrc;
  		if (psf_prev) {
  			psf_prev->sf_next = psf;
  		} else
  			pmc->sources = psf;
  	}
  	psf->sf_count[sfmode]++;
  	if (psf->sf_count[sfmode] == 1) {
  		ip_rt_multicast_event(pmc->interface);
  	}
  	return 0;
  }
  
  #ifdef CONFIG_IP_MULTICAST
  static void sf_markstate(struct ip_mc_list *pmc)
  {
  	struct ip_sf_list *psf;
  	int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
  
  	for (psf=pmc->sources; psf; psf=psf->sf_next)
  		if (pmc->sfcount[MCAST_EXCLUDE]) {
  			psf->sf_oldin = mca_xcount ==
  				psf->sf_count[MCAST_EXCLUDE] &&
  				!psf->sf_count[MCAST_INCLUDE];
  		} else
  			psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
  }
  
  static int sf_setstate(struct ip_mc_list *pmc)
  {
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1577
  	struct ip_sf_list *psf, *dpsf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
  	int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
  	int qrv = pmc->interface->mr_qrv;
  	int new_in, rv;
  
  	rv = 0;
  	for (psf=pmc->sources; psf; psf=psf->sf_next) {
  		if (pmc->sfcount[MCAST_EXCLUDE]) {
  			new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
  				!psf->sf_count[MCAST_INCLUDE];
  		} else
  			new_in = psf->sf_count[MCAST_INCLUDE] != 0;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1589
1590
  		if (new_in) {
  			if (!psf->sf_oldin) {
76edc6051   Al Viro   [PATCH] ipv4 NULL...
1591
  				struct ip_sf_list *prev = NULL;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
  
  				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) {
3ed37a6fa   Joe Perches   net/ipv4/igmp.c: ...
1619
  				dpsf = kmalloc(sizeof(*dpsf), GFP_ATOMIC);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1620
1621
1622
1623
1624
1625
1626
1627
  				if (!dpsf)
  					continue;
  				*dpsf = *psf;
  				/* pmc->lock held by callers */
  				dpsf->sf_next = pmc->tomb;
  				pmc->tomb = dpsf;
  			}
  			dpsf->sf_crcount = qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
  			rv++;
  		}
  	}
  	return rv;
  }
  #endif
  
  /*
   * Add multicast source filter list to the interface list
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1638
1639
  static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
  			 int sfcount, __be32 *psfsrc, int delta)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
  {
  	struct ip_mc_list *pmc;
  	int	isexclude;
  	int	i, err;
  
  	if (!in_dev)
  		return -ENODEV;
  	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->mc_list_lock);
  		return -ESRCH;
  	}
  	spin_lock_bh(&pmc->lock);
  	read_unlock(&in_dev->mc_list_lock);
  
  #ifdef CONFIG_IP_MULTICAST
  	sf_markstate(pmc);
  #endif
  	isexclude = pmc->sfmode == MCAST_EXCLUDE;
  	if (!delta)
  		pmc->sfcount[sfmode]++;
  	err = 0;
  	for (i=0; i<sfcount; i++) {
  		err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i], delta);
  		if (err)
  			break;
  	}
  	if (err) {
  		int j;
  
  		pmc->sfcount[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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1680
  		struct ip_sf_list *psf;
cfcabdcc2   Stephen Hemminger   [NET]: sparse war...
1681
  		in_dev = pmc->interface;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1682
1683
1684
1685
1686
1687
1688
1689
1690
  #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 */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1691
  		pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
  			IGMP_Unsolicited_Report_Count;
  		in_dev->mr_ifc_count = pmc->crcount;
  		for (psf=pmc->sources; psf; psf = psf->sf_next)
  			psf->sf_crcount = 0;
  		igmp_ifc_event(in_dev);
  	} else if (sf_setstate(pmc)) {
  		igmp_ifc_event(in_dev);
  #endif
  	}
  	spin_unlock_bh(&pmc->lock);
  	return err;
  }
  
  static void ip_mc_clear_src(struct ip_mc_list *pmc)
  {
  	struct ip_sf_list *psf, *nextpsf;
  
  	for (psf=pmc->tomb; psf; psf=nextpsf) {
  		nextpsf = psf->sf_next;
  		kfree(psf);
  	}
  	pmc->tomb = NULL;
  	for (psf=pmc->sources; psf; psf=nextpsf) {
  		nextpsf = psf->sf_next;
  		kfree(psf);
  	}
  	pmc->sources = NULL;
  	pmc->sfmode = MCAST_EXCLUDE;
de9daad90   Denis Lukianov   [MCAST]: Fix MCAS...
1720
  	pmc->sfcount[MCAST_INCLUDE] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
  	pmc->sfcount[MCAST_EXCLUDE] = 1;
  }
  
  
  /*
   * Join a multicast group
   */
  int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
  {
  	int err;
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1731
  	__be32 addr = imr->imr_multiaddr.s_addr;
a7e9ff735   Jianjun Kong   net: clean up net...
1732
  	struct ip_mc_socklist *iml = NULL, *i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1733
1734
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
877acedc0   Daniel Lezcano   netns: Fix crash ...
1735
  	struct net *net = sock_net(sk);
ca9b907d1   David L Stevens   [IPV4]: multicast...
1736
  	int ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737
  	int count = 0;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1738
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1739
  		return -EINVAL;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1740
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1741

877acedc0   Daniel Lezcano   netns: Fix crash ...
1742
  	in_dev = ip_mc_find_dev(net, imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1743
1744
1745
1746
1747
1748
  
  	if (!in_dev) {
  		iml = NULL;
  		err = -ENODEV;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1749
  	err = -EADDRINUSE;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1750
  	ifindex = imr->imr_ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1751
  	for (i = inet->mc_list; i; i = i->next) {
ca9b907d1   David L Stevens   [IPV4]: multicast...
1752
1753
  		if (i->multi.imr_multiaddr.s_addr == addr &&
  		    i->multi.imr_ifindex == ifindex)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1754
  			goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1755
1756
1757
  		count++;
  	}
  	err = -ENOBUFS;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1758
1759
  	if (count >= sysctl_igmp_max_memberships)
  		goto done;
a7e9ff735   Jianjun Kong   net: clean up net...
1760
  	iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
ca9b907d1   David L Stevens   [IPV4]: multicast...
1761
  	if (iml == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1762
  		goto done;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1763

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1764
1765
  	memcpy(&iml->multi, imr, sizeof(*imr));
  	iml->next = inet->mc_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1766
1767
  	iml->sflist = NULL;
  	iml->sfmode = MCAST_EXCLUDE;
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1768
  	rcu_assign_pointer(inet->mc_list, iml);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1769
  	ip_mc_inc_group(in_dev, addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1770
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1771
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1772
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1773
1774
  	return err;
  }
4bc2f18ba   Eric Dumazet   net/ipv4: EXPORT_...
1775
  EXPORT_SYMBOL(ip_mc_join_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1776

c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1777
1778
1779
1780
1781
1782
1783
1784
  static void ip_sf_socklist_reclaim(struct rcu_head *rp)
  {
  	struct ip_sf_socklist *psf;
  
  	psf = container_of(rp, struct ip_sf_socklist, rcu);
  	/* sk_omem_alloc should have been decreased by the caller*/
  	kfree(psf);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1785
1786
1787
  static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
  			   struct in_device *in_dev)
  {
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1788
  	struct ip_sf_socklist *psf = iml->sflist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1789
  	int err;
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1790
  	if (psf == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1791
1792
1793
1794
1795
  		/* any-source empty exclude case */
  		return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
  			iml->sfmode, 0, NULL, 0);
  	}
  	err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1796
1797
1798
1799
1800
  			iml->sfmode, psf->sl_count, psf->sl_addr, 0);
  	rcu_assign_pointer(iml->sflist, NULL);
  	/* decrease mem now to avoid the memleak warning */
  	atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc);
  	call_rcu(&psf->rcu, ip_sf_socklist_reclaim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
1802
  	return err;
  }
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1803
1804
1805
1806
1807
1808
1809
1810
1811
  
  static void ip_mc_socklist_reclaim(struct rcu_head *rp)
  {
  	struct ip_mc_socklist *iml;
  
  	iml = container_of(rp, struct ip_mc_socklist, rcu);
  	/* sk_omem_alloc should have been decreased by the caller*/
  	kfree(iml);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1812
1813
1814
1815
1816
1817
1818
1819
  /*
   *	Ask a socket to leave a group.
   */
  
  int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *iml, **imlp;
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1820
  	struct in_device *in_dev;
877acedc0   Daniel Lezcano   netns: Fix crash ...
1821
  	struct net *net = sock_net(sk);
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1822
  	__be32 group = imr->imr_multiaddr.s_addr;
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1823
  	u32 ifindex;
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1824
  	int ret = -EADDRNOTAVAIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1825
1826
  
  	rtnl_lock();
877acedc0   Daniel Lezcano   netns: Fix crash ...
1827
  	in_dev = ip_mc_find_dev(net, imr);
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1828
  	ifindex = imr->imr_ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1829
  	for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) {
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1830
1831
1832
1833
1834
1835
1836
1837
  		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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1838

acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1839
  		(void) ip_mc_leave_src(sk, iml, in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1840

c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1841
  		rcu_assign_pointer(*imlp, iml->next);
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1842
1843
  
  		if (in_dev)
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1844
  			ip_mc_dec_group(in_dev, group);
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1845
  		rtnl_unlock();
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1846
1847
1848
  		/* decrease mem now to avoid the memleak warning */
  		atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
  		call_rcu(&iml->rcu, ip_mc_socklist_reclaim);
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1849
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1850
  	}
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1851
1852
  	if (!in_dev)
  		ret = -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1853
  	rtnl_unlock();
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1854
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1855
1856
1857
1858
1859
1860
1861
  }
  
  int ip_mc_source(int add, int omode, struct sock *sk, struct
  	ip_mreq_source *mreqs, int ifindex)
  {
  	int err;
  	struct ip_mreqn imr;
63007727e   Al Viro   [IPV4]: trivial i...
1862
  	__be32 addr = mreqs->imr_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1863
1864
1865
1866
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev = NULL;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
877acedc0   Daniel Lezcano   netns: Fix crash ...
1867
  	struct net *net = sock_net(sk);
8cdaaa15d   David L Stevens   [IPV4]: multicast...
1868
  	int leavegroup = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1869
  	int i, j, rv;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1870
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1871
  		return -EINVAL;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1872
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1873
1874
1875
1876
  
  	imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
  	imr.imr_address.s_addr = mreqs->imr_interface;
  	imr.imr_ifindex = ifindex;
877acedc0   Daniel Lezcano   netns: Fix crash ...
1877
  	in_dev = ip_mc_find_dev(net, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1878
1879
1880
1881
1882
1883
1884
1885
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
  	err = -EADDRNOTAVAIL;
  
  	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
f64f9e719   Joe Perches   net: Move && and ...
1886
1887
1888
  		if ((pmc->multi.imr_multiaddr.s_addr ==
  		     imr.imr_multiaddr.s_addr) &&
  		    (pmc->multi.imr_ifindex == imr.imr_ifindex))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1889
1890
  			break;
  	}
917f2f105   David L Stevens   [IPV4]: multicast...
1891
1892
  	if (!pmc) {		/* must have a prior join */
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1893
  		goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
1894
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1895
1896
  	/* if a source filter was set, must be the same mode as before */
  	if (pmc->sflist) {
917f2f105   David L Stevens   [IPV4]: multicast...
1897
1898
  		if (pmc->sfmode != omode) {
  			err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1899
  			goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
1900
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1901
1902
1903
  	} else if (pmc->sfmode != omode) {
  		/* allow mode switches for empty-set filters */
  		ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1904
  		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1905
1906
1907
1908
1909
1910
1911
  			NULL, 0);
  		pmc->sfmode = omode;
  	}
  
  	psl = pmc->sflist;
  	if (!add) {
  		if (!psl)
917f2f105   David L Stevens   [IPV4]: multicast...
1912
  			goto done;	/* err = -EADDRNOTAVAIL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1913
1914
1915
  		rv = !0;
  		for (i=0; i<psl->sl_count; i++) {
  			rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
63007727e   Al Viro   [IPV4]: trivial i...
1916
  				sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1917
1918
1919
1920
  			if (rv == 0)
  				break;
  		}
  		if (rv)		/* source not found */
917f2f105   David L Stevens   [IPV4]: multicast...
1921
  			goto done;	/* err = -EADDRNOTAVAIL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1922

8cdaaa15d   David L Stevens   [IPV4]: multicast...
1923
1924
1925
1926
1927
  		/* special case - (INCLUDE, empty) == LEAVE_GROUP */
  		if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
  			leavegroup = 1;
  			goto done;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1928
  		/* update the interface filter */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1929
  		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
  			&mreqs->imr_sourceaddr, 1);
  
  		for (j=i+1; j<psl->sl_count; j++)
  			psl->sl_addr[j-1] = psl->sl_addr[j];
  		psl->sl_count--;
  		err = 0;
  		goto done;
  	}
  	/* else, add a new source to the filter */
  
  	if (psl && psl->sl_count >= sysctl_igmp_max_msf) {
  		err = -ENOBUFS;
  		goto done;
  	}
  	if (!psl || psl->sl_count == psl->sl_max) {
  		struct ip_sf_socklist *newpsl;
  		int count = IP_SFBLOCK;
  
  		if (psl)
  			count += psl->sl_max;
8b3a70058   Kris Katterjohn   [NET]: Remove mor...
1950
  		newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1951
1952
1953
1954
1955
1956
1957
1958
1959
  		if (!newpsl) {
  			err = -ENOBUFS;
  			goto done;
  		}
  		newpsl->sl_max = count;
  		newpsl->sl_count = count - IP_SFBLOCK;
  		if (psl) {
  			for (i=0; i<psl->sl_count; i++)
  				newpsl->sl_addr[i] = psl->sl_addr[i];
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1960
1961
1962
  			/* decrease mem now to avoid the memleak warning */
  			atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
  			call_rcu(&psl->rcu, ip_sf_socklist_reclaim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1963
  		}
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
1964
1965
  		rcu_assign_pointer(pmc->sflist, newpsl);
  		psl = newpsl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1966
1967
1968
1969
  	}
  	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[i], &mreqs->imr_sourceaddr,
63007727e   Al Viro   [IPV4]: trivial i...
1970
  			sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
  		if (rv == 0)
  			break;
  	}
  	if (rv == 0)		/* address already there is an error */
  		goto done;
  	for (j=psl->sl_count-1; j>=i; j--)
  		psl->sl_addr[j+1] = psl->sl_addr[j];
  	psl->sl_addr[i] = mreqs->imr_sourceaddr;
  	psl->sl_count++;
  	err = 0;
  	/* update the interface list */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1982
  	ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1983
1984
  		&mreqs->imr_sourceaddr, 1);
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1985
  	rtnl_unlock();
8cdaaa15d   David L Stevens   [IPV4]: multicast...
1986
1987
  	if (leavegroup)
  		return ip_mc_leave_group(sk, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1988
1989
1990
1991
1992
  	return err;
  }
  
  int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
  {
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
1993
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1994
  	struct ip_mreqn	imr;
63007727e   Al Viro   [IPV4]: trivial i...
1995
  	__be32 addr = msf->imsf_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1996
1997
1998
1999
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *newpsl, *psl;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2000
  	struct net *net = sock_net(sk);
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2001
  	int leavegroup = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2002

f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2003
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2004
2005
2006
2007
  		return -EINVAL;
  	if (msf->imsf_fmode != MCAST_INCLUDE &&
  	    msf->imsf_fmode != MCAST_EXCLUDE)
  		return -EINVAL;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2008
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2009
2010
2011
2012
  
  	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
  	imr.imr_address.s_addr = msf->imsf_interface;
  	imr.imr_ifindex = ifindex;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2013
  	in_dev = ip_mc_find_dev(net, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2014
2015
2016
2017
2018
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2019

9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2020
2021
2022
2023
2024
  	/* special case - (INCLUDE, empty) == LEAVE_GROUP */
  	if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) {
  		leavegroup = 1;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2025
2026
2027
2028
2029
  	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;
  	}
917f2f105   David L Stevens   [IPV4]: multicast...
2030
2031
  	if (!pmc) {		/* must have a prior join */
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2032
  		goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
2033
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2034
  	if (msf->imsf_numsrc) {
8b3a70058   Kris Katterjohn   [NET]: Remove mor...
2035
2036
  		newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc),
  							   GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
  		if (!newpsl) {
  			err = -ENOBUFS;
  			goto done;
  		}
  		newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc;
  		memcpy(newpsl->sl_addr, msf->imsf_slist,
  			msf->imsf_numsrc * sizeof(msf->imsf_slist[0]));
  		err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
  			msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0);
  		if (err) {
  			sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max));
  			goto done;
  		}
8713dbf05   Yan Zheng   [MCAST]: ip[6]_mc...
2050
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2051
  		newpsl = NULL;
8713dbf05   Yan Zheng   [MCAST]: ip[6]_mc...
2052
2053
2054
  		(void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
  				     msf->imsf_fmode, 0, NULL, 0);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2055
2056
2057
2058
  	psl = pmc->sflist;
  	if (psl) {
  		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
  			psl->sl_count, psl->sl_addr, 0);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2059
2060
2061
  		/* decrease mem now to avoid the memleak warning */
  		atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc);
  		call_rcu(&psl->rcu, ip_sf_socklist_reclaim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2062
2063
2064
  	} else
  		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
  			0, NULL, 0);
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2065
  	rcu_assign_pointer(pmc->sflist, newpsl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2066
  	pmc->sfmode = msf->imsf_fmode;
917f2f105   David L Stevens   [IPV4]: multicast...
2067
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2068
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2069
  	rtnl_unlock();
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2070
2071
  	if (leavegroup)
  		err = ip_mc_leave_group(sk, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2072
2073
2074
2075
2076
2077
2078
2079
  	return err;
  }
  
  int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
  	struct ip_msfilter __user *optval, int __user *optlen)
  {
  	int err, len, count, copycount;
  	struct ip_mreqn	imr;
63007727e   Al Viro   [IPV4]: trivial i...
2080
  	__be32 addr = msf->imsf_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2081
2082
2083
2084
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2085
  	struct net *net = sock_net(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2086

f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2087
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2088
  		return -EINVAL;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2089
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2090
2091
2092
2093
  
  	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
  	imr.imr_address.s_addr = msf->imsf_interface;
  	imr.imr_ifindex = 0;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2094
  	in_dev = ip_mc_find_dev(net, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
  	err = -EADDRNOTAVAIL;
  
  	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 */
  		goto done;
  	msf->imsf_fmode = pmc->sfmode;
  	psl = pmc->sflist;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2111
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
  	if (!psl) {
  		len = 0;
  		count = 0;
  	} else {
  		count = psl->sl_count;
  	}
  	copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc;
  	len = copycount * sizeof(psl->sl_addr[0]);
  	msf->imsf_numsrc = count;
  	if (put_user(IP_MSFILTER_SIZE(copycount), optlen) ||
  	    copy_to_user(optval, msf, IP_MSFILTER_SIZE(0))) {
  		return -EFAULT;
  	}
  	if (len &&
  	    copy_to_user(&optval->imsf_slist[0], psl->sl_addr, len))
  		return -EFAULT;
  	return 0;
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2130
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2131
2132
2133
2134
2135
2136
2137
2138
  	return err;
  }
  
  int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
  	struct group_filter __user *optval, int __user *optlen)
  {
  	int err, i, count, copycount;
  	struct sockaddr_in *psin;
63007727e   Al Viro   [IPV4]: trivial i...
2139
  	__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2140
2141
2142
2143
2144
2145
2146
2147
  	struct ip_mc_socklist *pmc;
  	struct inet_sock *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;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2148
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2149
  		return -EINVAL;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2150
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
  
  	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;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2163
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2164
2165
2166
2167
2168
2169
2170
2171
  	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(optval, gsf, GROUP_FILTER_SIZE(0))) {
  		return -EFAULT;
  	}
  	for (i=0; i<copycount; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
  		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(&optval->gf_slist[i], &ss, sizeof(ss)))
  			return -EFAULT;
  	}
  	return 0;
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2183
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2184
2185
2186
2187
2188
2189
  	return err;
  }
  
  /*
   * check if a multicast source filter allows delivery for a given <src,dst,intf>
   */
c0cda068a   Al Viro   [IPV4]: ip_mc_sf_...
2190
  int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2191
2192
2193
2194
2195
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *pmc;
  	struct ip_sf_socklist *psl;
  	int i;
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2196
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2197

c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2198
  	ret = 1;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2199
  	if (!ipv4_is_multicast(loc_addr))
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2200
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2201

c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2202
2203
  	rcu_read_lock();
  	for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2204
2205
2206
2207
  		if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
  		    pmc->multi.imr_ifindex == dif)
  			break;
  	}
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2208
  	ret = inet->mc_all;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2209
  	if (!pmc)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2210
  		goto unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2211
  	psl = pmc->sflist;
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2212
  	ret = (pmc->sfmode == MCAST_EXCLUDE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2213
  	if (!psl)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2214
  		goto unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2215
2216
2217
2218
2219
  
  	for (i=0; i<psl->sl_count; i++) {
  		if (psl->sl_addr[i] == rmt_addr)
  			break;
  	}
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2220
  	ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2221
  	if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2222
  		goto unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2223
  	if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2224
2225
2226
2227
2228
2229
  		goto unlock;
  	ret = 1;
  unlock:
  	rcu_read_unlock();
  out:
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
  }
  
  /*
   *	A socket is closing.
   */
  
  void ip_mc_drop_socket(struct sock *sk)
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *iml;
877acedc0   Daniel Lezcano   netns: Fix crash ...
2240
  	struct net *net = sock_net(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2241
2242
2243
2244
2245
2246
2247
  
  	if (inet->mc_list == NULL)
  		return;
  
  	rtnl_lock();
  	while ((iml = inet->mc_list) != NULL) {
  		struct in_device *in_dev;
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2248
  		rcu_assign_pointer(inet->mc_list, iml->next);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2249

877acedc0   Daniel Lezcano   netns: Fix crash ...
2250
  		in_dev = inetdev_by_index(net, iml->multi.imr_ifindex);
bb699cbca   Michal Ruzicka   [IPV4]: Possible ...
2251
2252
  		(void) ip_mc_leave_src(sk, iml, in_dev);
  		if (in_dev != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2253
2254
2255
  			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
  			in_dev_put(in_dev);
  		}
c85bb41e9   Flavio Leitner   igmp: fix ip_mc_s...
2256
2257
2258
  		/* decrease mem now to avoid the memleak warning */
  		atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
  		call_rcu(&iml->rcu, ip_mc_socklist_reclaim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2259
2260
2261
  	}
  	rtnl_unlock();
  }
a60c4923d   Al Viro   [IPV4]: ip_check_...
2262
  int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
  {
  	struct ip_mc_list *im;
  	struct ip_sf_list *psf;
  	int rv = 0;
  
  	read_lock(&in_dev->mc_list_lock);
  	for (im=in_dev->mc_list; im; im=im->next) {
  		if (im->multiaddr == mc_addr)
  			break;
  	}
  	if (im && proto == IPPROTO_IGMP) {
  		rv = 1;
  	} else if (im) {
  		if (src_addr) {
  			for (psf=im->sources; psf; psf=psf->sf_next) {
  				if (psf->sf_inaddr == src_addr)
  					break;
  			}
  			if (psf)
  				rv = psf->sf_count[MCAST_INCLUDE] ||
  					psf->sf_count[MCAST_EXCLUDE] !=
  					im->sfcount[MCAST_EXCLUDE];
  			else
  				rv = im->sfcount[MCAST_EXCLUDE] != 0;
  		} else
  			rv = 1; /* unspecified source; tentatively allow */
  	}
  	read_unlock(&in_dev->mc_list_lock);
  	return rv;
  }
  
  #if defined(CONFIG_PROC_FS)
  struct igmp_mc_iter_state {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2296
  	struct seq_net_private p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2297
2298
2299
2300
2301
2302
2303
2304
  	struct net_device *dev;
  	struct in_device *in_dev;
  };
  
  #define	igmp_mc_seq_private(seq)	((struct igmp_mc_iter_state *)(seq)->private)
  
  static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2305
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2306
2307
  	struct ip_mc_list *im = NULL;
  	struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
7562f876c   Pavel Emelianov   [NET]: Rework dev...
2308
  	state->in_dev = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2309
  	for_each_netdev_rcu(net, state->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2310
  		struct in_device *in_dev;
6baff1503   Eric Dumazet   igmp: Use next_ne...
2311
2312
  
  		in_dev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2313
2314
2315
2316
2317
2318
2319
2320
2321
  		if (!in_dev)
  			continue;
  		read_lock(&in_dev->mc_list_lock);
  		im = in_dev->mc_list;
  		if (im) {
  			state->in_dev = in_dev;
  			break;
  		}
  		read_unlock(&in_dev->mc_list_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2322
2323
2324
2325
2326
2327
2328
2329
2330
  	}
  	return im;
  }
  
  static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im)
  {
  	struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
  	im = im->next;
  	while (!im) {
6baff1503   Eric Dumazet   igmp: Use next_ne...
2331
  		if (likely(state->in_dev != NULL))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2332
  			read_unlock(&state->in_dev->mc_list_lock);
6baff1503   Eric Dumazet   igmp: Use next_ne...
2333
2334
  
  		state->dev = next_net_device_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2335
2336
2337
2338
  		if (!state->dev) {
  			state->in_dev = NULL;
  			break;
  		}
6baff1503   Eric Dumazet   igmp: Use next_ne...
2339
  		state->in_dev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
  		if (!state->in_dev)
  			continue;
  		read_lock(&state->in_dev->mc_list_lock);
  		im = state->in_dev->mc_list;
  	}
  	return im;
  }
  
  static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct ip_mc_list *im = igmp_mc_get_first(seq);
  	if (im)
  		while (pos && (im = igmp_mc_get_next(seq, im)) != NULL)
  			--pos;
  	return pos ? NULL : im;
  }
  
  static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2358
  	__acquires(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2359
  {
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2360
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
  	return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct ip_mc_list *im;
  	if (v == SEQ_START_TOKEN)
  		im = igmp_mc_get_first(seq);
  	else
  		im = igmp_mc_get_next(seq, v);
  	++*pos;
  	return im;
  }
  
  static void igmp_mc_seq_stop(struct seq_file *seq, void *v)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2376
  	__releases(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2377
2378
2379
2380
  {
  	struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
  	if (likely(state->in_dev != NULL)) {
  		read_unlock(&state->in_dev->mc_list_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2381
2382
2383
  		state->in_dev = NULL;
  	}
  	state->dev = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2384
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2385
2386
2387
2388
2389
  }
  
  static int igmp_mc_seq_show(struct seq_file *seq, void *v)
  {
  	if (v == SEQ_START_TOKEN)
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2390
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
  			 "Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter
  ");
  	else {
  		struct ip_mc_list *im = (struct ip_mc_list *)v;
  		struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
  		char   *querier;
  #ifdef CONFIG_IP_MULTICAST
  		querier = IGMP_V1_SEEN(state->in_dev) ? "V1" :
  			  IGMP_V2_SEEN(state->in_dev) ? "V2" :
  			  "V3";
  #else
  		querier = "NONE";
  #endif
  
  		if (state->in_dev->mc_list == im) {
  			seq_printf(seq, "%d\t%-10s: %5d %7s
  ",
b8bae41ed   Rami Rosen   ipv4: add mc_coun...
2408
  				   state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2409
2410
2411
  		}
  
  		seq_printf(seq,
338fcf988   Alexey Dobriyan   [IPV4] igmp: Fixu...
2412
2413
  			   "\t\t\t\t%08X %5d %d:%08lX\t\t%d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2414
2415
2416
2417
2418
2419
2420
  			   im->multiaddr, im->users,
  			   im->tm_running, im->tm_running ?
  			   jiffies_to_clock_t(im->timer.expires-jiffies) : 0,
  			   im->reporter);
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2421
  static const struct seq_operations igmp_mc_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2422
2423
2424
2425
2426
2427
2428
2429
  	.start	=	igmp_mc_seq_start,
  	.next	=	igmp_mc_seq_next,
  	.stop	=	igmp_mc_seq_stop,
  	.show	=	igmp_mc_seq_show,
  };
  
  static int igmp_mc_seq_open(struct inode *inode, struct file *file)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2430
  	return seq_open_net(inode, file, &igmp_mc_seq_ops,
cf7732e4c   Pavel Emelyanov   [NET]: Make core ...
2431
  			sizeof(struct igmp_mc_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2432
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2433
  static const struct file_operations igmp_mc_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2434
2435
2436
2437
  	.owner		=	THIS_MODULE,
  	.open		=	igmp_mc_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
7091e728c   Alexey Dobriyan   netns: igmp: make...
2438
  	.release	=	seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2439
2440
2441
  };
  
  struct igmp_mcf_iter_state {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2442
  	struct seq_net_private p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2443
2444
2445
2446
2447
2448
2449
2450
2451
  	struct net_device *dev;
  	struct in_device *idev;
  	struct ip_mc_list *im;
  };
  
  #define igmp_mcf_seq_private(seq)	((struct igmp_mcf_iter_state *)(seq)->private)
  
  static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2452
  	struct net *net = seq_file_net(seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2453
2454
2455
  	struct ip_sf_list *psf = NULL;
  	struct ip_mc_list *im = NULL;
  	struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
7562f876c   Pavel Emelianov   [NET]: Rework dev...
2456
2457
  	state->idev = NULL;
  	state->im = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2458
  	for_each_netdev_rcu(net, state->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2459
  		struct in_device *idev;
6baff1503   Eric Dumazet   igmp: Use next_ne...
2460
  		idev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
  		if (unlikely(idev == NULL))
  			continue;
  		read_lock(&idev->mc_list_lock);
  		im = idev->mc_list;
  		if (likely(im != NULL)) {
  			spin_lock_bh(&im->lock);
  			psf = im->sources;
  			if (likely(psf != NULL)) {
  				state->im = im;
  				state->idev = idev;
  				break;
  			}
  			spin_unlock_bh(&im->lock);
  		}
  		read_unlock(&idev->mc_list_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
  	}
  	return psf;
  }
  
  static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf)
  {
  	struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
  
  	psf = psf->sf_next;
  	while (!psf) {
  		spin_unlock_bh(&state->im->lock);
  		state->im = state->im->next;
  		while (!state->im) {
6baff1503   Eric Dumazet   igmp: Use next_ne...
2489
  			if (likely(state->idev != NULL))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2490
  				read_unlock(&state->idev->mc_list_lock);
6baff1503   Eric Dumazet   igmp: Use next_ne...
2491
2492
  
  			state->dev = next_net_device_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2493
2494
2495
2496
  			if (!state->dev) {
  				state->idev = NULL;
  				goto out;
  			}
6baff1503   Eric Dumazet   igmp: Use next_ne...
2497
  			state->idev = __in_dev_get_rcu(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
  			if (!state->idev)
  				continue;
  			read_lock(&state->idev->mc_list_lock);
  			state->im = state->idev->mc_list;
  		}
  		if (!state->im)
  			break;
  		spin_lock_bh(&state->im->lock);
  		psf = state->im->sources;
  	}
  out:
  	return psf;
  }
  
  static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos)
  {
  	struct ip_sf_list *psf = igmp_mcf_get_first(seq);
  	if (psf)
  		while (pos && (psf = igmp_mcf_get_next(seq, psf)) != NULL)
  			--pos;
  	return pos ? NULL : psf;
  }
  
  static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2522
  	__acquires(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2523
  {
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2524
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
  	return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
  }
  
  static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	struct ip_sf_list *psf;
  	if (v == SEQ_START_TOKEN)
  		psf = igmp_mcf_get_first(seq);
  	else
  		psf = igmp_mcf_get_next(seq, v);
  	++*pos;
  	return psf;
  }
  
  static void igmp_mcf_seq_stop(struct seq_file *seq, void *v)
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2540
  	__releases(rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2541
2542
2543
2544
2545
2546
2547
2548
  {
  	struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
  	if (likely(state->im != NULL)) {
  		spin_unlock_bh(&state->im->lock);
  		state->im = NULL;
  	}
  	if (likely(state->idev != NULL)) {
  		read_unlock(&state->idev->mc_list_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2549
2550
2551
  		state->idev = NULL;
  	}
  	state->dev = NULL;
61fbab77a   stephen hemminger   IPV4: use rcu to ...
2552
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2553
2554
2555
2556
2557
2558
2559
2560
  }
  
  static int igmp_mcf_seq_show(struct seq_file *seq, void *v)
  {
  	struct ip_sf_list *psf = (struct ip_sf_list *)v;
  	struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
  
  	if (v == SEQ_START_TOKEN) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2561
  		seq_printf(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2562
2563
2564
2565
2566
2567
2568
2569
  			   "%3s %6s "
  			   "%10s %10s %6s %6s
  ", "Idx",
  			   "Device", "MCA",
  			   "SRC", "INC", "EXC");
  	} else {
  		seq_printf(seq,
  			   "%3d %6.6s 0x%08x "
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2570
2571
2572
  			   "0x%08x %6lu %6lu
  ",
  			   state->dev->ifindex, state->dev->name,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2573
2574
2575
2576
2577
2578
2579
  			   ntohl(state->im->multiaddr),
  			   ntohl(psf->sf_inaddr),
  			   psf->sf_count[MCAST_INCLUDE],
  			   psf->sf_count[MCAST_EXCLUDE]);
  	}
  	return 0;
  }
f690808e1   Stephen Hemminger   [NET]: make seq_o...
2580
  static const struct seq_operations igmp_mcf_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2581
2582
2583
2584
2585
2586
2587
2588
  	.start	=	igmp_mcf_seq_start,
  	.next	=	igmp_mcf_seq_next,
  	.stop	=	igmp_mcf_seq_stop,
  	.show	=	igmp_mcf_seq_show,
  };
  
  static int igmp_mcf_seq_open(struct inode *inode, struct file *file)
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2589
  	return seq_open_net(inode, file, &igmp_mcf_seq_ops,
cf7732e4c   Pavel Emelyanov   [NET]: Make core ...
2590
  			sizeof(struct igmp_mcf_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2591
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2592
  static const struct file_operations igmp_mcf_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2593
2594
2595
2596
  	.owner		=	THIS_MODULE,
  	.open		=	igmp_mcf_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
7091e728c   Alexey Dobriyan   netns: igmp: make...
2597
  	.release	=	seq_release_net,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2598
  };
2c8c1e729   Alexey Dobriyan   net: spread __net...
2599
  static int __net_init igmp_net_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2600
  {
7091e728c   Alexey Dobriyan   netns: igmp: make...
2601
2602
2603
2604
2605
2606
2607
2608
  	struct proc_dir_entry *pde;
  
  	pde = proc_net_fops_create(net, "igmp", S_IRUGO, &igmp_mc_seq_fops);
  	if (!pde)
  		goto out_igmp;
  	pde = proc_net_fops_create(net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops);
  	if (!pde)
  		goto out_mcfilter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2609
  	return 0;
7091e728c   Alexey Dobriyan   netns: igmp: make...
2610
2611
2612
2613
2614
2615
  
  out_mcfilter:
  	proc_net_remove(net, "igmp");
  out_igmp:
  	return -ENOMEM;
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
2616
  static void __net_exit igmp_net_exit(struct net *net)
7091e728c   Alexey Dobriyan   netns: igmp: make...
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
  {
  	proc_net_remove(net, "mcfilter");
  	proc_net_remove(net, "igmp");
  }
  
  static struct pernet_operations igmp_net_ops = {
  	.init = igmp_net_init,
  	.exit = igmp_net_exit,
  };
  
  int __init igmp_mc_proc_init(void)
  {
  	return register_pernet_subsys(&igmp_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2630
2631
  }
  #endif