Blame view

net/ipv4/igmp.c 61.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  /*
   *	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.
   *
   *	Version: $Id: igmp.c,v 1.47 2002/02/01 22:01:03 davem Exp $
   *
   *	Authors:
   *		Alan Cox <Alan.Cox@linux.org>
   *
   *	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...
38
   *					The enhancements are mainly based on Steve Deering's
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
48
49
50
51
   * 					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...
52
53
   *					memberships but was being deleted,
   *					which caused a "del_timer() called
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
   *					from %p with timer not initialized
  "
   *					message (960131).
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
57
   *		Christian Daudt :	removed del_timer from
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
   *					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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  #include <linux/module.h>
  #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...
93

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
94
  #include <net/net_namespace.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
95
  #include <net/arp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
130
  #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...
131
  #define IGMP_V1_SEEN(in_dev) \
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
132
  	(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \
42f811b8b   Herbert Xu   [IPV4]: Convert I...
133
134
135
136
  	 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...
137
  	(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \
42f811b8b   Herbert Xu   [IPV4]: Convert I...
138
139
140
  	 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
141
142
  
  static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
63007727e   Al Viro   [IPV4]: trivial i...
143
  static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
  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...
149
150
  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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
210
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
236
  
  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);
  	im->tm_running=0;
  	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)
  {
  	int tv=net_random() % max_delay;
  
  	im->tm_running=1;
  	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);
  			im->tm_running=1;
  			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...
237
238
239
240
241
242
243
244
245
246
247
248
  		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
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
292
  	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;
f5184d267   Johannes Berg   net: Allow netdev...
293
  	skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
298
299
300
301
  	if (skb == NULL)
  		return NULL;
  
  	{
  		struct flowi fl = { .oif = dev->ifindex,
  				    .nl_u = { .ip4_u = {
  				    .daddr = IGMPV3_ALL_MCR } },
  				    .proto = IPPROTO_IGMP };
f206351a5   Denis V. Lunev   [NETNS]: Add name...
302
  		if (ip_route_output_key(&init_net, &rt, &fl)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  			kfree_skb(skb);
  			return NULL;
  		}
  	}
  	if (rt->rt_src == 0) {
  		kfree_skb(skb);
  		ip_rt_put(rt);
  		return NULL;
  	}
  
  	skb->dst = &rt->u.dst;
  	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
330
331
332
333
334
  
  	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 */
  	ip_select_ident(pip, &rt->u.dst, NULL);
  	((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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  }
  
  static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
  {
  	return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc,type,gdel,sdel);
  }
  
  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;
63007727e   Al Viro   [IPV4]: trivial i...
625
626
  	__be32	group = pmc ? pmc->multiaddr : 0;
  	__be32	dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
629
630
631
632
633
634
635
636
637
638
  
  	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 };
f206351a5   Denis V. Lunev   [NETNS]: Add name...
639
  		if (ip_route_output_key(&init_net, &rt, &fl))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
  			return -1;
  	}
  	if (rt->rt_src == 0) {
  		ip_rt_put(rt);
  		return -1;
  	}
f5184d267   Johannes Berg   net: Allow netdev...
646
  	skb=alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
651
652
653
654
  	if (skb == NULL) {
  		ip_rt_put(rt);
  		return -1;
  	}
  
  	skb->dst = &rt->u.dst;
  
  	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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
671
  	ip_select_ident(iph, &rt->u.dst, NULL);
  	((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
674
675
676
677
678
  
  	ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
  	ih->type=type;
  	ih->code=0;
  	ih->csum=0;
  	ih->group=group;
  	ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
c439cb2e4   Herbert Xu   [IPV4]: Add ip_lo...
679
  	return ip_local_out(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
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
  }
  
  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...
707
  	in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
  		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);
  	im->tm_running=0;
  
  	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...
737
  /* mark EXCLUDE-mode sources */
ea4d9e722   Al Viro   [IPV4]: struct ip...
738
  static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
741
742
743
744
745
746
  {
  	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...
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
  		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...
764
  static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
ad12583f4   David L Stevens   [IPV4]: Fix multi...
765
766
767
768
769
770
771
772
773
774
775
776
  {
  	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
777
778
779
780
781
782
783
  		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...
784
785
786
787
788
789
  	if (!scount) {
  		pmc->gsquery = 0;
  		return 0;
  	}
  	pmc->gsquery = 1;
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
  }
63007727e   Al Viro   [IPV4]: trivial i...
791
  static void igmp_heard_report(struct in_device *in_dev, __be32 group)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  {
  	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...
813
814
  	struct igmphdr 		*ih = igmp_hdr(skb);
  	struct igmpv3_query *ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
  	struct ip_mc_list	*im;
63007727e   Al Viro   [IPV4]: trivial i...
816
  	__be32			group = ih->group;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
818
819
820
821
822
823
  	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...
824

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
  			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...
846

d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
847
  		ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  		if (ih3->nsrcs) {
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
849
  			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
63007727e   Al Viro   [IPV4]: trivial i...
850
  					   + ntohs(ih3->nsrcs)*sizeof(__be32)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
  				return;
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
852
  			ih3 = igmpv3_query_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853
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
  		}
  
  		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...
883
  		int changed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
885
886
887
888
889
890
891
892
  		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...
893
  		changed = !im->gsquery ||
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
894
  			igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
  		spin_unlock_bh(&im->lock);
ad12583f4   David L Stevens   [IPV4]: Fix multi...
896
897
  		if (changed)
  			igmp_mod_timer(im, max_delay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
899
900
901
902
903
904
905
906
907
  	}
  	read_unlock(&in_dev->mc_list_lock);
  }
  
  int igmp_rcv(struct sk_buff *skb)
  {
  	/* This basically follows the spec line by line -- see RFC1112 */
  	struct igmphdr *ih;
  	struct in_device *in_dev = in_dev_get(skb->dev);
  	int len = skb->len;
cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
908
909
  	if (in_dev == NULL)
  		goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910

fb286bb29   Herbert Xu   [NET]: Detect har...
911
  	if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
912
  		goto drop_ref;
fb286bb29   Herbert Xu   [NET]: Detect har...
913
914
  
  	switch (skb->ip_summed) {
84fa7933a   Patrick McHardy   [NET]: Replace CH...
915
  	case CHECKSUM_COMPLETE:
d3bc23e7e   Al Viro   [NET]: Annotate c...
916
  		if (!csum_fold(skb->csum))
fb286bb29   Herbert Xu   [NET]: Detect har...
917
918
919
920
921
  			break;
  		/* fall through */
  	case CHECKSUM_NONE:
  		skb->csum = 0;
  		if (__skb_checksum_complete(skb))
cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
922
  			goto drop_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923
  	}
d9edf9e2b   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
924
  	ih = igmp_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
926
927
928
929
930
931
932
  	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:
  	case IGMPV3_HOST_MEMBERSHIP_REPORT:
  		/* Is it our report looped back? */
ee6b96730   Eric Dumazet   [IPV4]: Add 'rtab...
933
  		if (skb->rtable->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
942
943
944
945
946
947
948
949
950
951
  		break;
  	case IGMP_PIM:
  #ifdef CONFIG_IP_PIMSM_V1
  		in_dev_put(in_dev);
  		return pim_rcv_v1(skb);
  #endif
  	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_ref:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956
  	in_dev_put(in_dev);
cd557bc1c   Denis V. Lunev   [IGMP]: Optimize ...
957
  drop:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
960
961
962
963
964
965
966
967
  	kfree_skb(skb);
  	return 0;
  }
  
  #endif
  
  
  /*
   *	Add a filter to a device
   */
63007727e   Al Viro   [IPV4]: trivial i...
968
  static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
  {
  	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)
  		dev_mc_add(dev,buf,dev->addr_len,0);
  }
  
  /*
   *	Remove a filter from a device
   */
63007727e   Al Viro   [IPV4]: trivial i...
987
  static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
  {
  	char buf[MAX_ADDR_LEN];
  	struct net_device *dev = in_dev->dev;
  
  	if (arp_mc_map(addr, buf, dev, 0) == 0)
  		dev_mc_delete(dev,buf,dev->addr_len,0);
  }
  
  #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...
1010
  	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
1012
  	if (!pmc)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
  	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...
1036
  static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
1171
1172
  {
  	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...
1173
  void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1174
1175
1176
1177
  {
  	struct ip_mc_list *im;
  
  	ASSERT_RTNL();
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1178
  	if (dev_net(in_dev->dev) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1179
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
1181
1182
1183
1184
1185
1186
  	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...
1187
  	im = kmalloc(sizeof(*im), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
  	if (!im)
  		goto out;
  
  	im->users=1;
  	im->interface=in_dev;
  	in_dev_hold(in_dev);
  	im->multiaddr=addr;
  	/* 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
  	im->tm_running=0;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1206
  	setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
  	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);
  	im->next=in_dev->mc_list;
  	in_dev->mc_list=im;
  	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;
  }
  
  /*
a816c7c71   Jay Vosburgh   bonding: Improve ...
1227
1228
1229
1230
   *	Resend IGMP JOIN report; used for bonding.
   */
  void ip_mc_rejoin_group(struct ip_mc_list *im)
  {
08882669e   Geert Uytterhoeven   [IPV4]: Fix warni...
1231
  #ifdef CONFIG_IP_MULTICAST
a816c7c71   Jay Vosburgh   bonding: Improve ...
1232
  	struct in_device *in_dev = im->interface;
a816c7c71   Jay Vosburgh   bonding: Improve ...
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
  	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
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1248
1249
   *	A socket has left a multicast group on device dev
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1250
  void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1251
1252
  {
  	struct ip_mc_list *i, **ip;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1253

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

c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1256
  	if (dev_net(in_dev->dev) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1257
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
  	for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
  		if (i->multiaddr==addr) {
  			if (--i->users == 0) {
  				write_lock_bh(&in_dev->mc_list_lock);
  				*ip = i->next;
  				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;
  		}
  	}
  }
  
  /* Device going down */
  
  void ip_mc_down(struct in_device *in_dev)
  {
  	struct ip_mc_list *i;
  
  	ASSERT_RTNL();
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1284
  	if (dev_net(in_dev->dev) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1285
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
  	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();
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1305
  	if (dev_net(in_dev->dev) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1306
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1307
1308
1309
  	in_dev->mc_tomb = NULL;
  #ifdef CONFIG_IP_MULTICAST
  	in_dev->mr_gq_running = 0;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1310
1311
  	setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire,
  			(unsigned long)in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1312
  	in_dev->mr_ifc_count = 0;
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
1313
1314
  	setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire,
  			(unsigned long)in_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
  	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();
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1329
  	if (dev_net(in_dev->dev) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1330
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
  	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();
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1346
  	if (dev_net(in_dev->dev) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1347
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
  	/* 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;
  		write_unlock_bh(&in_dev->mc_list_lock);
  
  		igmp_group_dropped(i);
  		ip_ma_put(i);
  
  		write_lock_bh(&in_dev->mc_list_lock);
  	}
  	write_unlock_bh(&in_dev->mc_list_lock);
  }
  
  static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
  {
  	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) {
7fee0ca23   Denis V. Lunev   [NETNS]: Add netn...
1373
  		idev = inetdev_by_index(&init_net, imr->imr_ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1374
1375
1376
1377
1378
  		if (idev)
  			__in_dev_put(idev);
  		return idev;
  	}
  	if (imr->imr_address.s_addr) {
1ab352768   Denis V. Lunev   [NETNS]: Add name...
1379
  		dev = ip_dev_find(&init_net, imr->imr_address.s_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1380
1381
  		if (!dev)
  			return NULL;
153330618   Stephen Hemminger   [NET]: dev_put/de...
1382
  		dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
  	}
f206351a5   Denis V. Lunev   [NETNS]: Add name...
1384
  	if (!dev && !ip_route_output_key(&init_net, &rt, &fl)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1385
1386
1387
1388
1389
  		dev = rt->u.dst.dev;
  		ip_rt_put(rt);
  	}
  	if (dev) {
  		imr->imr_ifindex = dev->ifindex;
e5ed63991   Herbert Xu   [IPV4]: Replace _...
1390
  		idev = __in_dev_get_rtnl(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1391
1392
1393
1394
1395
1396
1397
  	}
  	return idev;
  }
  
  /*
   *	Join a socket to a group
   */
ab32ea5d8   Brian Haley   [NET/IPV4/IPV6]: ...
1398
1399
  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
1400
1401
1402
  
  
  static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1403
  	__be32 *psfsrc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
  {
  	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...
1435
  			psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
  				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...
1450
1451
  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
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
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
  {
  	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...
1498
  		pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
  			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...
1517
  	__be32 *psfsrc, int delta)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
  {
  	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...
1528
  		psf = kzalloc(sizeof(*psf), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1529
1530
  		if (!psf)
  			return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
  		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...
1561
  	struct ip_sf_list *psf, *dpsf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
  	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...
1573
1574
  		if (new_in) {
  			if (!psf->sf_oldin) {
76edc6051   Al Viro   [PATCH] ipv4 NULL...
1575
  				struct ip_sf_list *prev = NULL;
ad12583f4   David L Stevens   [IPV4]: Fix multi...
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
  
  				for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next) {
  					if (dpsf->sf_inaddr == psf->sf_inaddr)
  						break;
  					prev = dpsf;
  				}
  				if (dpsf) {
  					if (prev)
  						prev->sf_next = dpsf->sf_next;
  					else
  						pmc->tomb = dpsf->sf_next;
  					kfree(dpsf);
  				}
  				psf->sf_crcount = qrv;
  				rv++;
  			}
  		} else if (psf->sf_oldin) {
  
  			psf->sf_crcount = 0;
  			/*
  			 * add or update "delete" records if an active filter
  			 * is now inactive
  			 */
  			for (dpsf=pmc->tomb; dpsf; dpsf=dpsf->sf_next)
  				if (dpsf->sf_inaddr == psf->sf_inaddr)
  					break;
  			if (!dpsf) {
  				dpsf = (struct ip_sf_list *)
  					kmalloc(sizeof(*dpsf), GFP_ATOMIC);
  				if (!dpsf)
  					continue;
  				*dpsf = *psf;
  				/* pmc->lock held by callers */
  				dpsf->sf_next = pmc->tomb;
  				pmc->tomb = dpsf;
  			}
  			dpsf->sf_crcount = qrv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
  			rv++;
  		}
  	}
  	return rv;
  }
  #endif
  
  /*
   * Add multicast source filter list to the interface list
   */
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1623
1624
  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
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
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
  {
  	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
1665
  		struct ip_sf_list *psf;
cfcabdcc2   Stephen Hemminger   [NET]: sparse war...
1666
  		in_dev = pmc->interface;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1667
1668
1669
1670
1671
1672
1673
1674
1675
  #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...
1676
  		pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
  			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...
1705
  	pmc->sfcount[MCAST_INCLUDE] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
  	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...
1716
  	__be32 addr = imr->imr_multiaddr.s_addr;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1717
  	struct ip_mc_socklist *iml=NULL, *i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1718
1719
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
ca9b907d1   David L Stevens   [IPV4]: multicast...
1720
  	int ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1721
  	int count = 0;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1722
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1723
  		return -EINVAL;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1724
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1725
  		return -EPROTONOSUPPORT;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1726
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1727
1728
1729
1730
1731
1732
1733
1734
  
  	in_dev = ip_mc_find_dev(imr);
  
  	if (!in_dev) {
  		iml = NULL;
  		err = -ENODEV;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1735
  	err = -EADDRINUSE;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1736
  	ifindex = imr->imr_ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737
  	for (i = inet->mc_list; i; i = i->next) {
ca9b907d1   David L Stevens   [IPV4]: multicast...
1738
1739
  		if (i->multi.imr_multiaddr.s_addr == addr &&
  		    i->multi.imr_ifindex == ifindex)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1740
  			goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1741
1742
1743
  		count++;
  	}
  	err = -ENOBUFS;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1744
1745
  	if (count >= sysctl_igmp_max_memberships)
  		goto done;
8b3a70058   Kris Katterjohn   [NET]: Remove mor...
1746
  	iml = sock_kmalloc(sk,sizeof(*iml),GFP_KERNEL);
ca9b907d1   David L Stevens   [IPV4]: multicast...
1747
  	if (iml == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1748
  		goto done;
ca9b907d1   David L Stevens   [IPV4]: multicast...
1749

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1750
1751
  	memcpy(&iml->multi, imr, sizeof(*imr));
  	iml->next = inet->mc_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1752
1753
1754
1755
  	iml->sflist = NULL;
  	iml->sfmode = MCAST_EXCLUDE;
  	inet->mc_list = iml;
  	ip_mc_inc_group(in_dev, addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1756
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1757
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1758
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1759
1760
1761
1762
1763
1764
1765
  	return err;
  }
  
  static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
  			   struct in_device *in_dev)
  {
  	int err;
cfcabdcc2   Stephen Hemminger   [NET]: sparse war...
1766
  	if (iml->sflist == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
  		/* 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,
  			iml->sfmode, iml->sflist->sl_count,
  			iml->sflist->sl_addr, 0);
  	sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max));
  	iml->sflist = NULL;
  	return err;
  }
  
  /*
   *	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 ...
1787
  	struct in_device *in_dev;
8f935bbd7   Al Viro   [IPV4]: ip_mc_{in...
1788
  	__be32 group = imr->imr_multiaddr.s_addr;
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1789
  	u32 ifindex;
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1790
  	int ret = -EADDRNOTAVAIL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1791

3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1792
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1793
  		return -EPROTONOSUPPORT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1794
  	rtnl_lock();
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1795
  	in_dev = ip_mc_find_dev(imr);
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1796
  	ifindex = imr->imr_ifindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1797
  	for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) {
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1798
1799
1800
1801
1802
1803
1804
1805
  		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
1806

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

acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1809
1810
1811
  		*imlp = iml->next;
  
  		if (in_dev)
84b42baef   David L Stevens   [IPV4]: fix IPv4 ...
1812
  			ip_mc_dec_group(in_dev, group);
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1813
1814
1815
  		rtnl_unlock();
  		sock_kfree_s(sk, iml, sizeof(*iml));
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1816
  	}
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1817
1818
  	if (!in_dev)
  		ret = -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1819
  	rtnl_unlock();
acd6e00b8   David L Stevens   [MCAST]: Fix filt...
1820
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1821
1822
1823
1824
1825
1826
1827
  }
  
  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...
1828
  	__be32 addr = mreqs->imr_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1829
1830
1831
1832
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev = NULL;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
8cdaaa15d   David L Stevens   [IPV4]: multicast...
1833
  	int leavegroup = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1834
  	int i, j, rv;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1835
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1836
  		return -EINVAL;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1837
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1838
  		return -EPROTONOSUPPORT;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1839
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
  
  	imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
  	imr.imr_address.s_addr = mreqs->imr_interface;
  	imr.imr_ifindex = ifindex;
  	in_dev = ip_mc_find_dev(&imr);
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
  	err = -EADDRNOTAVAIL;
  
  	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
ca9b907d1   David L Stevens   [IPV4]: multicast...
1853
1854
  		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
1855
1856
  			break;
  	}
917f2f105   David L Stevens   [IPV4]: multicast...
1857
1858
  	if (!pmc) {		/* must have a prior join */
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1859
  		goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
1860
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1861
1862
  	/* if a source filter was set, must be the same mode as before */
  	if (pmc->sflist) {
917f2f105   David L Stevens   [IPV4]: multicast...
1863
1864
  		if (pmc->sfmode != omode) {
  			err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1865
  			goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
1866
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1867
1868
1869
  	} 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...
1870
  		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1871
1872
1873
1874
1875
1876
1877
  			NULL, 0);
  		pmc->sfmode = omode;
  	}
  
  	psl = pmc->sflist;
  	if (!add) {
  		if (!psl)
917f2f105   David L Stevens   [IPV4]: multicast...
1878
  			goto done;	/* err = -EADDRNOTAVAIL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1879
1880
1881
  		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...
1882
  				sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1883
1884
1885
1886
  			if (rv == 0)
  				break;
  		}
  		if (rv)		/* source not found */
917f2f105   David L Stevens   [IPV4]: multicast...
1887
  			goto done;	/* err = -EADDRNOTAVAIL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1888

8cdaaa15d   David L Stevens   [IPV4]: multicast...
1889
1890
1891
1892
1893
  		/* 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
1894
  		/* update the interface filter */
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
1895
  		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
  			&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...
1916
  		newpsl = sock_kmalloc(sk, IP_SFLSIZE(count), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
  		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];
  			sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
  		}
  		pmc->sflist = psl = newpsl;
  	}
  	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...
1933
  			sizeof(__be32));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
  		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...
1945
  	ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1946
1947
  		&mreqs->imr_sourceaddr, 1);
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1948
  	rtnl_unlock();
8cdaaa15d   David L Stevens   [IPV4]: multicast...
1949
1950
  	if (leavegroup)
  		return ip_mc_leave_group(sk, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1951
1952
1953
1954
1955
  	return err;
  }
  
  int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
  {
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
1956
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1957
  	struct ip_mreqn	imr;
63007727e   Al Viro   [IPV4]: trivial i...
1958
  	__be32 addr = msf->imsf_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1959
1960
1961
1962
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *newpsl, *psl;
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
1963
  	int leavegroup = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1964

f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
1965
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1966
1967
1968
1969
  		return -EINVAL;
  	if (msf->imsf_fmode != MCAST_INCLUDE &&
  	    msf->imsf_fmode != MCAST_EXCLUDE)
  		return -EINVAL;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1970
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
1971
  		return -EPROTONOSUPPORT;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
1972
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
  
  	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
  	imr.imr_address.s_addr = msf->imsf_interface;
  	imr.imr_ifindex = ifindex;
  	in_dev = ip_mc_find_dev(&imr);
  
  	if (!in_dev) {
  		err = -ENODEV;
  		goto done;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1983

9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
1984
1985
1986
1987
1988
  	/* 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
1989
1990
1991
1992
1993
  	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...
1994
1995
  	if (!pmc) {		/* must have a prior join */
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1996
  		goto done;
917f2f105   David L Stevens   [IPV4]: multicast...
1997
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1998
  	if (msf->imsf_numsrc) {
8b3a70058   Kris Katterjohn   [NET]: Remove mor...
1999
2000
  		newpsl = sock_kmalloc(sk, IP_SFLSIZE(msf->imsf_numsrc),
  							   GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
  		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...
2014
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2015
  		newpsl = NULL;
8713dbf05   Yan Zheng   [MCAST]: ip[6]_mc...
2016
2017
2018
  		(void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
  				     msf->imsf_fmode, 0, NULL, 0);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
  	psl = pmc->sflist;
  	if (psl) {
  		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
  			psl->sl_count, psl->sl_addr, 0);
  		sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
  	} else
  		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
  			0, NULL, 0);
  	pmc->sflist = newpsl;
  	pmc->sfmode = msf->imsf_fmode;
917f2f105   David L Stevens   [IPV4]: multicast...
2029
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2030
  done:
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2031
  	rtnl_unlock();
9951f036f   David L Stevens   [IPV4]: (INCLUDE,...
2032
2033
  	if (leavegroup)
  		err = ip_mc_leave_group(sk, &imr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2034
2035
2036
2037
2038
2039
2040
2041
  	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...
2042
  	__be32 addr = msf->imsf_multiaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2043
2044
2045
2046
  	struct ip_mc_socklist *pmc;
  	struct in_device *in_dev;
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_sf_socklist *psl;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2047
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2048
  		return -EINVAL;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
2049
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
2050
  		return -EPROTONOSUPPORT;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2051
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
  
  	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
  	imr.imr_address.s_addr = msf->imsf_interface;
  	imr.imr_ifindex = 0;
  	in_dev = ip_mc_find_dev(&imr);
  
  	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...
2073
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
  	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...
2092
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2093
2094
2095
2096
2097
2098
2099
2100
  	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...
2101
  	__be32 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2102
2103
2104
2105
2106
2107
2108
2109
  	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: ...
2110
  	if (!ipv4_is_multicast(addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2111
  		return -EINVAL;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
2112
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
2113
  		return -EPROTONOSUPPORT;
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
2114
  	rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
  
  	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...
2127
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2128
2129
2130
2131
2132
2133
2134
2135
  	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
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
  		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...
2147
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2148
2149
2150
2151
2152
2153
  	return err;
  }
  
  /*
   * check if a multicast source filter allows delivery for a given <src,dst,intf>
   */
c0cda068a   Al Viro   [IPV4]: ip_mc_sf_...
2154
  int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2155
2156
2157
2158
2159
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *pmc;
  	struct ip_sf_socklist *psl;
  	int i;
f97c1e0c6   Joe Perches   [IPV4] net/ipv4: ...
2160
  	if (!ipv4_is_multicast(loc_addr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
  		return 1;
  
  	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
  		if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
  		    pmc->multi.imr_ifindex == dif)
  			break;
  	}
  	if (!pmc)
  		return 1;
  	psl = pmc->sflist;
  	if (!psl)
  		return pmc->sfmode == MCAST_EXCLUDE;
  
  	for (i=0; i<psl->sl_count; i++) {
  		if (psl->sl_addr[i] == rmt_addr)
  			break;
  	}
  	if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
  		return 0;
  	if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
  		return 0;
  	return 1;
  }
  
  /*
   *	A socket is closing.
   */
  
  void ip_mc_drop_socket(struct sock *sk)
  {
  	struct inet_sock *inet = inet_sk(sk);
  	struct ip_mc_socklist *iml;
  
  	if (inet->mc_list == NULL)
  		return;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
2196
  	if (sock_net(sk) != &init_net)
2430aa85d   Denis V. Lunev   [NETNS]: Disable ...
2197
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2198
2199
2200
2201
  	rtnl_lock();
  	while ((iml = inet->mc_list) != NULL) {
  		struct in_device *in_dev;
  		inet->mc_list = iml->next;
7fee0ca23   Denis V. Lunev   [NETNS]: Add netn...
2202
  		in_dev = inetdev_by_index(&init_net, iml->multi.imr_ifindex);
bb699cbca   Michal Ruzicka   [IPV4]: Possible ...
2203
2204
  		(void) ip_mc_leave_src(sk, iml, in_dev);
  		if (in_dev != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2205
2206
2207
2208
  			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
  			in_dev_put(in_dev);
  		}
  		sock_kfree_s(sk, iml, sizeof(*iml));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2209
2210
2211
  	}
  	rtnl_unlock();
  }
a60c4923d   Al Viro   [IPV4]: ip_check_...
2212
  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
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
  {
  	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 {
  	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)
  {
  	struct ip_mc_list *im = NULL;
  	struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
7562f876c   Pavel Emelianov   [NET]: Rework dev...
2256
  	state->in_dev = NULL;
881d966b4   Eric W. Biederman   [NET]: Make the d...
2257
  	for_each_netdev(&init_net, state->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
  		struct in_device *in_dev;
  		in_dev = in_dev_get(state->dev);
  		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);
  		in_dev_put(in_dev);
  	}
  	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) {
  		if (likely(state->in_dev != NULL)) {
  			read_unlock(&state->in_dev->mc_list_lock);
  			in_dev_put(state->in_dev);
  		}
7562f876c   Pavel Emelianov   [NET]: Rework dev...
2283
  		state->dev = next_net_device(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
  		if (!state->dev) {
  			state->in_dev = NULL;
  			break;
  		}
  		state->in_dev = in_dev_get(state->dev);
  		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)
dd329bfa9   Stephen Hemminger   [IPV4]: igmp spar...
2307
  	__acquires(dev_base_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
  {
  	read_lock(&dev_base_lock);
  	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)
dd329bfa9   Stephen Hemminger   [IPV4]: igmp spar...
2325
  	__releases(dev_base_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
  {
  	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);
  		in_dev_put(state->in_dev);
  		state->in_dev = NULL;
  	}
  	state->dev = NULL;
  	read_unlock(&dev_base_lock);
  }
  
  static int igmp_mc_seq_show(struct seq_file *seq, void *v)
  {
  	if (v == SEQ_START_TOKEN)
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
2340
  		seq_puts(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
  			 "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
  ",
  				   state->dev->ifindex, state->dev->name, state->dev->mc_count, querier);
  		}
  
  		seq_printf(seq,
338fcf988   Alexey Dobriyan   [IPV4] igmp: Fixu...
2362
2363
  			   "\t\t\t\t%08X %5d %d:%08lX\t\t%d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2364
2365
2366
2367
2368
2369
2370
  			   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...
2371
  static const struct seq_operations igmp_mc_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2372
2373
2374
2375
2376
2377
2378
2379
  	.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)
  {
cf7732e4c   Pavel Emelyanov   [NET]: Make core ...
2380
2381
  	return seq_open_private(file, &igmp_mc_seq_ops,
  			sizeof(struct igmp_mc_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2382
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2383
  static const struct file_operations igmp_mc_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
  	.owner		=	THIS_MODULE,
  	.open		=	igmp_mc_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
  	.release	=	seq_release_private,
  };
  
  struct igmp_mcf_iter_state {
  	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)
  {
  	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...
2404
2405
  	state->idev = NULL;
  	state->im = NULL;
881d966b4   Eric W. Biederman   [NET]: Make the d...
2406
  	for_each_netdev(&init_net, state->dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
  		struct in_device *idev;
  		idev = in_dev_get(state->dev);
  		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);
  		in_dev_put(idev);
  	}
  	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) {
  			if (likely(state->idev != NULL)) {
  				read_unlock(&state->idev->mc_list_lock);
  				in_dev_put(state->idev);
  			}
7562f876c   Pavel Emelianov   [NET]: Rework dev...
2442
  			state->dev = next_net_device(state->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
  			if (!state->dev) {
  				state->idev = NULL;
  				goto out;
  			}
  			state->idev = in_dev_get(state->dev);
  			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)
  {
  	read_lock(&dev_base_lock);
  	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)
  {
  	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);
  		in_dev_put(state->idev);
  		state->idev = NULL;
  	}
  	state->dev = NULL;
  	read_unlock(&dev_base_lock);
  }
  
  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...
2510
  		seq_printf(seq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2511
2512
2513
2514
2515
2516
2517
2518
  			   "%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...
2519
2520
2521
  			   "0x%08x %6lu %6lu
  ",
  			   state->dev->ifindex, state->dev->name,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2522
2523
2524
2525
2526
2527
2528
  			   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...
2529
  static const struct seq_operations igmp_mcf_seq_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2530
2531
2532
2533
2534
2535
2536
2537
  	.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)
  {
cf7732e4c   Pavel Emelyanov   [NET]: Make core ...
2538
2539
  	return seq_open_private(file, &igmp_mcf_seq_ops,
  			sizeof(struct igmp_mcf_iter_state));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2540
  }
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
2541
  static const struct file_operations igmp_mcf_seq_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2542
2543
2544
2545
2546
2547
2548
2549
2550
  	.owner		=	THIS_MODULE,
  	.open		=	igmp_mcf_seq_open,
  	.read		=	seq_read,
  	.llseek		=	seq_lseek,
  	.release	=	seq_release_private,
  };
  
  int __init igmp_mc_proc_init(void)
  {
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
2551
2552
  	proc_net_fops_create(&init_net, "igmp", S_IRUGO, &igmp_mc_seq_fops);
  	proc_net_fops_create(&init_net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2553
2554
2555
2556
2557
2558
2559
  	return 0;
  }
  #endif
  
  EXPORT_SYMBOL(ip_mc_dec_group);
  EXPORT_SYMBOL(ip_mc_inc_group);
  EXPORT_SYMBOL(ip_mc_join_group);
a816c7c71   Jay Vosburgh   bonding: Improve ...
2560
  EXPORT_SYMBOL(ip_mc_rejoin_group);